From d1109b70f961cf59d7cbc8b8a85c401980a2b6c4 Mon Sep 17 00:00:00 2001 From: Doug Martin Date: Fri, 9 Apr 2021 15:41:57 -0600 Subject: [PATCH] feat(typeorm): Implement `setRelations` to set many relations --- .../services/typeorm-query.service.spec.ts | 55 +++++++++++++++++++ .../src/services/relation-query.service.ts | 40 ++++++++++++-- 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts b/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts index d7f9bc462..331be0801 100644 --- a/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts +++ b/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts @@ -1171,6 +1171,61 @@ describe('TypeOrmQueryService', (): void => { }); }); + describe('#setRelations', () => { + it('set all relations on the entity', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + const relationIds = TEST_RELATIONS.slice(3, 6).map((r) => r.testRelationPk); + const queryResult = await queryService.setRelations('testRelations', entity.testEntityPk, relationIds); + expect(queryResult).toEqual(entity); + + const relations = await queryService.queryRelations(TestRelation, 'testRelations', entity, {}); + expect(relations.map((r) => r.testRelationPk)).toEqual(relationIds); + }); + + it('should remove all relations if the relationIds is empty', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.setRelations('testRelations', entity.testEntityPk, []); + expect(queryResult).toEqual(entity); + + const relations = await queryService.queryRelations(TestRelation, 'testRelations', entity, {}); + expect(relations.map((r) => r.testRelationPk)).toEqual([]); + }); + + describe('with modify options', () => { + it('should throw an error if the entity is not found with the id and provided filter', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + return expect( + queryService.setRelations( + 'testRelations', + entity.testEntityPk, + TEST_RELATIONS.slice(3, 6).map((r) => r.testRelationPk), + { + filter: { stringType: { eq: TEST_ENTITIES[1].stringType } }, + }, + ), + ).rejects.toThrow('Unable to find TestEntity with id: test-entity-1'); + }); + + it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + return expect( + queryService.setRelations( + 'testRelations', + entity.testEntityPk, + TEST_RELATIONS.slice(3, 6).map((r) => r.testRelationPk), + { + relationFilter: { relationName: { like: '%-one' } }, + }, + ), + ).rejects.toThrow('Unable to find all testRelations to set on TestEntity'); + }); + }); + }); + describe('#setRelation', () => { it('call select and return the result', async () => { const entity = TEST_ENTITIES[0]; diff --git a/packages/query-typeorm/src/services/relation-query.service.ts b/packages/query-typeorm/src/services/relation-query.service.ts index d2b0aa684..3f87b693a 100644 --- a/packages/query-typeorm/src/services/relation-query.service.ts +++ b/packages/query-typeorm/src/services/relation-query.service.ts @@ -203,7 +203,35 @@ export abstract class RelationQueryService { if (!this.foundAllRelations(relationIds, relations)) { throw new Error(`Unable to find all ${relationName} to add to ${this.EntityClass.name}`); } - await this.createRelationQueryBuilder(entity, relationName).add(relationIds); + await this.createTypeormRelationQueryBuilder(entity, relationName).add(relationIds); + return entity; + } + + /** + * Set the relations on the entity. + * + * @param id - The id of the entity to set the relation on. + * @param relationName - The name of the relation to query for. + * @param relationIds - The ids of the relation to set on the entity. If the relationIds is empty all relations + * will be removed. + * @param opts - Additional options + */ + async setRelations( + relationName: string, + id: string | number, + relationIds: (string | number)[], + opts?: ModifyRelationOptions, + ): Promise { + const entity = await this.getById(id, opts); + const relations = await this.getRelations(relationName, relationIds, opts?.relationFilter); + if (relationIds.length) { + if (!this.foundAllRelations(relationIds, relations)) { + throw new Error(`Unable to find all ${relationName} to set on ${this.EntityClass.name}`); + } + } + const relationQueryBuilder = this.getRelationQueryBuilder(relationName); + const existingRelations = await relationQueryBuilder.select(entity, { filter: opts?.relationFilter }).getMany(); + await this.createTypeormRelationQueryBuilder(entity, relationName).addAndRemove(relations, existingRelations); return entity; } @@ -226,7 +254,7 @@ export abstract class RelationQueryService { if (!relation) { throw new Error(`Unable to find ${relationName} to set on ${this.EntityClass.name}`); } - await this.createRelationQueryBuilder(entity, relationName).set(relationId); + await this.createTypeormRelationQueryBuilder(entity, relationName).set(relationId); return entity; } @@ -248,7 +276,7 @@ export abstract class RelationQueryService { if (!this.foundAllRelations(relationIds, relations)) { throw new Error(`Unable to find all ${relationName} to remove from ${this.EntityClass.name}`); } - await this.createRelationQueryBuilder(entity, relationName).remove(relationIds); + await this.createTypeormRelationQueryBuilder(entity, relationName).remove(relationIds); return entity; } @@ -272,9 +300,9 @@ export abstract class RelationQueryService { } const meta = this.getRelationMeta(relationName); if (meta.isOneToOne || meta.isManyToOne) { - await this.createRelationQueryBuilder(entity, relationName).set(null); + await this.createTypeormRelationQueryBuilder(entity, relationName).set(null); } else { - await this.createRelationQueryBuilder(entity, relationName).remove(relationId); + await this.createTypeormRelationQueryBuilder(entity, relationName).remove(relationId); } return entity; @@ -399,7 +427,7 @@ export abstract class RelationQueryService { return results; } - private createRelationQueryBuilder(entity: Entity, relationName: string): TypeOrmRelationQueryBuilder { + private createTypeormRelationQueryBuilder(entity: Entity, relationName: string): TypeOrmRelationQueryBuilder { return this.repo.createQueryBuilder().relation(relationName).of(entity); }