Skip to content

Commit 18f4a0c

Browse files
committed
feat(json-api-nestjs): Microro orm
Create orm methode, create general transform service, use this service for swagger and zod
1 parent 8cd7955 commit 18f4a0c

26 files changed

+5393
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { Knex as TypeKnex } from '@mikro-orm/knex';
2+
import { MikroORM } from '@mikro-orm/core';
3+
import { PostgreSqlDriver } from '@mikro-orm/postgresql';
4+
import { SqlHighlighter } from '@mikro-orm/sql-highlighter';
5+
6+
import Knex from 'knex';
7+
import * as ClientPgLite from 'knex-pglite';
8+
9+
import {
10+
Addresses,
11+
Comments,
12+
Notes,
13+
Roles,
14+
UserGroups,
15+
Users,
16+
} from '../entities';
17+
18+
let knexInst: TypeKnex;
19+
20+
export async function sharedConnect(): Promise<TypeKnex> {
21+
// @ts-ignore
22+
// return globalThis.pgLite;
23+
24+
if (knexInst) {
25+
return knexInst;
26+
}
27+
28+
const pgLite = await Promise.all([
29+
import('@electric-sql/pglite'),
30+
// @ts-ignore
31+
import('@electric-sql/pglite/contrib/uuid_ossp'),
32+
]).then(
33+
([{ PGlite }, { uuid_ossp }]) =>
34+
new PGlite({
35+
extensions: { uuid_ossp },
36+
})
37+
);
38+
39+
knexInst = Knex({
40+
// @ts-ignore
41+
client: ClientPgLite,
42+
dialect: 'postgres',
43+
// @ts-ignore
44+
connection: { pglite: pgLite },
45+
});
46+
47+
return knexInst;
48+
}
49+
50+
export async function initMikroOrm(knex: TypeKnex, testDbName: string) {
51+
const result = await knex.raw(
52+
`select 1 from pg_database where datname = '${testDbName}'`
53+
);
54+
55+
if ((result['rows'] as []).length === 0) {
56+
await knex.raw(`create database ??`, [testDbName]);
57+
}
58+
59+
const orm = await MikroORM.init<PostgreSqlDriver>({
60+
highlighter: new SqlHighlighter(),
61+
driver: PostgreSqlDriver,
62+
dbName: testDbName,
63+
driverOptions: knexInst,
64+
entities: [Users, UserGroups, Roles, Comments, Addresses, Notes],
65+
allowGlobalContext: true,
66+
schema: 'public',
67+
debug: ['query', 'query-params'],
68+
});
69+
70+
if ((result['rows'] as []).length === 0) {
71+
const sql = await orm.getSchemaGenerator().getCreateSchemaSQL();
72+
const statements = sql.split(';').filter((s) => s.trim().length > 0); // Разбиваем на отдельные команды
73+
74+
for (const statement of statements) {
75+
await orm.em.execute(statement);
76+
}
77+
}
78+
79+
return orm;
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { EntityManager, MikroORM } from '@mikro-orm/core';
2+
3+
import {
4+
dbRandomName,
5+
getModuleForPgLite,
6+
pullData,
7+
Users,
8+
} from '../../../../mock-utils/microrom';
9+
import { MicroOrmService } from '../../service';
10+
11+
import {
12+
CURRENT_ENTITY_MANAGER_TOKEN,
13+
ORM_SERVICE,
14+
} from '../../../../constants';
15+
import { deleteOne } from './delete-one';
16+
17+
describe('delete-one', () => {
18+
let mikroORMUsers: MikroORM;
19+
let microOrmServiceUser: MicroOrmService<Users>;
20+
let em: EntityManager;
21+
let dbName: string;
22+
beforeAll(async () => {
23+
dbName = dbRandomName();
24+
const moduleUsers = await getModuleForPgLite(Users, dbName);
25+
microOrmServiceUser = moduleUsers.get<MicroOrmService<Users>>(ORM_SERVICE);
26+
mikroORMUsers = moduleUsers.get(MikroORM);
27+
em = moduleUsers.get(CURRENT_ENTITY_MANAGER_TOKEN);
28+
await pullData(em, 10);
29+
});
30+
31+
afterEach(() => {
32+
jest.clearAllMocks();
33+
jest.restoreAllMocks();
34+
});
35+
36+
afterAll(() => {
37+
mikroORMUsers.close(true);
38+
});
39+
40+
it('Delete one item', async () => {
41+
const checkData = await microOrmServiceUser.microOrmUtilService
42+
.queryBuilder()
43+
.limit(1)
44+
.execute('get', true);
45+
46+
await deleteOne.call<
47+
MicroOrmService<Users>,
48+
Parameters<typeof deleteOne<Users>>,
49+
ReturnType<typeof deleteOne<Users>>
50+
>(microOrmServiceUser, checkData.id);
51+
52+
const result = await microOrmServiceUser.microOrmUtilService
53+
.queryBuilder()
54+
.where({
55+
id: checkData.id,
56+
})
57+
.execute('get', true);
58+
59+
expect(result).toBe(null);
60+
});
61+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { MicroOrmService } from '../../service';
2+
import { ObjectLiteral } from '../../../../types';
3+
4+
export async function deleteOne<E extends ObjectLiteral>(
5+
this: MicroOrmService<E>,
6+
id: number | string
7+
): Promise<void> {
8+
const data = await this.microOrmUtilService
9+
.queryBuilder()
10+
.where({
11+
[this.microOrmUtilService.currentPrimaryColumn]: id,
12+
})
13+
.getSingleResult();
14+
15+
if (!data) return void 0;
16+
17+
await this.microOrmUtilService.entityManager.removeAndFlush(data);
18+
19+
return void 0;
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import { Collection, EntityManager, MikroORM } from '@mikro-orm/core';
2+
import { faker } from '@faker-js/faker';
3+
4+
import {
5+
Addresses,
6+
dbRandomName,
7+
getModuleForPgLite,
8+
Notes,
9+
pullData,
10+
Roles,
11+
UserGroups,
12+
Users,
13+
Comments,
14+
pullAddress,
15+
} from '../../../../mock-utils/microrom';
16+
import { MicroOrmService } from '../../service';
17+
18+
import {
19+
CURRENT_ENTITY_MANAGER_TOKEN,
20+
ORM_SERVICE,
21+
} from '../../../../constants';
22+
23+
import { deleteRelationship } from './delete-relationship';
24+
import { BadRequestException } from '@nestjs/common';
25+
import { EntityRelation } from '@klerick/json-api-nestjs-shared';
26+
27+
describe('delete-relationship', () => {
28+
let mikroORMUsers: MikroORM;
29+
let microOrmServiceUser: MicroOrmService<Users>;
30+
let em: EntityManager;
31+
let dbName: string;
32+
let addressForTest: Addresses;
33+
let addresses: Addresses;
34+
let userGroup: UserGroups;
35+
let notes: Collection<Notes>;
36+
let roles: Collection<Roles>;
37+
let comments: Collection<Comments>;
38+
let userObject: Users;
39+
let newUser: Users;
40+
beforeAll(async () => {
41+
dbName = dbRandomName();
42+
const moduleUsers = await getModuleForPgLite(Users, dbName);
43+
microOrmServiceUser = moduleUsers.get<MicroOrmService<Users>>(ORM_SERVICE);
44+
mikroORMUsers = moduleUsers.get(MikroORM);
45+
em = moduleUsers.get(CURRENT_ENTITY_MANAGER_TOKEN);
46+
await pullData(em, 10);
47+
});
48+
49+
beforeEach(async () => {
50+
const data = await microOrmServiceUser.microOrmUtilService
51+
.queryBuilder()
52+
.leftJoinAndSelect('Users.addresses', 'Addresses_addresses', {}, ['id'])
53+
.leftJoinAndSelect('Users.comments', 'Comments_comments', {}, ['id'])
54+
.leftJoinAndSelect('Users.roles', 'Roles__roles', {}, ['id'])
55+
.leftJoinAndSelect('Users.notes', 'Notes__notes', {}, ['id'])
56+
.leftJoinAndSelect('Users.userGroup', 'UserGroups__userGroup', {}, ['id'])
57+
.where({
58+
roles: {
59+
$exists: true,
60+
},
61+
userGroup: {
62+
$exists: true,
63+
},
64+
})
65+
.limit(1)
66+
.getSingleResult();
67+
68+
if (!data) throw new Error();
69+
({ roles, notes, userGroup, addresses, comments, ...userObject as any } =
70+
data);
71+
const firstName = faker.person.firstName();
72+
const lastName = faker.person.lastName();
73+
newUser = {
74+
id: faker.number.int({ min: 0, max: 999999 }),
75+
firstName: firstName,
76+
lastName: lastName,
77+
isActive: faker.datatype.boolean(),
78+
login: faker.internet.userName({
79+
lastName: firstName,
80+
firstName: lastName,
81+
}),
82+
testReal: [faker.number.float({ fractionDigits: 4 })],
83+
testArrayNull: null,
84+
testDate: faker.date.anytime(),
85+
} as Users;
86+
87+
addressForTest = await pullAddress();
88+
await em.persistAndFlush(addressForTest);
89+
});
90+
91+
afterEach(() => {
92+
jest.clearAllMocks();
93+
jest.restoreAllMocks();
94+
});
95+
96+
afterAll(() => {
97+
mikroORMUsers.close(true);
98+
});
99+
100+
it('should be ok', async () => {
101+
const saveCount = roles.length;
102+
const [roles1, roles2] = roles;
103+
await deleteRelationship.call<
104+
MicroOrmService<Users>,
105+
Parameters<typeof deleteRelationship<Users, EntityRelation<Users>>>,
106+
ReturnType<typeof deleteRelationship<Users, EntityRelation<Users>>>
107+
>(microOrmServiceUser, userObject.id, 'roles', [
108+
{ type: 'roles', id: roles1.id.toString() },
109+
]);
110+
111+
await deleteRelationship.call<
112+
MicroOrmService<Users>,
113+
Parameters<typeof deleteRelationship<Users, EntityRelation<Users>>>,
114+
ReturnType<typeof deleteRelationship<Users, EntityRelation<Users>>>
115+
>(microOrmServiceUser, userObject.id, 'userGroup', {
116+
type: 'user-groups',
117+
id: userGroup.id.toString(),
118+
});
119+
120+
const checkData = await microOrmServiceUser.microOrmUtilService
121+
.queryBuilder()
122+
.leftJoinAndSelect('Users.roles', 'Roles__roles', {}, ['id'])
123+
.leftJoinAndSelect('Users.userGroup', 'UserGroups__userGroup', {}, ['id'])
124+
.where({
125+
id: userObject.id,
126+
})
127+
.getSingleResult();
128+
129+
expect(checkData?.roles.length).toBe(saveCount - 1);
130+
expect(checkData?.roles.map((i) => i.id)).not.toContain(roles1.id);
131+
expect(checkData?.userGroup).toBe(null);
132+
});
133+
134+
it('should be error', async () => {
135+
await expect(
136+
deleteRelationship.call<
137+
MicroOrmService<Users>,
138+
Parameters<typeof deleteRelationship<Users, EntityRelation<Users>>>,
139+
ReturnType<typeof deleteRelationship<Users, EntityRelation<Users>>>
140+
>(microOrmServiceUser, userObject.id, 'roles', {
141+
type: 'roles',
142+
id: '1000',
143+
})
144+
).rejects.toThrow();
145+
146+
await expect(
147+
deleteRelationship.call<
148+
MicroOrmService<Users>,
149+
Parameters<typeof deleteRelationship<Users, EntityRelation<Users>>>,
150+
ReturnType<typeof deleteRelationship<Users, EntityRelation<Users>>>
151+
>(microOrmServiceUser, userObject.id, 'roles', [
152+
{
153+
type: 'roles',
154+
id: '1000',
155+
},
156+
])
157+
).rejects.toThrow();
158+
159+
await expect(
160+
deleteRelationship.call<
161+
MicroOrmService<Users>,
162+
Parameters<typeof deleteRelationship<Users, EntityRelation<Users>>>,
163+
ReturnType<typeof deleteRelationship<Users, EntityRelation<Users>>>
164+
>(microOrmServiceUser, userObject.id, 'userGroup', [
165+
{
166+
type: 'user-groups',
167+
id: '10000',
168+
},
169+
])
170+
).rejects.toThrow();
171+
await expect(
172+
deleteRelationship.call<
173+
MicroOrmService<Users>,
174+
Parameters<typeof deleteRelationship<Users, EntityRelation<Users>>>,
175+
ReturnType<typeof deleteRelationship<Users, EntityRelation<Users>>>
176+
>(microOrmServiceUser, userObject.id, 'userGroup', {
177+
type: 'user-groups',
178+
id: '10000',
179+
})
180+
).rejects.toThrow();
181+
});
182+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { EntityRelation } from '@klerick/json-api-nestjs-shared';
2+
3+
import { ObjectLiteral } from '../../../../types';
4+
5+
import { PostRelationshipData } from '../../../mixin/zod';
6+
import { MicroOrmService } from '../../service';
7+
8+
export async function deleteRelationship<
9+
E extends ObjectLiteral,
10+
Rel extends EntityRelation<E>
11+
>(
12+
this: MicroOrmService<E>,
13+
id: number | string,
14+
rel: Rel,
15+
input: PostRelationshipData
16+
): Promise<void> {
17+
const idsResult = await this.microOrmUtilService.validateRelationInputData(
18+
rel,
19+
input
20+
);
21+
22+
const currentEntityRef = this.microOrmUtilService.entityManager.getReference(
23+
this.microOrmUtilService.entity,
24+
id as any
25+
);
26+
27+
if (Array.isArray(idsResult)) {
28+
const relEntity = this.microOrmUtilService.getRelation(rel as any).entity();
29+
const relRef = idsResult.map((i) =>
30+
this.microOrmUtilService.entityManager.getReference(relEntity, i as any)
31+
);
32+
currentEntityRef[rel].remove(...relRef);
33+
} else {
34+
if (
35+
currentEntityRef[rel][this.microOrmUtilService.getPrimaryNameFor(rel)] ==
36+
idsResult
37+
) {
38+
// @ts-ignore
39+
currentEntityRef[rel] = null;
40+
}
41+
}
42+
43+
await this.microOrmUtilService.entityManager.flush();
44+
}

0 commit comments

Comments
 (0)