From 80cc8e988b73d057812cba901e909e1774eea77c Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Fri, 27 May 2022 17:22:01 +0200 Subject: [PATCH 1/8] feat(query-graphql): Added `disableFilter` and `disableSort` With these options you can disable the filter and sorting of relations. --- .eslintrc.json | 1 + .prettierrc | 2 +- .../read-relation.resolver.spec.ts.snap | 222 ++++++++++++++++++ .../relations/read-relation.resolver.spec.ts | 21 +- .../__tests__/types/query/filter.type.spec.ts | 24 +- .../types/query/sorting.type.spec.ts | 3 + .../query-graphql/src/decorators/index.ts | 2 +- .../src/decorators/query-options.decorator.ts | 3 +- .../src/decorators/relation.decorator.ts | 57 +++-- packages/query-graphql/src/index.ts | 2 +- .../relations/relations.interface.ts | 9 +- .../query-graphql/src/types/query/index.ts | 8 +- .../src/types/query/query-args.type.ts | 12 +- .../query-args/cursor-query-args.type.ts | 27 ++- .../src/types/query/query-args/interfaces.ts | 8 + .../query-args/none-paging-query-args.type.ts | 27 ++- .../query-args/offset-query-args.type.ts | 3 + .../src/types/query/sorting.type.ts | 12 +- 18 files changed, 340 insertions(+), 103 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index a1659a4ea..2cac1b0a7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -42,6 +42,7 @@ "rules": { // airbnb default is 1 "max-classes-per-file": ["error", 5], + "max-len": ["error", { "code": 130 }], // never allow default export "import/prefer-default-export": "off", // never allow default export diff --git a/.prettierrc b/.prettierrc index 7fc336779..e3be42585 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,5 @@ { "singleQuote": true, - "printWidth": 120, + "printWidth": 130, "trailingComma": "none" } diff --git a/packages/query-graphql/__tests__/resolvers/relations/__snapshots__/read-relation.resolver.spec.ts.snap b/packages/query-graphql/__tests__/resolvers/relations/__snapshots__/read-relation.resolver.spec.ts.snap index 30d8af149..2d528ffb1 100644 --- a/packages/query-graphql/__tests__/resolvers/relations/__snapshots__/read-relation.resolver.spec.ts.snap +++ b/packages/query-graphql/__tests__/resolvers/relations/__snapshots__/read-relation.resolver.spec.ts.snap @@ -1,5 +1,107 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`ReadRelationsResolver many should not add filter argument if disableFilter is true 1`] = ` +type TestResolverDTO { + id: ID! + stringField: String! + relations( + """Limit or page results.""" + paging: CursorPaging = {first: 10} + + """Specify to sort results.""" + sorting: [TestRelationDTOSort!] = [] + ): TestResolverDTORelationsConnection! +} + +input CursorPaging { + """Paginate before opaque cursor""" + before: ConnectionCursor + + """Paginate after opaque cursor""" + after: ConnectionCursor + + """Paginate first""" + first: Int + + """Paginate last""" + last: Int +} + +"""Cursor for paging through collections""" +scalar ConnectionCursor + +input TestRelationDTOSort { + field: TestRelationDTOSortFields! + direction: SortDirection! + nulls: SortNulls +} + +enum TestRelationDTOSortFields { + id + testResolverId +} + +"""Sort Directions""" +enum SortDirection { + ASC + DESC +} + +"""Sort Nulls Options""" +enum SortNulls { + NULLS_FIRST + NULLS_LAST +} + +type TestRelationDTO { + id: ID! + testResolverId: String! +} + +type TestRelationDTOEdge { + """The node containing the TestRelationDTO""" + node: TestRelationDTO! + + """Cursor for this node.""" + cursor: ConnectionCursor! +} + +type PageInfo { + """true if paging forward and there are more records.""" + hasNextPage: Boolean + + """true if paging backwards and there are more records.""" + hasPreviousPage: Boolean + + """The cursor of the first returned record.""" + startCursor: ConnectionCursor + + """The cursor of the last returned record.""" + endCursor: ConnectionCursor +} + +type TestResolverDTORelationsConnection { + """Paging information""" + pageInfo: PageInfo! + + """Array of edges.""" + edges: [TestRelationDTOEdge!]! +} + +type OffsetPageInfo { + """true if paging forward and there are more records.""" + hasNextPage: Boolean + + """true if paging backwards and there are more records.""" + hasPreviousPage: Boolean +} + +type Query { + test: TestResolverDTO! +} + +`; + exports[`ReadRelationsResolver many should not add read methods if disableRead is true 1`] = ` type TestResolverDTO { id: ID! @@ -50,6 +152,126 @@ type Query { `; +exports[`ReadRelationsResolver many should not add sorting argument if disableSorting is true 1`] = ` +type TestResolverDTO { + id: ID! + stringField: String! + relations( + """Limit or page results.""" + paging: CursorPaging = {first: 10} + + """Specify to filter the records returned.""" + filter: TestRelationDTOFilter = {} + ): TestResolverDTORelationsConnection! +} + +input CursorPaging { + """Paginate before opaque cursor""" + before: ConnectionCursor + + """Paginate after opaque cursor""" + after: ConnectionCursor + + """Paginate first""" + first: Int + + """Paginate last""" + last: Int +} + +"""Cursor for paging through collections""" +scalar ConnectionCursor + +input TestRelationDTOFilter { + and: [TestRelationDTOFilter!] + or: [TestRelationDTOFilter!] + id: IDFilterComparison + testResolverId: StringFieldComparison +} + +input IDFilterComparison { + is: Boolean + isNot: Boolean + eq: ID + neq: ID + gt: ID + gte: ID + lt: ID + lte: ID + like: ID + notLike: ID + iLike: ID + notILike: ID + in: [ID!] + notIn: [ID!] +} + +input StringFieldComparison { + is: Boolean + isNot: Boolean + eq: String + neq: String + gt: String + gte: String + lt: String + lte: String + like: String + notLike: String + iLike: String + notILike: String + in: [String!] + notIn: [String!] +} + +type TestRelationDTO { + id: ID! + testResolverId: String! +} + +type TestRelationDTOEdge { + """The node containing the TestRelationDTO""" + node: TestRelationDTO! + + """Cursor for this node.""" + cursor: ConnectionCursor! +} + +type PageInfo { + """true if paging forward and there are more records.""" + hasNextPage: Boolean + + """true if paging backwards and there are more records.""" + hasPreviousPage: Boolean + + """The cursor of the first returned record.""" + startCursor: ConnectionCursor + + """The cursor of the last returned record.""" + endCursor: ConnectionCursor +} + +type TestResolverDTORelationsConnection { + """Paging information""" + pageInfo: PageInfo! + + """Array of edges.""" + edges: [TestRelationDTOEdge!]! +} + +type OffsetPageInfo { + """true if paging forward and there are more records.""" + hasNextPage: Boolean + + """true if paging backwards and there are more records.""" + hasPreviousPage: Boolean +} + +type Query { + test: TestResolverDTO! +} + +`; + exports[`ReadRelationsResolver many should set the field to nullable if set to true 1`] = ` type TestResolverDTO { id: ID! diff --git a/packages/query-graphql/__tests__/resolvers/relations/read-relation.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/relations/read-relation.resolver.spec.ts index 92766cf7e..df2270f7f 100644 --- a/packages/query-graphql/__tests__/resolvers/relations/read-relation.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/relations/read-relation.resolver.spec.ts @@ -7,13 +7,7 @@ import { PagingStrategies } from '@ptc-org/nestjs-query-graphql'; import { ReadRelationsResolver, RelationsOpts } from '../../../src/resolvers/relations'; -import { - generateSchema, - createResolverFromNest, - TestResolverDTO, - TestService, - TestRelationDTO -} from '../../__fixtures__'; +import { generateSchema, createResolverFromNest, TestResolverDTO, TestService, TestRelationDTO } from '../../__fixtures__'; describe('ReadRelationsResolver', () => { const expectResolverSDL = async (opts?: RelationsOpts) => { @@ -30,6 +24,7 @@ describe('ReadRelationsResolver', () => { }; it('should not add read methods if one and many are empty', () => expectResolverSDL()); + describe('one', () => { @Resolver(() => TestResolverDTO) class TestResolver extends ReadRelationsResolver(TestResolverDTO, { @@ -161,6 +156,12 @@ describe('ReadRelationsResolver', () => { it('should not add read methods if disableRead is true', () => expectResolverSDL({ many: { relations: { DTO: TestRelationDTO, disableRead: true } } })); + it('should not add filter argument if disableFilter is true', () => + expectResolverSDL({ many: { relation: { DTO: TestRelationDTO, disableFilter: true } } })); + + it('should not add sorting argument if disableSorting is true', () => + expectResolverSDL({ many: { relation: { DTO: TestRelationDTO, disableSort: true } } })); + describe('many connection query', () => { @Resolver(() => TestResolverDTO) class TestResolver extends ReadRelationsResolver(TestResolverDTO, { @@ -442,9 +443,9 @@ describe('ReadRelationsResolver', () => { testResolverId: dto.id } ]; - when( - mockService.queryRelations(TestRelationDTO, 'others', deepEqual([dto]), objectContaining(query)) - ).thenResolve(new Map([[dto, output]])); + when(mockService.queryRelations(TestRelationDTO, 'others', deepEqual([dto]), objectContaining(query))).thenResolve( + new Map([[dto, output]]) + ); // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-call const result = await resolver.queryCustoms(dto, query, {}); diff --git a/packages/query-graphql/__tests__/types/query/filter.type.spec.ts b/packages/query-graphql/__tests__/types/query/filter.type.spec.ts index 27c39a474..d9eb804bb 100644 --- a/packages/query-graphql/__tests__/types/query/filter.type.spec.ts +++ b/packages/query-graphql/__tests__/types/query/filter.type.spec.ts @@ -142,9 +142,7 @@ describe('filter types', (): void => { @ObjectType('TestNoFields') class TestInvalidFilter {} - expect(() => FilterType(TestInvalidFilter)).toThrow( - 'No fields found to create GraphQLFilter for TestInvalidFilter' - ); + expect(() => FilterType(TestInvalidFilter)).toThrow('No fields found to create GraphQLFilter for TestInvalidFilter'); }); it('should throw an error when the field type is unknown', () => { @@ -411,9 +409,7 @@ describe('filter types', (): void => { @ObjectType('TestNoFields') class TestInvalidFilter {} - expect(() => UpdateFilterType(TestInvalidFilter)).toThrow( - 'No fields found to create GraphQLFilter for TestInvalidFilter' - ); + expect(() => UpdateFilterType(TestInvalidFilter)).toThrow('No fields found to create GraphQLFilter for TestInvalidFilter'); }); it('should throw an error when the field type is unknown', () => { @@ -427,9 +423,7 @@ describe('filter types', (): void => { fakeType!: EnumField; } - expect(() => UpdateFilterType(TestInvalidFilter)).toThrow( - 'Unable to create filter comparison for {"ONE":"one"}.' - ); + expect(() => UpdateFilterType(TestInvalidFilter)).toThrow('Unable to create filter comparison for {"ONE":"one"}.'); }); it('should convert and filters to filter class', () => { @@ -481,9 +475,7 @@ describe('filter types', (): void => { @ObjectType('TestNoFields') class TestInvalidFilter {} - expect(() => DeleteFilterType(TestInvalidFilter)).toThrow( - 'No fields found to create GraphQLFilter for TestInvalidFilter' - ); + expect(() => DeleteFilterType(TestInvalidFilter)).toThrow('No fields found to create GraphQLFilter for TestInvalidFilter'); }); it('should throw an error when the field type is unknown', () => { @@ -497,9 +489,7 @@ describe('filter types', (): void => { fakeType!: EnumField; } - expect(() => DeleteFilterType(TestInvalidFilter)).toThrow( - 'Unable to create filter comparison for {"ONE":"one"}.' - ); + expect(() => DeleteFilterType(TestInvalidFilter)).toThrow('Unable to create filter comparison for {"ONE":"one"}.'); }); it('should convert and filters to filter class', () => { @@ -567,9 +557,7 @@ describe('filter types', (): void => { fakeType!: EnumField; } - expect(() => SubscriptionFilterType(TestInvalidFilter)).toThrow( - 'Unable to create filter comparison for {"ONE":"one"}.' - ); + expect(() => SubscriptionFilterType(TestInvalidFilter)).toThrow('Unable to create filter comparison for {"ONE":"one"}.'); }); it('should convert and filters to filter class', () => { diff --git a/packages/query-graphql/__tests__/types/query/sorting.type.spec.ts b/packages/query-graphql/__tests__/types/query/sorting.type.spec.ts index 9d6e352a3..53fac01fe 100644 --- a/packages/query-graphql/__tests__/types/query/sorting.type.spec.ts +++ b/packages/query-graphql/__tests__/types/query/sorting.type.spec.ts @@ -35,12 +35,14 @@ describe('SortingType', (): void => { return 1; } } + const schema = await generateSchema([SortingTypeSpec]); expect(schema).toMatchSnapshot(); }); it('should throw an error if the class is not annotated with @ObjectType', () => { class BadTestSort {} + expect(() => getOrCreateSortType(BadTestSort)).toThrow( 'Unable to make SortType. Ensure BadTestSort is annotated with @nestjs/graphql @ObjectType' ); @@ -48,6 +50,7 @@ describe('SortingType', (): void => { it('should throw an error if no fields are found', () => { @ObjectType() class BadTestSort {} + expect(() => getOrCreateSortType(BadTestSort)).toThrow( 'No fields found to create SortType for BadTestSort. Ensure fields are annotated with @FilterableField' ); diff --git a/packages/query-graphql/src/decorators/index.ts b/packages/query-graphql/src/decorators/index.ts index 03462d736..bedcfb1f8 100644 --- a/packages/query-graphql/src/decorators/index.ts +++ b/packages/query-graphql/src/decorators/index.ts @@ -14,7 +14,7 @@ export { FilterableUnPagedRelation, Relation, FilterableRelation, - RelationDecoratorOpts, + RelationOneDecoratorOpts, RelationTypeFunc, getRelations } from './relation.decorator'; diff --git a/packages/query-graphql/src/decorators/query-options.decorator.ts b/packages/query-graphql/src/decorators/query-options.decorator.ts index db4f9a8c2..3a1d906bd 100644 --- a/packages/query-graphql/src/decorators/query-options.decorator.ts +++ b/packages/query-graphql/src/decorators/query-options.decorator.ts @@ -13,5 +13,4 @@ export function QueryOptions(opts: QueryOptionsDecoratorOpts) { }; } -export const getQueryOptions = (DTOClass: Class): MetaValue> => - valueReflector.get(DTOClass); +export const getQueryOptions = (DTOClass: Class): MetaValue> => valueReflector.get(DTOClass); diff --git a/packages/query-graphql/src/decorators/relation.decorator.ts b/packages/query-graphql/src/decorators/relation.decorator.ts index 7bed5f11e..d2572655d 100644 --- a/packages/query-graphql/src/decorators/relation.decorator.ts +++ b/packages/query-graphql/src/decorators/relation.decorator.ts @@ -4,10 +4,12 @@ import { PagingStrategies } from '../types/query/paging'; import { RELATION_KEY } from './constants'; import { BaseResolverOptions } from './resolver-method.decorator'; import { mergeBaseResolverOpts } from '../common'; +import { ResolverManyRelation, ResolverOneRelation } from '../resolvers/relations/relations.interface'; export const reflector = new ArrayReflector(RELATION_KEY); -export type RelationDecoratorOpts = Omit, 'DTO' | 'allowFiltering'>; +export type RelationOneDecoratorOpts = Omit, 'DTO' | 'allowFiltering'>; +export type RelationManyDecoratorOpts = Omit, 'DTO' | 'allowFiltering'>; export type RelationTypeFunc = () => Class; export type RelationClassDecorator = >(DTOClass: Cls) => Cls | void; @@ -27,10 +29,7 @@ function getRelationsDescriptors(DTOClass: Class): RelationDescriptor< }, [] as RelationDescriptor[]); } -function convertRelationsToOpts( - relations: RelationDescriptor[], - baseOpts?: BaseResolverOptions -): RelationsOpts { +function convertRelationsToOpts(relations: RelationDescriptor[], baseOpts?: BaseResolverOptions): RelationsOpts { const relationOpts: RelationsOpts = {}; relations.forEach((relation) => { const DTO = relation.relationTypeFunc(); @@ -50,31 +49,31 @@ export function getRelations(DTOClass: Class, opts?: BaseResolverOptio return convertRelationsToOpts(relationDescriptors, opts); } -const relationDecorator = - (isMany: boolean, allowFiltering: boolean, pagingStrategy?: PagingStrategies) => - ( - name: string, - relationTypeFunc: RelationTypeFunc, - options?: RelationDecoratorOpts - ): RelationClassDecorator => - >(DTOClass: Cls): Cls | void => { - reflector.append(DTOClass, { - name, - isMany, - relationOpts: { pagingStrategy, allowFiltering, ...options }, - relationTypeFunc - }); - return DTOClass; - }; +const relationDecorator = (isMany: boolean, allowFiltering: boolean, pagingStrategy?: PagingStrategies) => { + return ( + name: string, + relationTypeFunc: RelationTypeFunc, + options?: IsMany extends true ? RelationManyDecoratorOpts : RelationOneDecoratorOpts + ): RelationClassDecorator => + >(DTOClass: Cls): Cls | void => { + reflector.append(DTOClass, { + name, + isMany, + relationOpts: { pagingStrategy, allowFiltering, ...options }, + relationTypeFunc + }); + return DTOClass; + }; +}; -export const Relation = relationDecorator(false, false); -export const FilterableRelation = relationDecorator(false, true); +export const Relation = relationDecorator(false, false); +export const FilterableRelation = relationDecorator(false, true); -export const UnPagedRelation = relationDecorator(true, false, PagingStrategies.NONE); -export const FilterableUnPagedRelation = relationDecorator(true, true, PagingStrategies.NONE); +export const UnPagedRelation = relationDecorator(true, false, PagingStrategies.NONE); +export const FilterableUnPagedRelation = relationDecorator(true, true, PagingStrategies.NONE); -export const OffsetConnection = relationDecorator(true, false, PagingStrategies.OFFSET); -export const FilterableOffsetConnection = relationDecorator(true, true, PagingStrategies.OFFSET); +export const OffsetConnection = relationDecorator(true, false, PagingStrategies.OFFSET); +export const FilterableOffsetConnection = relationDecorator(true, true, PagingStrategies.OFFSET); -export const CursorConnection = relationDecorator(true, false, PagingStrategies.CURSOR); -export const FilterableCursorConnection = relationDecorator(true, true, PagingStrategies.CURSOR); +export const CursorConnection = relationDecorator(true, false, PagingStrategies.CURSOR); +export const FilterableCursorConnection = relationDecorator(true, true, PagingStrategies.CURSOR); diff --git a/packages/query-graphql/src/index.ts b/packages/query-graphql/src/index.ts index 93fbcae78..3d67a21d9 100644 --- a/packages/query-graphql/src/index.ts +++ b/packages/query-graphql/src/index.ts @@ -12,7 +12,7 @@ export { UnPagedRelation, FilterableUnPagedRelation, RelationTypeFunc, - RelationDecoratorOpts, + RelationOneDecoratorOpts, Reference, ReferenceTypeFunc, ReferenceDecoratorOpts, diff --git a/packages/query-graphql/src/resolvers/relations/relations.interface.ts b/packages/query-graphql/src/resolvers/relations/relations.interface.ts index c92739dcd..92ceb480c 100644 --- a/packages/query-graphql/src/resolvers/relations/relations.interface.ts +++ b/packages/query-graphql/src/resolvers/relations/relations.interface.ts @@ -86,15 +86,18 @@ export type ResolverRelation = { export type RelationTypeMap = Record; -export type RelationsOpts = { +export type ResolverOneRelation = Omit, 'disableFilter' | 'disableSorting'>; +export type ResolverManyRelation = ResolverRelation; + +export type RelationsOpts = { /** * All relations that are a single record */ - one?: RelationTypeMap>; + one?: RelationTypeMap>; /** * All relations that have multiple records */ - many?: RelationTypeMap>; + many?: RelationTypeMap>; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/query-graphql/src/types/query/index.ts b/packages/query-graphql/src/types/query/index.ts index 5638987c7..f2e0e90d3 100644 --- a/packages/query-graphql/src/types/query/index.ts +++ b/packages/query-graphql/src/types/query/index.ts @@ -10,11 +10,5 @@ export { StaticQueryType, QueryType } from './query-args'; -export { - FilterType, - DeleteFilterType, - UpdateFilterType, - SubscriptionFilterType, - AggregateFilterType -} from './filter.type'; +export { FilterType, DeleteFilterType, UpdateFilterType, SubscriptionFilterType, AggregateFilterType } from './filter.type'; export { CursorPagingType, OffsetPagingType, NonePagingType, PagingTypes, PagingStrategies } from './paging'; diff --git a/packages/query-graphql/src/types/query/query-args.type.ts b/packages/query-graphql/src/types/query/query-args.type.ts index 20405a7a4..28c393cb3 100644 --- a/packages/query-graphql/src/types/query/query-args.type.ts +++ b/packages/query-graphql/src/types/query/query-args.type.ts @@ -41,21 +41,17 @@ export function QueryArgsType( DTOClass: Class, opts: CursorQueryArgsTypeOpts ): StaticQueryType; -export function QueryArgsType( - DTOClass: Class, - opts?: QueryArgsTypeOpts -): StaticQueryType; -export function QueryArgsType( - DTOClass: Class, - opts?: QueryArgsTypeOpts -): StaticQueryType { +export function QueryArgsType(DTOClass: Class, opts?: QueryArgsTypeOpts): StaticQueryType; +export function QueryArgsType(DTOClass: Class, opts?: QueryArgsTypeOpts): StaticQueryType { // override any options from the DTO with the options passed in const mergedOpts = getMergedQueryOpts(DTOClass, opts); if (mergedOpts.pagingStrategy === PagingStrategies.OFFSET) { return createOffsetQueryArgs(DTOClass, mergedOpts); } + if (mergedOpts.pagingStrategy === PagingStrategies.NONE) { return createNonePagingQueryArgsType(DTOClass, mergedOpts); } + return createCursorQueryArgsType(DTOClass, mergedOpts); } diff --git a/packages/query-graphql/src/types/query/query-args/cursor-query-args.type.ts b/packages/query-graphql/src/types/query/query-args/cursor-query-args.type.ts index d1dfca1aa..d9666351c 100644 --- a/packages/query-graphql/src/types/query/query-args/cursor-query-args.type.ts +++ b/packages/query-graphql/src/types/query/query-args/cursor-query-args.type.ts @@ -9,8 +9,10 @@ import { PagingStrategies, getOrCreateCursorPagingType, CursorPagingType } from import { FilterType } from '../filter.type'; import { getOrCreateSortType } from '../sorting.type'; import { getOrCreateCursorConnectionType } from '../../connection'; +import { SkipIf } from '../../../decorators'; export type CursorQueryArgsType = QueryType; + export function createCursorQueryArgsType( DTOClass: Class, opts: CursorQueryArgsTypeOpts = { ...DEFAULT_QUERY_OPTS, pagingStrategy: PagingStrategies.CURSOR } @@ -40,22 +42,29 @@ export function createCursorQueryArgsType( @Type(() => P) paging?: CursorPagingType; - @Field(() => F, { - defaultValue: !F.hasRequiredFilters ? opts.defaultFilter ?? DEFAULT_QUERY_OPTS.defaultFilter : undefined, - description: 'Specify to filter the records returned.', - nullable: false - }) + @SkipIf( + () => opts.disableFilter, + Field(() => F, { + defaultValue: !F.hasRequiredFilters ? opts.defaultFilter ?? DEFAULT_QUERY_OPTS.defaultFilter : undefined, + description: 'Specify to filter the records returned.', + nullable: false + }) + ) @ValidateNested() @Type(() => F) filter?: Filter; - @Field(() => [S], { - defaultValue: opts.defaultSort ?? DEFAULT_QUERY_OPTS.defaultSort, - description: 'Specify to sort results.' - }) + @SkipIf( + () => opts.disableSort, + Field(() => [S], { + defaultValue: opts.defaultSort ?? DEFAULT_QUERY_OPTS.defaultSort, + description: 'Specify to sort results.' + }) + ) @ValidateNested() @Type(() => S) sorting?: SortField[]; } + return QueryArgs; } diff --git a/packages/query-graphql/src/types/query/query-args/interfaces.ts b/packages/query-graphql/src/types/query/query-args/interfaces.ts index 9665d7bc2..3fbf5e346 100644 --- a/packages/query-graphql/src/types/query/query-args/interfaces.ts +++ b/packages/query-graphql/src/types/query/query-args/interfaces.ts @@ -24,11 +24,19 @@ export type BaseQueryArgsTypeOpts = { * [Default=[]] */ defaultSort?: SortField[]; + /** + * Disable the sorting of this relation. + */ + disableSort?: boolean; /** * Default filter. * [Default=\{\}] */ defaultFilter?: Filter; + /** + * Disable the filtering of this relation. + */ + disableFilter?: boolean; } & FilterTypeOptions; export interface CursorQueryArgsTypeOpts extends BaseQueryArgsTypeOpts, CursorConnectionOptions { diff --git a/packages/query-graphql/src/types/query/query-args/none-paging-query-args.type.ts b/packages/query-graphql/src/types/query/query-args/none-paging-query-args.type.ts index ea2786f43..3d8eaf94a 100644 --- a/packages/query-graphql/src/types/query/query-args/none-paging-query-args.type.ts +++ b/packages/query-graphql/src/types/query/query-args/none-paging-query-args.type.ts @@ -8,8 +8,10 @@ import { DEFAULT_QUERY_OPTS } from './constants'; import { NonePagingQueryArgsTypeOpts, QueryType, StaticQueryType } from './interfaces'; import { FilterType } from '../filter.type'; import { getOrCreateSortType } from '../sorting.type'; +import { SkipIf } from '../../../decorators'; export type NonePagingQueryArgsType = QueryType; + export function createNonePagingQueryArgsType( DTOClass: Class, opts: NonePagingQueryArgsTypeOpts = { ...DEFAULT_QUERY_OPTS, pagingStrategy: PagingStrategies.NONE } @@ -29,24 +31,31 @@ export function createNonePagingQueryArgsType( static ConnectionType = C; - @Field(() => F, { - defaultValue: !F.hasRequiredFilters ? opts.defaultFilter ?? DEFAULT_QUERY_OPTS.defaultFilter : undefined, - description: 'Specify to filter the records returned.', - nullable: false - }) + @SkipIf( + () => opts.disableFilter, + Field(() => F, { + defaultValue: !F.hasRequiredFilters ? opts.defaultFilter ?? DEFAULT_QUERY_OPTS.defaultFilter : undefined, + description: 'Specify to filter the records returned.', + nullable: false + }) + ) @ValidateNested() @Type(() => F) filter?: Filter; - @Field(() => [S], { - defaultValue: opts.defaultSort ?? DEFAULT_QUERY_OPTS.defaultSort, - description: 'Specify to sort results.' - }) + @SkipIf( + () => opts.disableSort, + Field(() => [S], { + defaultValue: opts.defaultSort ?? DEFAULT_QUERY_OPTS.defaultSort, + description: 'Specify to sort results.' + }) + ) @ValidateNested() @Type(() => S) sorting?: SortField[]; paging?: NonePagingType; } + return QueryArgs; } diff --git a/packages/query-graphql/src/types/query/query-args/offset-query-args.type.ts b/packages/query-graphql/src/types/query/query-args/offset-query-args.type.ts index bae837d86..a6e44bfae 100644 --- a/packages/query-graphql/src/types/query/query-args/offset-query-args.type.ts +++ b/packages/query-graphql/src/types/query/query-args/offset-query-args.type.ts @@ -9,6 +9,7 @@ import { getOrCreateOffsetPagingType, OffsetPagingType, PagingStrategies } from import { FilterType } from '../filter.type'; import { getOrCreateSortType } from '../sorting.type'; import { getOrCreateOffsetConnectionType } from '../../connection'; +import { SkipIf } from '../../../decorators'; export type OffsetQueryArgsType = QueryType; export function createOffsetQueryArgs( @@ -39,6 +40,7 @@ export function createOffsetQueryArgs( @Type(() => P) paging?: OffsetPagingType; + @SkipIf(() => opts.disableFilter) @Field(() => F, { defaultValue: !F.hasRequiredFilters ? opts.defaultFilter ?? DEFAULT_QUERY_OPTS.defaultFilter : undefined, description: 'Specify to filter the records returned.', @@ -48,6 +50,7 @@ export function createOffsetQueryArgs( @Type(() => F) filter?: Filter; + @SkipIf(() => opts.disableSort) @Field(() => [S], { defaultValue: opts.defaultSort ?? DEFAULT_QUERY_OPTS.defaultSort, description: 'Specify to sort results.' diff --git a/packages/query-graphql/src/types/query/sorting.type.ts b/packages/query-graphql/src/types/query/sorting.type.ts index bec0202b4..cc35267af 100644 --- a/packages/query-graphql/src/types/query/sorting.type.ts +++ b/packages/query-graphql/src/types/query/sorting.type.ts @@ -21,14 +21,15 @@ export function getOrCreateSortType(TClass: Class): Class> { return reflector.memoize(TClass, () => { const prefix = getGraphqlObjectName(TClass, 'Unable to make SortType.'); const fields = getFilterableFields(TClass); + if (!fields.length) { - throw new Error( - `No fields found to create SortType for ${TClass.name}. Ensure fields are annotated with @FilterableField` - ); + throw new Error(`No fields found to create SortType for ${TClass.name}. Ensure fields are annotated with @FilterableField`); } - const fieldNames = fields.map((f) => f.propertyName); - const fieldNameMap = fieldNames.reduce((acc, f) => ({ ...acc, [f]: f }), {}); + + const fieldNames = fields.map((field) => field.propertyName); + const fieldNameMap = fieldNames.reduce((acc, field) => ({ ...acc, [field]: field }), {}); registerEnumType(fieldNameMap, { name: `${prefix}SortFields` }); + @InputType(`${prefix}Sort`) class Sort { @Field(() => fieldNameMap) @@ -44,6 +45,7 @@ export function getOrCreateSortType(TClass: Class): Class> { @IsEnum(SortNulls) nulls?: SortNulls; } + return Sort; }); } From 1c105c60afa73171f195593e68310597a0095215 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Fri, 27 May 2022 17:27:39 +0200 Subject: [PATCH 2/8] docs: Added `disableFilter` and `disableSort` documentation --- documentation/docs/graphql/relations.mdx | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/documentation/docs/graphql/relations.mdx b/documentation/docs/graphql/relations.mdx index 2504a93e0..3e7f82a06 100644 --- a/documentation/docs/graphql/relations.mdx +++ b/documentation/docs/graphql/relations.mdx @@ -1127,6 +1127,51 @@ To disable the `read` `queries` you can set the `disableRead` option to `true`. +#### Disable `filter` or `sorting` in relations + +To disable the `filter` or `sorting` of relations you can set the `disableFilter` or/and `disableSort` option to `true`. + + + + + This is not available in `relation` as it will only fetch one record. + + + + + ```ts + // disable reading the connection + @CursorConnection('subTasks', () => SubTaskDTO, { disableFilter: true, disableSort: true }) + ``` + + + + + ```ts + // disable reading the relation + @OffsetConnection('subTaskConnection', () => SubTaskDTO, { disableFilter: true, disableSort: true }) + ``` + + + + + ```ts + // disable reading the relation + @UnPagedRelation('subTaskConnection', () => SubTaskDTO, { disableFilter: true, disableSort: true }) + ``` + + + + ### Disable Updates To disable the `update` `mutations` you can set the `disableUpdate` option to `true`. From 9dcc28f9da52e1a9a7d2e0db9c26899cc4f80d84 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Fri, 27 May 2022 17:30:48 +0200 Subject: [PATCH 3/8] refactor: Fix linting --- .eslintrc.json | 1 - examples/auth/e2e/sub-task.resolver.spec.ts | 8 +- examples/auth/e2e/tag.resolver.spec.ts | 6 +- examples/auth/e2e/todo-item.resolver.spec.ts | 11 +- examples/auth/src/auth/auth.service.ts | 5 +- .../src/sub-task/dto/subtask-input.dto.ts | 7 +- .../src/sub-task/dto/subtask-update.dto.ts | 7 +- examples/auth/src/tag/tag.entity.ts | 10 +- .../src/todo-item/dto/todo-item-input.dto.ts | 7 +- .../src/todo-item/dto/todo-item-update.dto.ts | 7 +- examples/auth/src/user/user.entity.ts | 10 +- examples/basic/e2e/sub-task.resolver.spec.ts | 8 +- examples/basic/e2e/todo-item.resolver.spec.ts | 8 +- examples/basic/src/tag/tag.entity.ts | 10 +- .../complexity/e2e/sub-task.resolver.spec.ts | 8 +- .../complexity/e2e/todo-item.resolver.spec.ts | 8 +- examples/complexity/src/tag/tag.entity.ts | 10 +- .../custom-id/e2e/sub-task.resolver.spec.ts | 8 +- examples/custom-id/e2e/tag.resolver.spec.ts | 8 +- .../custom-id/e2e/todo-item.resolver.spec.ts | 8 +- examples/custom-id/src/tag/tag.entity.ts | 10 +- .../e2e/todo-item.resolver.spec.ts | 8 +- .../e2e/sub-task.resolver.spec.ts | 8 +- .../e2e/todo-item.resolver.spec.ts | 8 +- .../filters/e2e/todo-item.resolver.spec.ts | 8 +- examples/hooks/e2e/sub-task.resolver.spec.ts | 8 +- examples/hooks/e2e/todo-item.resolver.spec.ts | 8 +- examples/hooks/src/tag/tag.entity.ts | 10 +- .../hooks/src/todo-item/todo-item.resolver.ts | 5 +- .../no-paging/e2e/sub-task.resolver.spec.ts | 8 +- .../no-paging/e2e/todo-item.resolver.spec.ts | 8 +- examples/no-paging/src/tag/tag.entity.ts | 10 +- .../e2e/sub-task.resolver.spec.ts | 8 +- .../offset-paging/e2e/tag.resolver.spec.ts | 3 +- .../e2e/todo-item.resolver.spec.ts | 11 +- examples/offset-paging/src/tag/tag.entity.ts | 10 +- .../sequelize/e2e/sub-task.resolver.spec.ts | 8 +- examples/sequelize/e2e/tag.resolver.spec.ts | 6 +- .../sequelize/e2e/todo-item.resolver.spec.ts | 11 +- examples/sequelize/src/tag/tag.entity.ts | 11 +- examples/subscriptions/src/tag/tag.entity.ts | 10 +- .../e2e/todo-item.resolver.spec.ts | 8 +- .../typeorm/e2e/sub-task.resolver.spec.ts | 8 +- examples/typeorm/e2e/tag.resolver.spec.ts | 6 +- .../typeorm/e2e/todo-item.resolver.spec.ts | 11 +- .../src/sub-task/dto/subtask-input.dto.ts | 7 +- .../src/sub-task/dto/subtask-update.dto.ts | 7 +- examples/typeorm/src/tag/tag.entity.ts | 10 +- .../src/todo-item/dto/todo-item-input.dto.ts | 7 +- .../src/todo-item/dto/todo-item-update.dto.ts | 7 +- .../class-transformer.assembler.spec.ts | 7 +- .../decorators/assembler.decorator.spec.ts | 4 +- ...-assembler-query-service.decorator.spec.ts | 7 +- packages/core/__tests__/helpers.spec.ts | 118 +++--------------- .../services/assembler-query.service.spec.ts | 58 +++------ .../services/noop-query.service.spec.ts | 14 +-- .../services/proxy-query.service.spec.ts | 4 +- .../services/relation-query.service.spec.ts | 26 ++-- packages/core/src/common/reflect.utils.ts | 12 +- ...nject-assembler-query-service.decorator.ts | 9 +- .../core/src/helpers/comparison.builder.ts | 18 +-- packages/core/src/helpers/filter.builder.ts | 5 +- .../filter-field-comparison.interface.ts | 14 +-- packages/core/src/providers.ts | 9 +- .../src/services/assembler-query.service.ts | 13 +- .../src/services/relation-query.service.ts | 6 +- .../auth/default-crud-auth.service.spec.ts | 8 +- .../decorators/relation.decorator.spec.ts | 7 +- .../aggregate-relations.loader.spec.ts | 16 +-- .../loaders/count-relations.loader.spec.ts | 11 +- .../loaders/query-relations.loader.spec.ts | 7 +- .../resolvers/create.resolver.spec.ts | 11 +- .../resolvers/delete.resolver.spec.ts | 9 +- .../federation/federation.resolver.spec.ts | 8 +- .../__tests__/resolvers/read.resolver.spec.ts | 3 +- .../aggregate-relation.resolver.spec.ts | 8 +- .../references-relation.resolver.spec.ts | 8 +- .../remove-relation.resolver.spec.ts | 16 +-- .../update-relation.resolver.spec.ts | 20 +-- .../resolvers/update.resolver.spec.ts | 67 ++++------ .../connection/cursor-connection.type.spec.ts | 49 +++----- .../connection/offset-connection.type.spec.ts | 3 +- .../src/auth/default-crud.authorizer.ts | 5 +- .../query-graphql/src/common/dto.utils.ts | 5 +- .../src/common/resolver.utils.ts | 5 +- .../decorators/authorize-filter.decorator.ts | 11 +- .../src/decorators/hook.decorator.ts | 6 +- packages/query-graphql/src/hooks/tokens.ts | 3 +- .../src/loader/aggregate-relations.loader.ts | 12 +- .../src/providers/resolver.provider.ts | 8 +- .../src/resolvers/aggregate.resolver.ts | 17 +-- .../src/resolvers/create.resolver.ts | 25 +--- .../src/resolvers/crud.resolver.ts | 5 +- .../src/resolvers/delete.resolver.ts | 29 +---- .../federation/federation.resolver.ts | 5 +- .../src/resolvers/read.resolver.ts | 8 +- .../src/resolvers/reference.resolver.ts | 5 +- .../relations/aggregate-relations.resolver.ts | 9 +- .../relations/read-relations.resolver.ts | 10 +- .../src/resolvers/resolver.interface.ts | 13 +- .../src/resolvers/update.resolver.ts | 38 ++---- .../aggregate/aggregate-response.type.ts | 5 +- .../types/connection/array-connection.type.ts | 9 +- .../pager/strategies/keyset.pager-strategy.ts | 9 +- .../src/types/connection/interfaces.ts | 3 +- .../types/connection/offset/pager/pager.ts | 6 +- .../field-comparison.factory.ts | 9 +- .../src/types/query/paging/index.ts | 8 +- .../src/types/query/query-args/interfaces.ts | 7 +- .../src/types/update-many-input.type.ts | 5 +- .../validators/property-max.validator.ts | 4 +- .../__tests__/providers.spec.ts | 4 +- .../__tests__/query/where.builder.spec.ts | 14 +-- .../services/mongoose-query.service.spec.ts | 49 +++----- .../src/mongoose-types.helper.ts | 6 +- packages/query-mongoose/src/providers.ts | 9 +- .../src/query/comparison.builder.ts | 5 +- .../query-mongoose/src/query/where.builder.ts | 9 +- .../src/services/mongoose-query.service.ts | 5 +- .../src/services/reference-query.service.ts | 6 +- .../__tests__/__fixtures__/seeds.ts | 7 +- .../__tests__/query/where.builder.spec.ts | 34 +---- .../services/sequelize-query.service.spec.ts | 43 ++----- .../src/query/filter-query.builder.ts | 10 +- .../src/query/sql-comparison.builder.ts | 10 +- .../src/query/where.builder.ts | 13 +- .../src/services/relation-query.service.ts | 23 ++-- .../src/services/sequelize-query.service.ts | 5 +- .../__tests__/__fixtures__/seeds.ts | 4 +- .../__tests__/query/where.builder.spec.ts | 19 +-- packages/query-typegoose/src/providers.ts | 5 +- .../src/query/where.builder.ts | 4 +- .../src/services/reference-query.service.ts | 20 +-- .../src/services/typegoose-query-service.ts | 21 +--- .../src/typegoose-types.helper.ts | 6 +- .../__tests__/__fixtures__/seeds.ts | 8 +- .../__tests__/__fixtures__/test.entity.ts | 12 +- .../query/filter-query.builder.spec.ts | 28 ++--- .../query/relation-query.builder.spec.ts | 5 +- .../query/sql-comparison.builder.spec.ts | 4 +- .../__tests__/query/where.builder.spec.ts | 14 +-- .../services/typeorm-query.service.spec.ts | 104 +++++---------- .../src/query/aggregate.builder.ts | 5 +- .../src/query/filter-query.builder.ts | 5 +- .../src/query/relation-query.builder.ts | 14 +-- .../src/query/sql-comparison.builder.ts | 5 +- .../query-typeorm/src/query/where.builder.ts | 7 +- .../src/services/relation-query.service.ts | 27 ++-- 148 files changed, 405 insertions(+), 1426 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 2cac1b0a7..a1659a4ea 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -42,7 +42,6 @@ "rules": { // airbnb default is 1 "max-classes-per-file": ["error", 5], - "max-len": ["error", { "code": 130 }], // never allow default export "import/prefer-default-export": "off", // never allow default export diff --git a/examples/auth/e2e/sub-task.resolver.spec.ts b/examples/auth/e2e/sub-task.resolver.spec.ts index 6534ff585..7d190f0b8 100644 --- a/examples/auth/e2e/sub-task.resolver.spec.ts +++ b/examples/auth/e2e/sub-task.resolver.spec.ts @@ -722,9 +722,7 @@ describe('SubTaskResolver (auth - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -913,9 +911,7 @@ describe('SubTaskResolver (auth - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/auth/e2e/tag.resolver.spec.ts b/examples/auth/e2e/tag.resolver.spec.ts index 25494b2cb..78e1c7610 100644 --- a/examples/auth/e2e/tag.resolver.spec.ts +++ b/examples/auth/e2e/tag.resolver.spec.ts @@ -1107,8 +1107,7 @@ describe('TagResolver (auth - e2e)', () => { }) .expect(200) .then(({ body }) => { - const { edges, pageInfo, totalCount }: CursorConnectionType = - body.data.addTodoItemsToTag.todoItems; + const { edges, pageInfo, totalCount }: CursorConnectionType = body.data.addTodoItemsToTag.todoItems; expect(body.data.addTodoItemsToTag.id).toBe('1'); expect(pageInfo).toEqual({ endCursor: 'YXJyYXljb25uZWN0aW9uOjQ=', @@ -1178,8 +1177,7 @@ describe('TagResolver (auth - e2e)', () => { }) .expect(200) .then(({ body }) => { - const { edges, pageInfo, totalCount }: CursorConnectionType = - body.data.removeTodoItemsFromTag.todoItems; + const { edges, pageInfo, totalCount }: CursorConnectionType = body.data.removeTodoItemsFromTag.todoItems; expect(body.data.removeTodoItemsFromTag.id).toBe('1'); expect(pageInfo).toEqual({ endCursor: 'YXJyYXljb25uZWN0aW9uOjE=', diff --git a/examples/auth/e2e/todo-item.resolver.spec.ts b/examples/auth/e2e/todo-item.resolver.spec.ts index e5fc9fb8f..120a6fe00 100644 --- a/examples/auth/e2e/todo-item.resolver.spec.ts +++ b/examples/auth/e2e/todo-item.resolver.spec.ts @@ -1102,9 +1102,7 @@ describe('TodoItemResolver (auth - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -1439,9 +1437,7 @@ describe('TodoItemResolver (auth - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.'); })); }); @@ -1615,8 +1611,7 @@ describe('TodoItemResolver (auth - e2e)', () => { }) .expect(200) .then(({ body }) => { - const { edges, pageInfo, totalCount }: CursorConnectionType = - body.data.addSubTasksToTodoItem.subTasks; + const { edges, pageInfo, totalCount }: CursorConnectionType = body.data.addSubTasksToTodoItem.subTasks; expect(body.data.addSubTasksToTodoItem.id).toBe('1'); expect(pageInfo).toEqual({ endCursor: 'YXJyYXljb25uZWN0aW9uOjU=', diff --git a/examples/auth/src/auth/auth.service.ts b/examples/auth/src/auth/auth.service.ts index a91809441..283b0e8a1 100644 --- a/examples/auth/src/auth/auth.service.ts +++ b/examples/auth/src/auth/auth.service.ts @@ -8,10 +8,7 @@ import { UserDTO } from '../user/user.dto'; @Injectable() export class AuthService { - constructor( - @InjectQueryService(UserEntity) private usersService: QueryService, - private jwtService: JwtService - ) {} + constructor(@InjectQueryService(UserEntity) private usersService: QueryService, private jwtService: JwtService) {} async validateUser(username: string, pass: string): Promise { const [user] = await this.usersService.query({ filter: { username: { eq: username } }, paging: { limit: 1 } }); diff --git a/examples/auth/src/sub-task/dto/subtask-input.dto.ts b/examples/auth/src/sub-task/dto/subtask-input.dto.ts index be94abcbd..8276f3af2 100644 --- a/examples/auth/src/sub-task/dto/subtask-input.dto.ts +++ b/examples/auth/src/sub-task/dto/subtask-input.dto.ts @@ -1,11 +1,6 @@ import { Field, InputType, ID } from '@nestjs/graphql'; import { IsOptional, IsString, IsBoolean, IsNotEmpty } from 'class-validator'; -import { - BeforeCreateMany, - BeforeCreateOne, - CreateManyInputType, - CreateOneInputType -} from '@ptc-org/nestjs-query-graphql'; +import { BeforeCreateMany, BeforeCreateOne, CreateManyInputType, CreateOneInputType } from '@ptc-org/nestjs-query-graphql'; import { UserContext } from '../../auth/auth.interfaces'; @InputType('SubTaskInput') diff --git a/examples/auth/src/sub-task/dto/subtask-update.dto.ts b/examples/auth/src/sub-task/dto/subtask-update.dto.ts index b585d4ced..c2172bc89 100644 --- a/examples/auth/src/sub-task/dto/subtask-update.dto.ts +++ b/examples/auth/src/sub-task/dto/subtask-update.dto.ts @@ -1,11 +1,6 @@ import { Field, InputType } from '@nestjs/graphql'; import { IsOptional, IsBoolean, IsString, IsNotEmpty } from 'class-validator'; -import { - BeforeUpdateMany, - BeforeUpdateOne, - UpdateManyInputType, - UpdateOneInputType -} from '@ptc-org/nestjs-query-graphql'; +import { BeforeUpdateMany, BeforeUpdateOne, UpdateManyInputType, UpdateOneInputType } from '@ptc-org/nestjs-query-graphql'; import { SubTaskDTO } from './sub-task.dto'; import { UserContext } from '../../auth/auth.interfaces'; diff --git a/examples/auth/src/tag/tag.entity.ts b/examples/auth/src/tag/tag.entity.ts index 3f8b53bcc..1f4f3967f 100644 --- a/examples/auth/src/tag/tag.entity.ts +++ b/examples/auth/src/tag/tag.entity.ts @@ -1,12 +1,4 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ObjectType, - ManyToMany -} from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ObjectType, ManyToMany } from 'typeorm'; import { TodoItemEntity } from '../todo-item/todo-item.entity'; @Entity({ name: 'tag' }) diff --git a/examples/auth/src/todo-item/dto/todo-item-input.dto.ts b/examples/auth/src/todo-item/dto/todo-item-input.dto.ts index 31e008a6e..df40330df 100644 --- a/examples/auth/src/todo-item/dto/todo-item-input.dto.ts +++ b/examples/auth/src/todo-item/dto/todo-item-input.dto.ts @@ -1,11 +1,6 @@ import { IsString, MaxLength, IsBoolean } from 'class-validator'; import { Field, InputType } from '@nestjs/graphql'; -import { - BeforeCreateMany, - BeforeCreateOne, - CreateManyInputType, - CreateOneInputType -} from '@ptc-org/nestjs-query-graphql'; +import { BeforeCreateMany, BeforeCreateOne, CreateManyInputType, CreateOneInputType } from '@ptc-org/nestjs-query-graphql'; import { UserContext } from '../../auth/auth.interfaces'; @InputType('TodoItemInput') diff --git a/examples/auth/src/todo-item/dto/todo-item-update.dto.ts b/examples/auth/src/todo-item/dto/todo-item-update.dto.ts index 7f4cfed32..0f3747b4d 100644 --- a/examples/auth/src/todo-item/dto/todo-item-update.dto.ts +++ b/examples/auth/src/todo-item/dto/todo-item-update.dto.ts @@ -1,11 +1,6 @@ import { Field, InputType } from '@nestjs/graphql'; import { IsBoolean, IsNumber, IsOptional, IsString, MaxLength } from 'class-validator'; -import { - BeforeUpdateMany, - BeforeUpdateOne, - UpdateManyInputType, - UpdateOneInputType -} from '@ptc-org/nestjs-query-graphql'; +import { BeforeUpdateMany, BeforeUpdateOne, UpdateManyInputType, UpdateOneInputType } from '@ptc-org/nestjs-query-graphql'; import { TodoItemDTO } from './todo-item.dto'; import { UserContext } from '../../auth/auth.interfaces'; diff --git a/examples/auth/src/user/user.entity.ts b/examples/auth/src/user/user.entity.ts index 40a83813c..a4b50b528 100644 --- a/examples/auth/src/user/user.entity.ts +++ b/examples/auth/src/user/user.entity.ts @@ -1,12 +1,4 @@ -import { - Column, - CreateDateColumn, - Entity, - PrimaryGeneratedColumn, - UpdateDateColumn, - OneToMany, - JoinTable -} from 'typeorm'; +import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, OneToMany, JoinTable } from 'typeorm'; import { TodoItemEntity } from '../todo-item/todo-item.entity'; @Entity({ name: 'user' }) diff --git a/examples/basic/e2e/sub-task.resolver.spec.ts b/examples/basic/e2e/sub-task.resolver.spec.ts index 24417b564..48bb0ae15 100644 --- a/examples/basic/e2e/sub-task.resolver.spec.ts +++ b/examples/basic/e2e/sub-task.resolver.spec.ts @@ -466,9 +466,7 @@ describe('SubTaskResolver (basic - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -615,9 +613,7 @@ describe('SubTaskResolver (basic - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/basic/e2e/todo-item.resolver.spec.ts b/examples/basic/e2e/todo-item.resolver.spec.ts index 5cfd57c01..a26ec8eb2 100644 --- a/examples/basic/e2e/todo-item.resolver.spec.ts +++ b/examples/basic/e2e/todo-item.resolver.spec.ts @@ -510,9 +510,7 @@ describe('TodoItemResolver (basic - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -661,9 +659,7 @@ describe('TodoItemResolver (basic - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/basic/src/tag/tag.entity.ts b/examples/basic/src/tag/tag.entity.ts index 21d452891..f163f5740 100644 --- a/examples/basic/src/tag/tag.entity.ts +++ b/examples/basic/src/tag/tag.entity.ts @@ -1,12 +1,4 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ObjectType, - ManyToMany -} from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ObjectType, ManyToMany } from 'typeorm'; import { TodoItemEntity } from '../todo-item/todo-item.entity'; @Entity({ name: 'tag' }) diff --git a/examples/complexity/e2e/sub-task.resolver.spec.ts b/examples/complexity/e2e/sub-task.resolver.spec.ts index 239bb9419..bd22139f3 100644 --- a/examples/complexity/e2e/sub-task.resolver.spec.ts +++ b/examples/complexity/e2e/sub-task.resolver.spec.ts @@ -502,9 +502,7 @@ describe('SubTaskResolver (complexity - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -651,9 +649,7 @@ describe('SubTaskResolver (complexity - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/complexity/e2e/todo-item.resolver.spec.ts b/examples/complexity/e2e/todo-item.resolver.spec.ts index a7f7c9c1e..cbc2717c0 100644 --- a/examples/complexity/e2e/todo-item.resolver.spec.ts +++ b/examples/complexity/e2e/todo-item.resolver.spec.ts @@ -489,9 +489,7 @@ describe('TodoItemResolver (complexity - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -640,9 +638,7 @@ describe('TodoItemResolver (complexity - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/complexity/src/tag/tag.entity.ts b/examples/complexity/src/tag/tag.entity.ts index 21d452891..f163f5740 100644 --- a/examples/complexity/src/tag/tag.entity.ts +++ b/examples/complexity/src/tag/tag.entity.ts @@ -1,12 +1,4 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ObjectType, - ManyToMany -} from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ObjectType, ManyToMany } from 'typeorm'; import { TodoItemEntity } from '../todo-item/todo-item.entity'; @Entity({ name: 'tag' }) diff --git a/examples/custom-id/e2e/sub-task.resolver.spec.ts b/examples/custom-id/e2e/sub-task.resolver.spec.ts index e4a1c44ad..84ef07d12 100644 --- a/examples/custom-id/e2e/sub-task.resolver.spec.ts +++ b/examples/custom-id/e2e/sub-task.resolver.spec.ts @@ -508,9 +508,7 @@ describe('SubTaskResolver (custom-id - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneSubTaskInput.id" of required type "CustomID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneSubTaskInput.id" of required type "CustomID!" was not provided.'); })); it('should validate an update', () => @@ -657,9 +655,7 @@ describe('SubTaskResolver (custom-id - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneSubTaskInput.id" of required type "CustomID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneSubTaskInput.id" of required type "CustomID!" was not provided.'); })); }); diff --git a/examples/custom-id/e2e/tag.resolver.spec.ts b/examples/custom-id/e2e/tag.resolver.spec.ts index e02641aef..2caf344e6 100644 --- a/examples/custom-id/e2e/tag.resolver.spec.ts +++ b/examples/custom-id/e2e/tag.resolver.spec.ts @@ -385,9 +385,7 @@ describe('TagResolver (custom-id - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTagInput.id" of required type "CustomID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTagInput.id" of required type "CustomID!" was not provided.'); })); it('should validate an update', () => @@ -529,9 +527,7 @@ describe('TagResolver (custom-id - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTagInput.id" of required type "CustomID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTagInput.id" of required type "CustomID!" was not provided.'); })); }); diff --git a/examples/custom-id/e2e/todo-item.resolver.spec.ts b/examples/custom-id/e2e/todo-item.resolver.spec.ts index 07318bdc9..bdf5c498e 100644 --- a/examples/custom-id/e2e/todo-item.resolver.spec.ts +++ b/examples/custom-id/e2e/todo-item.resolver.spec.ts @@ -510,9 +510,7 @@ describe('TodoItemResolver (custom-id - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "CustomID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "CustomID!" was not provided.'); })); it('should validate an update', () => @@ -661,9 +659,7 @@ describe('TodoItemResolver (custom-id - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "CustomID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "CustomID!" was not provided.'); })); }); diff --git a/examples/custom-id/src/tag/tag.entity.ts b/examples/custom-id/src/tag/tag.entity.ts index 21d452891..f163f5740 100644 --- a/examples/custom-id/src/tag/tag.entity.ts +++ b/examples/custom-id/src/tag/tag.entity.ts @@ -1,12 +1,4 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ObjectType, - ManyToMany -} from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ObjectType, ManyToMany } from 'typeorm'; import { TodoItemEntity } from '../todo-item/todo-item.entity'; @Entity({ name: 'tag' }) diff --git a/examples/custom-service/e2e/todo-item.resolver.spec.ts b/examples/custom-service/e2e/todo-item.resolver.spec.ts index b95b54b0a..773d5a94f 100644 --- a/examples/custom-service/e2e/todo-item.resolver.spec.ts +++ b/examples/custom-service/e2e/todo-item.resolver.spec.ts @@ -396,9 +396,7 @@ describe('TodoItemResolver (custom-service - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -547,9 +545,7 @@ describe('TodoItemResolver (custom-service - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/federation/sub-task-graphql/e2e/sub-task.resolver.spec.ts b/examples/federation/sub-task-graphql/e2e/sub-task.resolver.spec.ts index 80a3d2ee6..52fafe843 100644 --- a/examples/federation/sub-task-graphql/e2e/sub-task.resolver.spec.ts +++ b/examples/federation/sub-task-graphql/e2e/sub-task.resolver.spec.ts @@ -467,9 +467,7 @@ describe('Federated - SubTaskResolver (e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -616,9 +614,7 @@ describe('Federated - SubTaskResolver (e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/federation/todo-item-graphql/e2e/todo-item.resolver.spec.ts b/examples/federation/todo-item-graphql/e2e/todo-item.resolver.spec.ts index d06098f80..873742dfe 100644 --- a/examples/federation/todo-item-graphql/e2e/todo-item.resolver.spec.ts +++ b/examples/federation/todo-item-graphql/e2e/todo-item.resolver.spec.ts @@ -465,9 +465,7 @@ describe('Federated - TodoItemResolver (e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -616,9 +614,7 @@ describe('Federated - TodoItemResolver (e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/filters/e2e/todo-item.resolver.spec.ts b/examples/filters/e2e/todo-item.resolver.spec.ts index 5caf033fa..92367d2b7 100644 --- a/examples/filters/e2e/todo-item.resolver.spec.ts +++ b/examples/filters/e2e/todo-item.resolver.spec.ts @@ -78,9 +78,7 @@ describe('TodoItemResolver (filters - e2e)', () => { }); expect(edges).toHaveLength(1); - expect(edges.map((e) => e.node)).toEqual([ - { id: '1', title: 'Create Nest App', completed: true, description: null } - ]); + expect(edges.map((e) => e.node)).toEqual([{ id: '1', title: 'Create Nest App', completed: true, description: null }]); })); it(`should not accepted empty "completed" filter`, () => @@ -98,9 +96,7 @@ describe('TodoItemResolver (filters - e2e)', () => { }) .expect(200) .then(({ body }) => { - expect(body.errors[0].extensions.response.message[0]).toBe( - 'filter.There was no filter provided for "completed"!' - ); + expect(body.errors[0].extensions.response.message[0]).toBe('filter.There was no filter provided for "completed"!'); })); }); diff --git a/examples/hooks/e2e/sub-task.resolver.spec.ts b/examples/hooks/e2e/sub-task.resolver.spec.ts index 980b52c62..20437daee 100644 --- a/examples/hooks/e2e/sub-task.resolver.spec.ts +++ b/examples/hooks/e2e/sub-task.resolver.spec.ts @@ -497,9 +497,7 @@ describe('SubTaskResolver (hooks - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -646,9 +644,7 @@ describe('SubTaskResolver (hooks - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/hooks/e2e/todo-item.resolver.spec.ts b/examples/hooks/e2e/todo-item.resolver.spec.ts index d37479478..67e770554 100644 --- a/examples/hooks/e2e/todo-item.resolver.spec.ts +++ b/examples/hooks/e2e/todo-item.resolver.spec.ts @@ -712,9 +712,7 @@ describe('TodoItemResolver (hooks - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -954,9 +952,7 @@ describe('TodoItemResolver (hooks - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/hooks/src/tag/tag.entity.ts b/examples/hooks/src/tag/tag.entity.ts index 3f8b53bcc..1f4f3967f 100644 --- a/examples/hooks/src/tag/tag.entity.ts +++ b/examples/hooks/src/tag/tag.entity.ts @@ -1,12 +1,4 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ObjectType, - ManyToMany -} from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ObjectType, ManyToMany } from 'typeorm'; import { TodoItemEntity } from '../todo-item/todo-item.entity'; @Entity({ name: 'tag' }) diff --git a/examples/hooks/src/todo-item/todo-item.resolver.ts b/examples/hooks/src/todo-item/todo-item.resolver.ts index 7648dc08c..2474c80fe 100644 --- a/examples/hooks/src/todo-item/todo-item.resolver.ts +++ b/examples/hooks/src/todo-item/todo-item.resolver.ts @@ -17,9 +17,6 @@ export class TodoItemResolver { @UseGuards(AuthGuard) @UseInterceptors(HookInterceptor(HookTypes.BEFORE_UPDATE_MANY, TodoItemUpdateDTO)) markTodoItemsAsCompleted(@MutationHookArgs() { input }: MarkTodoItemsAsCompletedArgs): Promise { - return this.service.updateMany( - { ...input.update, completed: true }, - mergeFilter(input.filter, { completed: { is: false } }) - ); + return this.service.updateMany({ ...input.update, completed: true }, mergeFilter(input.filter, { completed: { is: false } })); } } diff --git a/examples/no-paging/e2e/sub-task.resolver.spec.ts b/examples/no-paging/e2e/sub-task.resolver.spec.ts index a42f64450..87a6ef866 100644 --- a/examples/no-paging/e2e/sub-task.resolver.spec.ts +++ b/examples/no-paging/e2e/sub-task.resolver.spec.ts @@ -390,9 +390,7 @@ describe('SubTaskResolver (noPaging - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -539,9 +537,7 @@ describe('SubTaskResolver (noPaging - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/no-paging/e2e/todo-item.resolver.spec.ts b/examples/no-paging/e2e/todo-item.resolver.spec.ts index b943b9857..829d324f0 100644 --- a/examples/no-paging/e2e/todo-item.resolver.spec.ts +++ b/examples/no-paging/e2e/todo-item.resolver.spec.ts @@ -360,9 +360,7 @@ describe('TodoItemResolver (noPaging - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -511,9 +509,7 @@ describe('TodoItemResolver (noPaging - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/no-paging/src/tag/tag.entity.ts b/examples/no-paging/src/tag/tag.entity.ts index 21d452891..f163f5740 100644 --- a/examples/no-paging/src/tag/tag.entity.ts +++ b/examples/no-paging/src/tag/tag.entity.ts @@ -1,12 +1,4 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ObjectType, - ManyToMany -} from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ObjectType, ManyToMany } from 'typeorm'; import { TodoItemEntity } from '../todo-item/todo-item.entity'; @Entity({ name: 'tag' }) diff --git a/examples/offset-paging/e2e/sub-task.resolver.spec.ts b/examples/offset-paging/e2e/sub-task.resolver.spec.ts index 930d8ac66..13137fb73 100644 --- a/examples/offset-paging/e2e/sub-task.resolver.spec.ts +++ b/examples/offset-paging/e2e/sub-task.resolver.spec.ts @@ -417,9 +417,7 @@ describe('SubTaskResolver (limitOffset - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -566,9 +564,7 @@ describe('SubTaskResolver (limitOffset - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/offset-paging/e2e/tag.resolver.spec.ts b/examples/offset-paging/e2e/tag.resolver.spec.ts index 32b04219f..f267218a0 100644 --- a/examples/offset-paging/e2e/tag.resolver.spec.ts +++ b/examples/offset-paging/e2e/tag.resolver.spec.ts @@ -569,8 +569,7 @@ describe('TagResolver (limitOffset - e2e)', () => { }) .expect(200) .then(({ body }) => { - const { id, todoItems }: { id: string; todoItems: OffsetConnectionType } = - body.data.addTodoItemsToTag; + const { id, todoItems }: { id: string; todoItems: OffsetConnectionType } = body.data.addTodoItemsToTag; expect(id).toBe('1'); expect(todoItems.nodes).toHaveLength(5); expect(todoItems.nodes.map((e) => e.title).sort()).toEqual([ diff --git a/examples/offset-paging/e2e/todo-item.resolver.spec.ts b/examples/offset-paging/e2e/todo-item.resolver.spec.ts index d09cac250..cec4b0dea 100644 --- a/examples/offset-paging/e2e/todo-item.resolver.spec.ts +++ b/examples/offset-paging/e2e/todo-item.resolver.spec.ts @@ -407,9 +407,7 @@ describe('TodoItemResolver (limitOffset - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -558,9 +556,7 @@ describe('TodoItemResolver (limitOffset - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.'); })); }); @@ -658,8 +654,7 @@ describe('TodoItemResolver (limitOffset - e2e)', () => { }) .expect(200) .then(({ body }) => { - const { id, subTasks }: { id: string; subTasks: OffsetConnectionType } = - body.data.addSubTasksToTodoItem; + const { id, subTasks }: { id: string; subTasks: OffsetConnectionType } = body.data.addSubTasksToTodoItem; expect(id).toBe('1'); expect(subTasks.nodes).toHaveLength(6); subTasks.nodes.forEach((e) => expect(e.todoItemId).toBe('1')); diff --git a/examples/offset-paging/src/tag/tag.entity.ts b/examples/offset-paging/src/tag/tag.entity.ts index 21d452891..f163f5740 100644 --- a/examples/offset-paging/src/tag/tag.entity.ts +++ b/examples/offset-paging/src/tag/tag.entity.ts @@ -1,12 +1,4 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ObjectType, - ManyToMany -} from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ObjectType, ManyToMany } from 'typeorm'; import { TodoItemEntity } from '../todo-item/todo-item.entity'; @Entity({ name: 'tag' }) diff --git a/examples/sequelize/e2e/sub-task.resolver.spec.ts b/examples/sequelize/e2e/sub-task.resolver.spec.ts index f24f7d853..021e86905 100644 --- a/examples/sequelize/e2e/sub-task.resolver.spec.ts +++ b/examples/sequelize/e2e/sub-task.resolver.spec.ts @@ -576,9 +576,7 @@ describe('SubTaskResolver (sequelize - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -725,9 +723,7 @@ describe('SubTaskResolver (sequelize - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/sequelize/e2e/tag.resolver.spec.ts b/examples/sequelize/e2e/tag.resolver.spec.ts index 5f13fb805..88918b714 100644 --- a/examples/sequelize/e2e/tag.resolver.spec.ts +++ b/examples/sequelize/e2e/tag.resolver.spec.ts @@ -756,8 +756,7 @@ describe('TagResolver (sequelize - e2e)', () => { }) .expect(200) .then(({ body }) => { - const { edges, pageInfo, totalCount }: CursorConnectionType = - body.data.addTodoItemsToTag.todoItems; + const { edges, pageInfo, totalCount }: CursorConnectionType = body.data.addTodoItemsToTag.todoItems; expect(body.data.addTodoItemsToTag.id).toBe('1'); expect(pageInfo).toEqual({ endCursor: 'YXJyYXljb25uZWN0aW9uOjQ=', @@ -802,8 +801,7 @@ describe('TagResolver (sequelize - e2e)', () => { }) .expect(200) .then(({ body }) => { - const { edges, pageInfo, totalCount }: CursorConnectionType = - body.data.removeTodoItemsFromTag.todoItems; + const { edges, pageInfo, totalCount }: CursorConnectionType = body.data.removeTodoItemsFromTag.todoItems; expect(body.data.removeTodoItemsFromTag.id).toBe('1'); expect(pageInfo).toEqual({ endCursor: 'YXJyYXljb25uZWN0aW9uOjE=', diff --git a/examples/sequelize/e2e/todo-item.resolver.spec.ts b/examples/sequelize/e2e/todo-item.resolver.spec.ts index b34bd3596..91a23251c 100644 --- a/examples/sequelize/e2e/todo-item.resolver.spec.ts +++ b/examples/sequelize/e2e/todo-item.resolver.spec.ts @@ -806,9 +806,7 @@ describe('TodoItemResolver (sequelize - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -1004,9 +1002,7 @@ describe('TodoItemResolver (sequelize - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.'); })); }); @@ -1130,8 +1126,7 @@ describe('TodoItemResolver (sequelize - e2e)', () => { }) .expect(200) .then(({ body }) => { - const { edges, pageInfo, totalCount }: CursorConnectionType = - body.data.addSubTasksToTodoItem.subTasks; + const { edges, pageInfo, totalCount }: CursorConnectionType = body.data.addSubTasksToTodoItem.subTasks; expect(body.data.addSubTasksToTodoItem.id).toBe('1'); expect(pageInfo).toEqual({ endCursor: 'YXJyYXljb25uZWN0aW9uOjU=', diff --git a/examples/sequelize/src/tag/tag.entity.ts b/examples/sequelize/src/tag/tag.entity.ts index a32520cd6..240f2dce9 100644 --- a/examples/sequelize/src/tag/tag.entity.ts +++ b/examples/sequelize/src/tag/tag.entity.ts @@ -1,13 +1,4 @@ -import { - Table, - Column, - UpdatedAt, - BelongsToMany, - CreatedAt, - Model, - PrimaryKey, - AutoIncrement -} from 'sequelize-typescript'; +import { Table, Column, UpdatedAt, BelongsToMany, CreatedAt, Model, PrimaryKey, AutoIncrement } from 'sequelize-typescript'; import { TodoItemEntityTags } from '../todo-item/entity/todo-item-tag.entity'; import { TodoItemEntity } from '../todo-item/entity/todo-item.entity'; diff --git a/examples/subscriptions/src/tag/tag.entity.ts b/examples/subscriptions/src/tag/tag.entity.ts index 21d452891..f163f5740 100644 --- a/examples/subscriptions/src/tag/tag.entity.ts +++ b/examples/subscriptions/src/tag/tag.entity.ts @@ -1,12 +1,4 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ObjectType, - ManyToMany -} from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ObjectType, ManyToMany } from 'typeorm'; import { TodoItemEntity } from '../todo-item/todo-item.entity'; @Entity({ name: 'tag' }) diff --git a/examples/typeorm-soft-delete/e2e/todo-item.resolver.spec.ts b/examples/typeorm-soft-delete/e2e/todo-item.resolver.spec.ts index 7d20d9cdb..27687480c 100644 --- a/examples/typeorm-soft-delete/e2e/todo-item.resolver.spec.ts +++ b/examples/typeorm-soft-delete/e2e/todo-item.resolver.spec.ts @@ -391,9 +391,7 @@ describe('SoftDelete - TodoItemResolver (e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -542,9 +540,7 @@ describe('SoftDelete - TodoItemResolver (e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/typeorm/e2e/sub-task.resolver.spec.ts b/examples/typeorm/e2e/sub-task.resolver.spec.ts index a3a2f7c41..a8d3f9c6e 100644 --- a/examples/typeorm/e2e/sub-task.resolver.spec.ts +++ b/examples/typeorm/e2e/sub-task.resolver.spec.ts @@ -576,9 +576,7 @@ describe('SubTaskResolver (typeorm - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneSubTaskInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -725,9 +723,7 @@ describe('SubTaskResolver (typeorm - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneSubTaskInput.id" of required type "ID!" was not provided.'); })); }); diff --git a/examples/typeorm/e2e/tag.resolver.spec.ts b/examples/typeorm/e2e/tag.resolver.spec.ts index c8419896c..590ff0854 100644 --- a/examples/typeorm/e2e/tag.resolver.spec.ts +++ b/examples/typeorm/e2e/tag.resolver.spec.ts @@ -885,8 +885,7 @@ describe('TagResolver (typeorm - e2e)', () => { }) .expect(200) .then(({ body }) => { - const { edges, pageInfo, totalCount }: CursorConnectionType = - body.data.addTodoItemsToTag.todoItems; + const { edges, pageInfo, totalCount }: CursorConnectionType = body.data.addTodoItemsToTag.todoItems; expect(body.data.addTodoItemsToTag.id).toBe('1'); expect(pageInfo).toEqual({ endCursor: 'YXJyYXljb25uZWN0aW9uOjQ=', @@ -931,8 +930,7 @@ describe('TagResolver (typeorm - e2e)', () => { }) .expect(200) .then(({ body }) => { - const { edges, pageInfo, totalCount }: CursorConnectionType = - body.data.removeTodoItemsFromTag.todoItems; + const { edges, pageInfo, totalCount }: CursorConnectionType = body.data.removeTodoItemsFromTag.todoItems; expect(body.data.removeTodoItemsFromTag.id).toBe('1'); expect(pageInfo).toEqual({ endCursor: 'YXJyYXljb25uZWN0aW9uOjE=', diff --git a/examples/typeorm/e2e/todo-item.resolver.spec.ts b/examples/typeorm/e2e/todo-item.resolver.spec.ts index 5bc12ad3c..fd1c89328 100644 --- a/examples/typeorm/e2e/todo-item.resolver.spec.ts +++ b/examples/typeorm/e2e/todo-item.resolver.spec.ts @@ -911,9 +911,7 @@ describe('TodoItemResolver (typeorm - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "UpdateOneTodoItemInput.id" of required type "ID!" was not provided.'); })); it('should validate an update', () => @@ -1153,9 +1151,7 @@ describe('TodoItemResolver (typeorm - e2e)', () => { .expect(400) .then(({ body }) => { expect(body.errors).toHaveLength(1); - expect(body.errors[0].message).toBe( - 'Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.' - ); + expect(body.errors[0].message).toBe('Field "DeleteOneTodoItemInput.id" of required type "ID!" was not provided.'); })); }); @@ -1279,8 +1275,7 @@ describe('TodoItemResolver (typeorm - e2e)', () => { }) .expect(200) .then(({ body }) => { - const { edges, pageInfo, totalCount }: CursorConnectionType = - body.data.addSubTasksToTodoItem.subTasks; + const { edges, pageInfo, totalCount }: CursorConnectionType = body.data.addSubTasksToTodoItem.subTasks; expect(body.data.addSubTasksToTodoItem.id).toBe('1'); expect(pageInfo).toEqual({ endCursor: 'YXJyYXljb25uZWN0aW9uOjU=', diff --git a/examples/typeorm/src/sub-task/dto/subtask-input.dto.ts b/examples/typeorm/src/sub-task/dto/subtask-input.dto.ts index 42586a2d6..963cced81 100644 --- a/examples/typeorm/src/sub-task/dto/subtask-input.dto.ts +++ b/examples/typeorm/src/sub-task/dto/subtask-input.dto.ts @@ -1,11 +1,6 @@ import { Field, InputType, ID } from '@nestjs/graphql'; import { IsOptional, IsString, IsBoolean, IsNotEmpty } from 'class-validator'; -import { - BeforeCreateMany, - BeforeCreateOne, - CreateManyInputType, - CreateOneInputType -} from '@ptc-org/nestjs-query-graphql'; +import { BeforeCreateMany, BeforeCreateOne, CreateManyInputType, CreateOneInputType } from '@ptc-org/nestjs-query-graphql'; import { GqlContext } from '../../auth.guard'; import { getUserName } from '../../helpers'; import { SubTaskDTO } from './sub-task.dto'; diff --git a/examples/typeorm/src/sub-task/dto/subtask-update.dto.ts b/examples/typeorm/src/sub-task/dto/subtask-update.dto.ts index d4479aa9e..e6546b546 100644 --- a/examples/typeorm/src/sub-task/dto/subtask-update.dto.ts +++ b/examples/typeorm/src/sub-task/dto/subtask-update.dto.ts @@ -1,11 +1,6 @@ import { Field, InputType } from '@nestjs/graphql'; import { IsOptional, IsBoolean, IsString, IsNotEmpty } from 'class-validator'; -import { - BeforeUpdateMany, - BeforeUpdateOne, - UpdateManyInputType, - UpdateOneInputType -} from '@ptc-org/nestjs-query-graphql'; +import { BeforeUpdateMany, BeforeUpdateOne, UpdateManyInputType, UpdateOneInputType } from '@ptc-org/nestjs-query-graphql'; import { GqlContext } from '../../auth.guard'; import { getUserName } from '../../helpers'; import { SubTaskDTO } from './sub-task.dto'; diff --git a/examples/typeorm/src/tag/tag.entity.ts b/examples/typeorm/src/tag/tag.entity.ts index 3f8b53bcc..1f4f3967f 100644 --- a/examples/typeorm/src/tag/tag.entity.ts +++ b/examples/typeorm/src/tag/tag.entity.ts @@ -1,12 +1,4 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ObjectType, - ManyToMany -} from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ObjectType, ManyToMany } from 'typeorm'; import { TodoItemEntity } from '../todo-item/todo-item.entity'; @Entity({ name: 'tag' }) diff --git a/examples/typeorm/src/todo-item/dto/todo-item-input.dto.ts b/examples/typeorm/src/todo-item/dto/todo-item-input.dto.ts index a66dca096..22242b1e7 100644 --- a/examples/typeorm/src/todo-item/dto/todo-item-input.dto.ts +++ b/examples/typeorm/src/todo-item/dto/todo-item-input.dto.ts @@ -1,11 +1,6 @@ import { IsString, MaxLength, IsBoolean } from 'class-validator'; import { Field, InputType } from '@nestjs/graphql'; -import { - BeforeCreateMany, - BeforeCreateOne, - CreateManyInputType, - CreateOneInputType -} from '@ptc-org/nestjs-query-graphql'; +import { BeforeCreateMany, BeforeCreateOne, CreateManyInputType, CreateOneInputType } from '@ptc-org/nestjs-query-graphql'; import { GqlContext } from '../../auth.guard'; import { getUserName } from '../../helpers'; import { TodoItemDTO } from './todo-item.dto'; diff --git a/examples/typeorm/src/todo-item/dto/todo-item-update.dto.ts b/examples/typeorm/src/todo-item/dto/todo-item-update.dto.ts index 0cafdcda6..8a2f0d381 100644 --- a/examples/typeorm/src/todo-item/dto/todo-item-update.dto.ts +++ b/examples/typeorm/src/todo-item/dto/todo-item-update.dto.ts @@ -1,11 +1,6 @@ import { Field, InputType } from '@nestjs/graphql'; import { IsBoolean, IsNumber, IsOptional, IsString, MaxLength } from 'class-validator'; -import { - BeforeUpdateMany, - BeforeUpdateOne, - UpdateManyInputType, - UpdateOneInputType -} from '@ptc-org/nestjs-query-graphql'; +import { BeforeUpdateMany, BeforeUpdateOne, UpdateManyInputType, UpdateOneInputType } from '@ptc-org/nestjs-query-graphql'; import { GqlContext } from '../../auth.guard'; import { getUserName } from '../../helpers'; import { TodoItemDTO } from './todo-item.dto'; diff --git a/packages/core/__tests__/assemblers/class-transformer.assembler.spec.ts b/packages/core/__tests__/assemblers/class-transformer.assembler.spec.ts index 272cd2257..2317e3777 100644 --- a/packages/core/__tests__/assemblers/class-transformer.assembler.spec.ts +++ b/packages/core/__tests__/assemblers/class-transformer.assembler.spec.ts @@ -1,11 +1,6 @@ // eslint-disable-next-line max-classes-per-file import * as classTransformer from 'class-transformer'; -import { - Assembler, - AssemblerDeserializer, - AssemblerSerializer, - ClassTransformerAssembler -} from '@ptc-org/nestjs-query-core'; +import { Assembler, AssemblerDeserializer, AssemblerSerializer, ClassTransformerAssembler } from '@ptc-org/nestjs-query-core'; describe('ClassTransformerAssembler', () => { const plainToClassSpy = jest.spyOn(classTransformer, 'plainToClass'); diff --git a/packages/core/__tests__/decorators/assembler.decorator.spec.ts b/packages/core/__tests__/decorators/assembler.decorator.spec.ts index 0c2e199bf..2ae35f170 100644 --- a/packages/core/__tests__/decorators/assembler.decorator.spec.ts +++ b/packages/core/__tests__/decorators/assembler.decorator.spec.ts @@ -30,8 +30,6 @@ describe('@Assembler', () => { return dtoOrEntity; } } - expect(() => Assembler(TestFrom, TestTo)(TestAssembler)).toThrow( - 'Assembler already registered for TestFrom TestTo' - ); + expect(() => Assembler(TestFrom, TestTo)(TestAssembler)).toThrow('Assembler already registered for TestFrom TestTo'); }); }); diff --git a/packages/core/__tests__/decorators/inject-assembler-query-service.decorator.spec.ts b/packages/core/__tests__/decorators/inject-assembler-query-service.decorator.spec.ts index 83a7eb5c0..7609bb4ff 100644 --- a/packages/core/__tests__/decorators/inject-assembler-query-service.decorator.spec.ts +++ b/packages/core/__tests__/decorators/inject-assembler-query-service.decorator.spec.ts @@ -1,11 +1,6 @@ import { Test } from '@nestjs/testing'; import { Injectable } from '@nestjs/common'; -import { - QueryService, - InjectAssemblerQueryService, - DefaultAssembler, - NoOpQueryService -} from '@ptc-org/nestjs-query-core'; +import { QueryService, InjectAssemblerQueryService, DefaultAssembler, NoOpQueryService } from '@ptc-org/nestjs-query-core'; import { getAssemblerQueryServiceToken } from '../../src/decorators/helpers'; describe('@InjectAssemblerQueryService', () => { diff --git a/packages/core/__tests__/helpers.spec.ts b/packages/core/__tests__/helpers.spec.ts index a007fe8c8..8cfcdd98f 100644 --- a/packages/core/__tests__/helpers.spec.ts +++ b/packages/core/__tests__/helpers.spec.ts @@ -556,18 +556,14 @@ describe('applyFilter', () => { }); it('should handle between comparisons', () => { - expect(applyFilter(singleNestedNull(), { child: { first: { between: { lower: 'foo', upper: 'bar' } } } })).toBe( + expect(applyFilter(singleNestedNull(), { child: { first: { between: { lower: 'foo', upper: 'bar' } } } })).toBe(false); + expect(applyFilter(doubleNestedNull(), { child: { child: { first: { between: { lower: 'foo', upper: 'bar' } } } } })).toBe( false ); - expect( - applyFilter(doubleNestedNull(), { child: { child: { first: { between: { lower: 'foo', upper: 'bar' } } } } }) - ).toBe(false); }); it('should handle notBetween comparisons', () => { - expect( - applyFilter(singleNestedNull(), { child: { first: { notBetween: { lower: 'foo', upper: 'bar' } } } }) - ).toBe(true); + expect(applyFilter(singleNestedNull(), { child: { first: { notBetween: { lower: 'foo', upper: 'bar' } } } })).toBe(true); expect( applyFilter(doubleNestedNull(), { child: { child: { first: { notBetween: { lower: 'foo', upper: 'bar' } } } } @@ -917,62 +913,20 @@ describe('applySort', () => { { description: 'sort dates with nulls asc', sortFields: [{ field: 'created', direction: SortDirection.ASC }], - input: [ - { created: date(4) }, - { created: date(2) }, - { created: date(3) }, - { created: date(1) }, - { created: null }, - {} - ], - expected: [ - { created: date(1) }, - { created: date(2) }, - { created: date(3) }, - { created: date(4) }, - { created: null }, - {} - ] + input: [{ created: date(4) }, { created: date(2) }, { created: date(3) }, { created: date(1) }, { created: null }, {}], + expected: [{ created: date(1) }, { created: date(2) }, { created: date(3) }, { created: date(4) }, { created: null }, {}] }, { description: 'sort dates with nulls first asc', sortFields: [{ field: 'created', direction: SortDirection.ASC, nulls: SortNulls.NULLS_FIRST }], - input: [ - { created: date(4) }, - { created: date(2) }, - { created: date(3) }, - { created: date(1) }, - { created: null }, - {} - ], - expected: [ - {}, - { created: null }, - { created: date(1) }, - { created: date(2) }, - { created: date(3) }, - { created: date(4) } - ] + input: [{ created: date(4) }, { created: date(2) }, { created: date(3) }, { created: date(1) }, { created: null }, {}], + expected: [{}, { created: null }, { created: date(1) }, { created: date(2) }, { created: date(3) }, { created: date(4) }] }, { description: 'sort dates with nulls last asc', sortFields: [{ field: 'created', direction: SortDirection.ASC, nulls: SortNulls.NULLS_LAST }], - input: [ - { created: date(4) }, - { created: date(2) }, - { created: date(3) }, - { created: date(1) }, - { created: null }, - {} - ], - expected: [ - { created: date(1) }, - { created: date(2) }, - { created: date(3) }, - { created: date(4) }, - { created: null }, - {} - ] + input: [{ created: date(4) }, { created: date(2) }, { created: date(3) }, { created: date(1) }, { created: null }, {}], + expected: [{ created: date(1) }, { created: date(2) }, { created: date(3) }, { created: date(4) }, { created: null }, {}] } ]; testCases.forEach(({ description, input, expected, sortFields }) => { @@ -1107,62 +1061,20 @@ describe('applySort', () => { { description: 'sort dates with nulls desc', sortFields: [{ field: 'created', direction: SortDirection.DESC }], - input: [ - { created: date(4) }, - { created: date(2) }, - { created: date(3) }, - { created: date(1) }, - { created: null }, - {} - ], - expected: [ - {}, - { created: null }, - { created: date(4) }, - { created: date(3) }, - { created: date(2) }, - { created: date(1) } - ] + input: [{ created: date(4) }, { created: date(2) }, { created: date(3) }, { created: date(1) }, { created: null }, {}], + expected: [{}, { created: null }, { created: date(4) }, { created: date(3) }, { created: date(2) }, { created: date(1) }] }, { description: 'sort dates with nulls first desc', sortFields: [{ field: 'created', direction: SortDirection.DESC, nulls: SortNulls.NULLS_FIRST }], - input: [ - { created: date(4) }, - { created: date(2) }, - { created: date(3) }, - { created: date(1) }, - { created: null }, - {} - ], - expected: [ - {}, - { created: null }, - { created: date(4) }, - { created: date(3) }, - { created: date(2) }, - { created: date(1) } - ] + input: [{ created: date(4) }, { created: date(2) }, { created: date(3) }, { created: date(1) }, { created: null }, {}], + expected: [{}, { created: null }, { created: date(4) }, { created: date(3) }, { created: date(2) }, { created: date(1) }] }, { description: 'sort dates with nulls last desc', sortFields: [{ field: 'created', direction: SortDirection.DESC, nulls: SortNulls.NULLS_LAST }], - input: [ - { created: date(4) }, - { created: date(2) }, - { created: date(3) }, - { created: date(1) }, - { created: null }, - {} - ], - expected: [ - { created: date(4) }, - { created: date(3) }, - { created: date(2) }, - { created: date(1) }, - { created: null }, - {} - ] + input: [{ created: date(4) }, { created: date(2) }, { created: date(3) }, { created: date(1) }, { created: null }, {}], + expected: [{ created: date(4) }, { created: date(3) }, { created: date(2) }, { created: date(1) }, { created: null }, {}] } ]; testCases.forEach(({ description, input, expected, sortFields }) => { diff --git a/packages/core/__tests__/services/assembler-query.service.spec.ts b/packages/core/__tests__/services/assembler-query.service.spec.ts index 4308dec99..b3f9cff4c 100644 --- a/packages/core/__tests__/services/assembler-query.service.spec.ts +++ b/packages/core/__tests__/services/assembler-query.service.spec.ts @@ -249,9 +249,7 @@ describe('AssemblerQueryService', () => { ) ).thenResolve(1); - return expect( - assemblerService.countRelations(TestDTO, 'test', { foo: 'bar' }, { foo: { eq: 'bar' } }) - ).resolves.toBe(1); + return expect(assemblerService.countRelations(TestDTO, 'test', { foo: 'bar' }, { foo: { eq: 'bar' } })).resolves.toBe(1); }); it('should transform multiple entities', () => { @@ -261,12 +259,10 @@ describe('AssemblerQueryService', () => { const entity: TestEntity = { bar: 'bar' }; when( mockQueryService.countRelations(TestDTO, 'test', deepEqual([entity]), objectContaining({ foo: { eq: 'bar' } })) - ).thenCall((relationClass, relation, entities) => - Promise.resolve(new Map([[entities[0], 1]])) + ).thenCall((relationClass, relation, entities) => Promise.resolve(new Map([[entities[0], 1]]))); + return expect(assemblerService.countRelations(TestDTO, 'test', [{ foo: 'bar' }], { foo: { eq: 'bar' } })).resolves.toEqual( + new Map([[dto, 1]]) ); - return expect( - assemblerService.countRelations(TestDTO, 'test', [{ foo: 'bar' }], { foo: { eq: 'bar' } }) - ).resolves.toEqual(new Map([[dto, 1]])); }); }); @@ -290,9 +286,7 @@ describe('AssemblerQueryService', () => { when(mockQueryService.findRelation(TestDTO, 'test', deepEqual([entity]), undefined)).thenCall( (relationClass, relation, entities) => Promise.resolve(new Map([[entities[0], result]])) ); - return expect(assemblerService.findRelation(TestDTO, 'test', [{ foo: 'bar' }])).resolves.toEqual( - new Map([[dto, result]]) - ); + return expect(assemblerService.findRelation(TestDTO, 'test', [{ foo: 'bar' }])).resolves.toEqual(new Map([[dto, result]])); }); }); @@ -311,12 +305,7 @@ describe('AssemblerQueryService', () => { const mockQueryService = mock>(); const assemblerService = new AssemblerQueryService(new TestAssembler(), instance(mockQueryService)); when( - mockQueryService.addRelations( - 'test', - 1, - deepEqual([2, 3, 4]), - objectContaining({ filter: { bar: { eq: 'bar' } } }) - ) + mockQueryService.addRelations('test', 1, deepEqual([2, 3, 4]), objectContaining({ filter: { bar: { eq: 'bar' } } })) ).thenResolve({ bar: 'baz' }); @@ -339,9 +328,7 @@ describe('AssemblerQueryService', () => { it('should transform the options and results for a single entity', () => { const mockQueryService = mock>(); const assemblerService = new AssemblerQueryService(new TestAssembler(), instance(mockQueryService)); - when( - mockQueryService.setRelation('test', 1, 2, objectContaining({ filter: { bar: { eq: 'bar' } } })) - ).thenResolve({ + when(mockQueryService.setRelation('test', 1, 2, objectContaining({ filter: { bar: { eq: 'bar' } } }))).thenResolve({ bar: 'baz' }); @@ -395,12 +382,7 @@ describe('AssemblerQueryService', () => { const mockQueryService = mock>(); const assemblerService = new AssemblerQueryService(new TestAssembler(), instance(mockQueryService)); when( - mockQueryService.removeRelations( - 'test', - 1, - deepEqual([2, 3, 4]), - objectContaining({ filter: { bar: { eq: 'bar' } } }) - ) + mockQueryService.removeRelations('test', 1, deepEqual([2, 3, 4]), objectContaining({ filter: { bar: { eq: 'bar' } } })) ).thenResolve({ bar: 'baz' }); @@ -425,9 +407,7 @@ describe('AssemblerQueryService', () => { it('should transform the options and results for a single entity', () => { const mockQueryService = mock>(); const assemblerService = new AssemblerQueryService(new TestAssembler(), instance(mockQueryService)); - when( - mockQueryService.removeRelation('test', 1, 2, objectContaining({ filter: { bar: { eq: 'bar' } } })) - ).thenResolve({ + when(mockQueryService.removeRelation('test', 1, 2, objectContaining({ filter: { bar: { eq: 'bar' } } }))).thenResolve({ bar: 'baz' }); @@ -496,20 +476,14 @@ describe('AssemblerQueryService', () => { const mockQueryService = mock>(); const assemblerService = new AssemblerQueryService(new TestAssembler(), instance(mockQueryService)); when( - mockQueryService.updateOne( - 1, - objectContaining({ bar: 'baz' }), - objectContaining({ filter: { bar: { eq: 'bar' } } }) - ) + mockQueryService.updateOne(1, objectContaining({ bar: 'baz' }), objectContaining({ filter: { bar: { eq: 'bar' } } })) ).thenResolve({ bar: 'baz' }); - return expect(assemblerService.updateOne(1, { foo: 'baz' }, { filter: { foo: { eq: 'bar' } } })).resolves.toEqual( - { - foo: 'baz' - } - ); + return expect(assemblerService.updateOne(1, { foo: 'baz' }, { filter: { foo: { eq: 'bar' } } })).resolves.toEqual({ + foo: 'baz' + }); }); }); @@ -517,9 +491,9 @@ describe('AssemblerQueryService', () => { it('should transform the arguments', () => { const mockQueryService = mock>(); const assemblerService = new AssemblerQueryService(new TestAssembler(), instance(mockQueryService)); - when( - mockQueryService.updateMany(objectContaining({ bar: 'baz' }), objectContaining({ bar: { eq: 'bar' } })) - ).thenResolve({ updatedCount: 1 }); + when(mockQueryService.updateMany(objectContaining({ bar: 'baz' }), objectContaining({ bar: { eq: 'bar' } }))).thenResolve({ + updatedCount: 1 + }); return expect(assemblerService.updateMany({ foo: 'baz' }, { foo: { eq: 'bar' } })).resolves.toEqual({ updatedCount: 1 diff --git a/packages/core/__tests__/services/noop-query.service.spec.ts b/packages/core/__tests__/services/noop-query.service.spec.ts index 7dede452a..9909ca1bb 100644 --- a/packages/core/__tests__/services/noop-query.service.spec.ts +++ b/packages/core/__tests__/services/noop-query.service.spec.ts @@ -7,11 +7,7 @@ describe('NoOpQueryService', () => { foo!: string; } - const instance: QueryService = NoOpQueryService.getInstance< - TestType, - DeepPartial, - DeepPartial - >(); + const instance: QueryService = NoOpQueryService.getInstance, DeepPartial>(); it('should throw a NotImplementedException when calling addRelations', () => expect(instance.addRelations('test', 1, [1, 2, 3])).rejects.toThrow('addRelations is not implemented')); @@ -47,14 +43,10 @@ describe('NoOpQueryService', () => { expect(instance.count({})).rejects.toThrow('count is not implemented')); it('should throw a NotImplementedException when calling queryRelations', () => - expect(instance.queryRelations(TestType, 'test', new TestType(), {})).rejects.toThrow( - 'queryRelations is not implemented' - )); + expect(instance.queryRelations(TestType, 'test', new TestType(), {})).rejects.toThrow('queryRelations is not implemented')); it('should throw a NotImplementedException when calling countRelations', () => - expect(instance.countRelations(TestType, 'test', new TestType(), {})).rejects.toThrow( - 'countRelations is not implemented' - )); + expect(instance.countRelations(TestType, 'test', new TestType(), {})).rejects.toThrow('countRelations is not implemented')); it('should throw a NotImplementedException when calling removeRelation', () => expect(instance.removeRelation('test', 1, 2)).rejects.toThrow('removeRelation is not implemented')); diff --git a/packages/core/__tests__/services/proxy-query.service.spec.ts b/packages/core/__tests__/services/proxy-query.service.spec.ts index 6378442c3..93391cebb 100644 --- a/packages/core/__tests__/services/proxy-query.service.spec.ts +++ b/packages/core/__tests__/services/proxy-query.service.spec.ts @@ -122,9 +122,7 @@ describe('ProxyQueryService', () => { const aggQuery: AggregateQuery = { count: ['foo'] }; const result = new Map([[{ foo: 'bar' }, [{ count: { foo: 1 } }]]]); when(mockQueryService.aggregateRelations(TestType, relationName, dtos, filter, aggQuery)).thenResolve(result); - return expect(queryService.aggregateRelations(TestType, relationName, dtos, filter, aggQuery)).resolves.toBe( - result - ); + return expect(queryService.aggregateRelations(TestType, relationName, dtos, filter, aggQuery)).resolves.toBe(result); }); it('should proxy to the underlying service when calling countRelations with one dto', () => { diff --git a/packages/core/__tests__/services/relation-query.service.spec.ts b/packages/core/__tests__/services/relation-query.service.spec.ts index 5376d4852..c3558cedf 100644 --- a/packages/core/__tests__/services/relation-query.service.spec.ts +++ b/packages/core/__tests__/services/relation-query.service.spec.ts @@ -134,9 +134,9 @@ describe('RelationQueryService', () => { const relationAggregateQuery: AggregateQuery = { count: ['foo'] }; testRelationFn.mockReturnValue({ filter: relationFilter }); when(mockRelationService.aggregate(deepEqual(relationFilter), relationAggregateQuery)).thenResolve(result); - await expect( - queryService.aggregateRelations(TestType, relationName, dto, filter, relationAggregateQuery) - ).resolves.toBe(result); + await expect(queryService.aggregateRelations(TestType, relationName, dto, filter, relationAggregateQuery)).resolves.toBe( + result + ); return expect(testRelationFn).toHaveBeenCalledWith(dto); }); @@ -149,9 +149,7 @@ describe('RelationQueryService', () => { const relationFilter = {}; const relationAggregateQuery: AggregateQuery = { count: ['foo'] }; testRelationFn.mockReturnValue({ filter: relationFilter }); - when(mockRelationService.aggregate(deepEqual(relationFilter), relationAggregateQuery)).thenResolve( - relationResults - ); + when(mockRelationService.aggregate(deepEqual(relationFilter), relationAggregateQuery)).thenResolve(relationResults); return expect( queryService.aggregateRelations(TestType, relationName, dtos, filter, relationAggregateQuery) ).resolves.toEqual(result); @@ -163,12 +161,8 @@ describe('RelationQueryService', () => { const filter = {}; const aggregateQuery: AggregateQuery = { count: ['foo'] }; const result = [{ count: { foo: 1 } }]; - when(mockQueryService.aggregateRelations(TestType, relationName, dto, filter, aggregateQuery)).thenResolve( - result - ); - return expect(queryService.aggregateRelations(TestType, relationName, dto, filter, aggregateQuery)).resolves.toBe( - result - ); + when(mockQueryService.aggregateRelations(TestType, relationName, dto, filter, aggregateQuery)).thenResolve(result); + return expect(queryService.aggregateRelations(TestType, relationName, dto, filter, aggregateQuery)).resolves.toBe(result); }); it('should proxy to the underlying service when calling queryRelations with many dtos and a unknown relation', () => { @@ -177,12 +171,8 @@ describe('RelationQueryService', () => { const filter = {}; const aggregateQuery: AggregateQuery = { count: ['foo'] }; const result = new Map([[dtos[0], [{ count: { foo: 1 } }]]]); - when(mockQueryService.aggregateRelations(TestType, relationName, dtos, filter, aggregateQuery)).thenResolve( - result - ); - return expect( - queryService.aggregateRelations(TestType, relationName, dtos, filter, aggregateQuery) - ).resolves.toBe(result); + when(mockQueryService.aggregateRelations(TestType, relationName, dtos, filter, aggregateQuery)).thenResolve(result); + return expect(queryService.aggregateRelations(TestType, relationName, dtos, filter, aggregateQuery)).resolves.toBe(result); }); }); diff --git a/packages/core/src/common/reflect.utils.ts b/packages/core/src/common/reflect.utils.ts index f4745d8a4..d07985a1c 100644 --- a/packages/core/src/common/reflect.utils.ts +++ b/packages/core/src/common/reflect.utils.ts @@ -13,11 +13,7 @@ export const classMetadataDecorator = Reflect.defineMetadata(key, data, target); }; -export function getClassMetadata( - DTOClass: Class, - key: string, - includeParents: boolean -): MetaValue { +export function getClassMetadata(DTOClass: Class, key: string, includeParents: boolean): MetaValue { if (includeParents) { return Reflect.getMetadata(key, DTOClass) as MetaValue; } @@ -86,11 +82,7 @@ export class MapReflector extends Reflector { get(DTOClass: Class, includeParents?: boolean): MetaValue>; get(DTOClass: Class, key: K, includeParents?: boolean): MetaValue; - get( - DTOClass: Class, - key: K | boolean | undefined, - includeParents?: boolean - ): MetaValue> { + get(DTOClass: Class, key: K | boolean | undefined, includeParents?: boolean): MetaValue> { if (typeof key === 'boolean' || typeof key === 'undefined') { return this.getMetadata>(DTOClass, includeParents ?? false); } diff --git a/packages/core/src/decorators/inject-assembler-query-service.decorator.ts b/packages/core/src/decorators/inject-assembler-query-service.decorator.ts index 9f9c22f08..a97ac8dc4 100644 --- a/packages/core/src/decorators/inject-assembler-query-service.decorator.ts +++ b/packages/core/src/decorators/inject-assembler-query-service.decorator.ts @@ -3,13 +3,6 @@ import { Assembler } from '../assemblers'; import { Class, DeepPartial } from '../common'; import { getAssemblerQueryServiceToken } from './helpers'; -export const InjectAssemblerQueryService = < - DTO, - Entity, - C = DeepPartial, - CE = DeepPartial, - U = C, - UE = CE ->( +export const InjectAssemblerQueryService = , CE = DeepPartial, U = C, UE = CE>( AssemblerClass: Class> ): ReturnType => Inject(getAssemblerQueryServiceToken(AssemblerClass)); diff --git a/packages/core/src/helpers/comparison.builder.ts b/packages/core/src/helpers/comparison.builder.ts index 8b2e2e474..efcb37cc2 100644 --- a/packages/core/src/helpers/comparison.builder.ts +++ b/packages/core/src/helpers/comparison.builder.ts @@ -62,11 +62,7 @@ export class ComparisonBuilder { return (dto?: DTO): boolean => (dto ? dto[field] : null) == val; } - private static rangeComparison( - cmp: RangeComparisonOperators, - field: F, - val: DTO[F] - ): FilterFn { + private static rangeComparison(cmp: RangeComparisonOperators, field: F, val: DTO[F]): FilterFn { if (cmp === 'gt') { return compare((dto) => dto[field] > val, false); } @@ -79,11 +75,7 @@ export class ComparisonBuilder { return compare((dto) => dto[field] <= val, false); } - private static likeComparison( - cmp: LikeComparisonOperators, - field: F, - val: string - ): FilterFn { + private static likeComparison(cmp: LikeComparisonOperators, field: F, val: string): FilterFn { if (cmp === 'like') { const likeRegexp = this.likeSearchToRegexp(val); return compare((dto) => likeRegexp.test(dto[field] as unknown as string), false); @@ -100,11 +92,7 @@ export class ComparisonBuilder { return compare((dto) => !likeRegexp.test(dto[field] as unknown as string), true); } - private static inComparison( - cmp: InComparisonOperators, - field: F, - val: DTO[F][] - ): FilterFn { + private static inComparison(cmp: InComparisonOperators, field: F, val: DTO[F][]): FilterFn { if (cmp === 'notIn') { return compare((dto) => !val.includes(dto[field]), true); } diff --git a/packages/core/src/helpers/filter.builder.ts b/packages/core/src/helpers/filter.builder.ts index 88622c6f1..be0cb3375 100644 --- a/packages/core/src/helpers/filter.builder.ts +++ b/packages/core/src/helpers/filter.builder.ts @@ -37,10 +37,7 @@ export class FilterBuilder { ); } - private static withFilterComparison( - field: T, - cmp: FilterFieldComparison - ): FilterFn { + private static withFilterComparison(field: T, cmp: FilterFieldComparison): FilterFn { const operators = Object.keys(cmp) as (keyof FilterFieldComparison)[]; return this.orFilterFn( ...operators.map((operator) => ComparisonBuilder.build(field, operator, cmp[operator] as ComparisonField)) diff --git a/packages/core/src/interfaces/filter-field-comparison.interface.ts b/packages/core/src/interfaces/filter-field-comparison.interface.ts index fcb46ad89..2a2daf153 100644 --- a/packages/core/src/interfaces/filter-field-comparison.interface.ts +++ b/packages/core/src/interfaces/filter-field-comparison.interface.ts @@ -181,19 +181,7 @@ export interface StringFieldComparisons extends CommonFieldComparisonType( - AssemblerClass: Class> -): Provider { +function createServiceProvider(AssemblerClass: Class>): Provider { const classes = getAssemblerClasses(AssemblerClass); if (!classes) { throw new Error( @@ -24,6 +22,5 @@ function createServiceProvider( }; } -export const createServices = ( - opts: Class>[] -): Provider[] => opts.map((opt) => createServiceProvider(opt)); +export const createServices = (opts: Class>[]): Provider[] => + opts.map((opt) => createServiceProvider(opt)); diff --git a/packages/core/src/services/assembler-query.service.ts b/packages/core/src/services/assembler-query.service.ts index b00b725ab..6f73d211a 100644 --- a/packages/core/src/services/assembler-query.service.ts +++ b/packages/core/src/services/assembler-query.service.ts @@ -20,10 +20,7 @@ import { QueryService } from './query.service'; export class AssemblerQueryService, CE = DeepPartial, U = C, UE = CE> implements QueryService { - constructor( - readonly assembler: Assembler, - readonly queryService: QueryService - ) {} + constructor(readonly assembler: Assembler, readonly queryService: QueryService) {} addRelations( relationName: string, @@ -292,13 +289,7 @@ export class AssemblerQueryService, CE = DeepP ): Promise[] | Map[]>> { if (Array.isArray(dto)) { const entities = this.assembler.convertToEntities(dto); - const relationMap = await this.queryService.aggregateRelations( - RelationClass, - relationName, - entities, - filter, - aggregate - ); + const relationMap = await this.queryService.aggregateRelations(RelationClass, relationName, entities, filter, aggregate); return entities.reduce((map, e, index) => { const entry = relationMap.get(e) ?? []; map.set(dto[index], entry); diff --git a/packages/core/src/services/relation-query.service.ts b/packages/core/src/services/relation-query.service.ts index 8c1ad3389..3d3069346 100644 --- a/packages/core/src/services/relation-query.service.ts +++ b/packages/core/src/services/relation-query.service.ts @@ -10,11 +10,7 @@ export type QueryServiceRelation = { query: (dto: DTO) => Query; }; -export class RelationQueryService, U = DeepPartial> extends ProxyQueryService< - DTO, - C, - U -> { +export class RelationQueryService, U = DeepPartial> extends ProxyQueryService { readonly relations: Record>; constructor(queryService: QueryService, relations: Record>); diff --git a/packages/query-graphql/__tests__/auth/default-crud-auth.service.spec.ts b/packages/query-graphql/__tests__/auth/default-crud-auth.service.spec.ts index 30187fafd..15d186d46 100644 --- a/packages/query-graphql/__tests__/auth/default-crud-auth.service.spec.ts +++ b/packages/query-graphql/__tests__/auth/default-crud-auth.service.spec.ts @@ -47,9 +47,7 @@ describe('createDefaultAuthorizer', () => { @Authorize({ authorize: (ctx: UserContext, authorizationContext?: AuthorizationContext) => - authorizationContext?.operationName === 'other' - ? { ownerId: { neq: ctx.user.id } } - : { ownerId: { eq: ctx.user.id } } + authorizationContext?.operationName === 'other' ? { ownerId: { neq: ctx.user.id } } : { ownerId: { eq: ctx.user.id } } }) @Relation('relations', () => TestRelation, { auth: { @@ -282,9 +280,7 @@ describe('createDefaultAuthorizer', () => { ); jest.spyOn(customAuthorizer, 'authorizeRelation'); expect(customAuthorizer).toBeDefined(); - const relationAuthorizer = testingModule.get>( - getAuthorizerToken(RelationWithAuthorizer) - ); + const relationAuthorizer = testingModule.get>(getAuthorizerToken(RelationWithAuthorizer)); jest.spyOn(relationAuthorizer, 'authorize'); const customRelationAuthorizer = testingModule.get>( getCustomAuthorizerToken(RelationWithAuthorizer) diff --git a/packages/query-graphql/__tests__/decorators/relation.decorator.spec.ts b/packages/query-graphql/__tests__/decorators/relation.decorator.spec.ts index 0c9a247ea..37e22ed23 100644 --- a/packages/query-graphql/__tests__/decorators/relation.decorator.spec.ts +++ b/packages/query-graphql/__tests__/decorators/relation.decorator.spec.ts @@ -8,12 +8,7 @@ import { OffsetConnection, FilterableRelation } from '@ptc-org/nestjs-query-graphql'; -import { - CursorConnection, - FilterableCursorConnection, - FilterableOffsetConnection, - getRelations -} from '../../src/decorators'; +import { CursorConnection, FilterableCursorConnection, FilterableOffsetConnection, getRelations } from '../../src/decorators'; @ObjectType() class TestRelation {} diff --git a/packages/query-graphql/__tests__/loaders/aggregate-relations.loader.spec.ts b/packages/query-graphql/__tests__/loaders/aggregate-relations.loader.spec.ts index f9e5699de..6aa510380 100644 --- a/packages/query-graphql/__tests__/loaders/aggregate-relations.loader.spec.ts +++ b/packages/query-graphql/__tests__/loaders/aggregate-relations.loader.spec.ts @@ -20,9 +20,7 @@ describe('AggregateRelationsLoader', () => { it('should try to load the relations with the query args', () => { const service = mock>(); - const aggregateRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader( - instance(service) - ); + const aggregateRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader(instance(service)); const filter = {}; const aggregate: AggregateQuery = { count: ['id'] }; const dtos = [{ id: 'dto-1' }, { id: 'dto-2' }]; @@ -46,9 +44,7 @@ describe('AggregateRelationsLoader', () => { it('should try return an empty aggregate result for each dto if no results are found', () => { const service = mock>(); - const aggregateRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader( - instance(service) - ); + const aggregateRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader(instance(service)); const filter = {}; const aggregate: AggregateQuery = { count: ['id'] }; const dtos = [{ id: 'dto-1' }, { id: 'dto-2' }]; @@ -66,9 +62,7 @@ describe('AggregateRelationsLoader', () => { it('should group queryRelations calls by filter and return in the correct order', () => { const service = mock>(); - const queryRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader( - instance(service) - ); + const queryRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader(instance(service)); const filter1 = { id: { gt: 'a' } }; const filter2 = {}; const aggregate: AggregateQuery = { count: ['id'] }; @@ -117,9 +111,7 @@ describe('AggregateRelationsLoader', () => { it('should group queryRelations calls by aggregate and return in the correct order', () => { const service = mock>(); - const queryRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader( - instance(service) - ); + const queryRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader(instance(service)); const filter = {}; const aggregate1: AggregateQuery = { count: ['id'] }; const aggregate2: AggregateQuery = { sum: ['id'] }; diff --git a/packages/query-graphql/__tests__/loaders/count-relations.loader.spec.ts b/packages/query-graphql/__tests__/loaders/count-relations.loader.spec.ts index e1945051e..6a7571a7c 100644 --- a/packages/query-graphql/__tests__/loaders/count-relations.loader.spec.ts +++ b/packages/query-graphql/__tests__/loaders/count-relations.loader.spec.ts @@ -40,9 +40,7 @@ describe('CountRelationsLoader', () => { const service = mock>(); const countRelationsLoader = new CountRelationsLoader(RelationDTO, 'relation').createLoader(instance(service)); const dtos = [{ id: 'dto-1' }, { id: 'dto-2' }]; - when(service.countRelations(RelationDTO, 'relation', deepEqual(dtos), deepEqual({}))).thenResolve( - new Map([[dtos[0], 1]]) - ); + when(service.countRelations(RelationDTO, 'relation', deepEqual(dtos), deepEqual({}))).thenResolve(new Map([[dtos[0], 1]])); return expect( countRelationsLoader([ { dto: dtos[0], filter: {} }, @@ -56,12 +54,7 @@ describe('CountRelationsLoader', () => { const countRelationsLoader = new CountRelationsLoader(RelationDTO, 'relation').createLoader(instance(service)); const dtos: DTO[] = [{ id: 'dto-1' }, { id: 'dto-2' }, { id: 'dto-3' }, { id: 'dto-4' }]; when( - service.countRelations( - RelationDTO, - 'relation', - deepEqual([dtos[0], dtos[2]]), - deepEqual({ id: { isNot: null } }) - ) + service.countRelations(RelationDTO, 'relation', deepEqual([dtos[0], dtos[2]]), deepEqual({ id: { isNot: null } })) ).thenResolve( new Map([ [dtos[0], 1], diff --git a/packages/query-graphql/__tests__/loaders/query-relations.loader.spec.ts b/packages/query-graphql/__tests__/loaders/query-relations.loader.spec.ts index e9a625d58..9769b2449 100644 --- a/packages/query-graphql/__tests__/loaders/query-relations.loader.spec.ts +++ b/packages/query-graphql/__tests__/loaders/query-relations.loader.spec.ts @@ -63,12 +63,7 @@ describe('QueryRelationsLoader', () => { const dto3Relations = [{ id: 'relation-5' }, { id: 'relation-6' }]; const dto4Relations = [{ id: 'relation-7' }, { id: 'relation-8' }]; when( - service.queryRelations( - RelationDTO, - 'relation', - deepEqual([dtos[0], dtos[2]]), - deepEqual({ paging: { limit: 10 } }) - ) + service.queryRelations(RelationDTO, 'relation', deepEqual([dtos[0], dtos[2]]), deepEqual({ paging: { limit: 10 } })) ).thenResolve( new Map([ [dtos[0], dto1Relations], diff --git a/packages/query-graphql/__tests__/resolvers/create.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/create.resolver.spec.ts index 078d26477..ebcb3f523 100644 --- a/packages/query-graphql/__tests__/resolvers/create.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/create.resolver.spec.ts @@ -12,13 +12,7 @@ import { } from '@ptc-org/nestjs-query-graphql'; import { CreatedEvent } from '../../src/resolvers/create.resolver'; import { EventType, getDTOEventName } from '../../src/subscription'; -import { - generateSchema, - createResolverFromNest, - TestResolverDTO, - TestResolverInputDTO, - TestService -} from '../__fixtures__'; +import { generateSchema, createResolverFromNest, TestResolverDTO, TestResolverInputDTO, TestService } from '../__fixtures__'; describe('CreateResolver', () => { const expectResolverSDL = async (opts?: CreateResolverOpts) => { @@ -118,8 +112,7 @@ describe('CreateResolver', () => { }); describe('created subscription', () => { - it('should add subscription types if enableSubscriptions is true', () => - expectResolverSDL({ enableSubscriptions: true })); + it('should add subscription types if enableSubscriptions is true', () => expectResolverSDL({ enableSubscriptions: true })); it('should not expose subscriptions if enableSubscriptions is false', () => expectResolverSDL({ enableSubscriptions: false })); diff --git a/packages/query-graphql/__tests__/resolvers/delete.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/delete.resolver.spec.ts index 1b9f93ef3..19d7d4ae3 100644 --- a/packages/query-graphql/__tests__/resolvers/delete.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/delete.resolver.spec.ts @@ -140,9 +140,9 @@ describe('DeleteResolver', () => { filter: { id: { eq: 'id-1' } } }; const output: DeleteManyResponse = { deletedCount: 1 }; - when( - mockService.deleteMany(objectContaining(input.filter), objectContaining({ useSoftDelete: false })) - ).thenResolve(output); + when(mockService.deleteMany(objectContaining(input.filter), objectContaining({ useSoftDelete: false }))).thenResolve( + output + ); const result = await resolver.deleteMany({ input }); return expect(result).toEqual(output); }); @@ -177,8 +177,7 @@ describe('DeleteResolver', () => { }); describe('deleted subscription', () => { - it('should add subscription types if enableSubscriptions is true', () => - expectResolverSDL({ enableSubscriptions: true })); + it('should add subscription types if enableSubscriptions is true', () => expectResolverSDL({ enableSubscriptions: true })); it('should add subscription types if one.enableSubscriptions is true', () => expectResolverSDL({ one: { enableSubscriptions: true } })); diff --git a/packages/query-graphql/__tests__/resolvers/federation/federation.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/federation/federation.resolver.spec.ts index 6f8d8be6f..aa9bce91a 100644 --- a/packages/query-graphql/__tests__/resolvers/federation/federation.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/federation/federation.resolver.spec.ts @@ -11,13 +11,7 @@ import { Relation, UnPagedRelation } from '@ptc-org/nestjs-query-graphql'; -import { - generateSchema, - createResolverFromNest, - TestResolverDTO, - TestService, - TestRelationDTO -} from '../../__fixtures__'; +import { generateSchema, createResolverFromNest, TestResolverDTO, TestService, TestRelationDTO } from '../../__fixtures__'; describe('FederationResolver', () => { const generateSDL = (DTOClass: Class): Promise => { diff --git a/packages/query-graphql/__tests__/resolvers/read.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/read.resolver.spec.ts index c013843cb..8ad72f2ac 100644 --- a/packages/query-graphql/__tests__/resolvers/read.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/read.resolver.spec.ts @@ -31,8 +31,7 @@ describe('ReadResolver', () => { it('should use the dtoName if provided', () => expectResolverSDL({ dtoName: 'Test' })); - it('should use the one.name option for the findById if provided', () => - expectResolverSDL({ one: { name: 'read_one_test' } })); + it('should use the one.name option for the findById if provided', () => expectResolverSDL({ one: { name: 'read_one_test' } })); it('should use the many.name option for the queryMany if provided', () => expectResolverSDL({ many: { name: 'read_many_test' } })); diff --git a/packages/query-graphql/__tests__/resolvers/relations/aggregate-relation.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/relations/aggregate-relation.resolver.spec.ts index e9ace0b8c..e4c5d19ad 100644 --- a/packages/query-graphql/__tests__/resolvers/relations/aggregate-relation.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/relations/aggregate-relation.resolver.spec.ts @@ -3,13 +3,7 @@ import { deepEqual, objectContaining, when } from 'ts-mockito'; import { AggregateQuery, AggregateResponse, Filter } from '@ptc-org/nestjs-query-core'; import { AggregateRelationsResolver } from '../../../src/resolvers/relations'; import { AggregateRelationsResolverOpts } from '../../../src/resolvers/relations/aggregate-relations.resolver'; -import { - generateSchema, - createResolverFromNest, - TestResolverDTO, - TestService, - TestRelationDTO -} from '../../__fixtures__'; +import { generateSchema, createResolverFromNest, TestResolverDTO, TestService, TestRelationDTO } from '../../__fixtures__'; describe('AggregateRelationsResolver', () => { const expectResolverSDL = async (opts?: AggregateRelationsResolverOpts) => { diff --git a/packages/query-graphql/__tests__/resolvers/relations/references-relation.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/relations/references-relation.resolver.spec.ts index 0aca9817c..f7437c4e6 100644 --- a/packages/query-graphql/__tests__/resolvers/relations/references-relation.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/relations/references-relation.resolver.spec.ts @@ -1,12 +1,6 @@ import { Resolver, Query } from '@nestjs/graphql'; import { ReferencesOpts, ReferencesRelationsResolver } from '../../../src/resolvers/relations'; -import { - generateSchema, - createResolverFromNest, - TestResolverDTO, - TestService, - TestRelationDTO -} from '../../__fixtures__'; +import { generateSchema, createResolverFromNest, TestResolverDTO, TestService, TestRelationDTO } from '../../__fixtures__'; @Resolver(() => TestResolverDTO) class TestResolver extends ReferencesRelationsResolver(TestResolverDTO, { diff --git a/packages/query-graphql/__tests__/resolvers/relations/remove-relation.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/relations/remove-relation.resolver.spec.ts index 17080f4de..2e181d4c6 100644 --- a/packages/query-graphql/__tests__/resolvers/relations/remove-relation.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/relations/remove-relation.resolver.spec.ts @@ -2,13 +2,7 @@ import { when, deepEqual } from 'ts-mockito'; import { Resolver, Query } from '@nestjs/graphql'; import { RelationsOpts, RemoveRelationsResolver } from '../../../src/resolvers/relations'; import { RelationInputType, RelationsInputType } from '../../../src/types'; -import { - generateSchema, - createResolverFromNest, - TestResolverDTO, - TestService, - TestRelationDTO -} from '../../__fixtures__'; +import { generateSchema, createResolverFromNest, TestResolverDTO, TestService, TestRelationDTO } from '../../__fixtures__'; @Resolver(() => TestResolverDTO) class TestResolver extends RemoveRelationsResolver(TestResolverDTO, { @@ -98,9 +92,7 @@ describe('RemoveRelationsResolver', () => { id: 'record-id', stringField: 'foo' }; - when(mockService.removeRelations('relations', input.id, deepEqual(input.relationIds), undefined)).thenResolve( - output - ); + when(mockService.removeRelations('relations', input.id, deepEqual(input.relationIds), undefined)).thenResolve(output); // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-call const result = await resolver.removeRelationsFromTestResolverDTO({ input }); @@ -117,9 +109,7 @@ describe('RemoveRelationsResolver', () => { id: 'record-id', stringField: 'foo' }; - when(mockService.removeRelations('others', input.id, deepEqual(input.relationIds), undefined)).thenResolve( - output - ); + when(mockService.removeRelations('others', input.id, deepEqual(input.relationIds), undefined)).thenResolve(output); // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-call const result = await resolver.removeCustomsFromTestResolverDTO({ input }); diff --git a/packages/query-graphql/__tests__/resolvers/relations/update-relation.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/relations/update-relation.resolver.spec.ts index 707993cd1..c8e33a5b1 100644 --- a/packages/query-graphql/__tests__/resolvers/relations/update-relation.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/relations/update-relation.resolver.spec.ts @@ -2,13 +2,7 @@ import { when, deepEqual } from 'ts-mockito'; import { Resolver, Query } from '@nestjs/graphql'; import { RelationsOpts, UpdateRelationsResolver } from '../../../src/resolvers/relations'; import { RelationInputType, RelationsInputType } from '../../../src/types'; -import { - generateSchema, - createResolverFromNest, - TestResolverDTO, - TestService, - TestRelationDTO -} from '../../__fixtures__'; +import { generateSchema, createResolverFromNest, TestResolverDTO, TestService, TestRelationDTO } from '../../__fixtures__'; @Resolver(() => TestResolverDTO) class TestResolver extends UpdateRelationsResolver(TestResolverDTO, { @@ -100,9 +94,7 @@ describe('UpdateRelationsResolver', () => { id: 'record-id', stringField: 'foo' }; - when(mockService.addRelations('relations', input.id, deepEqual(input.relationIds), undefined)).thenResolve( - output - ); + when(mockService.addRelations('relations', input.id, deepEqual(input.relationIds), undefined)).thenResolve(output); // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-call const result = await resolver.addRelationsToTestResolverDTO({ input }); @@ -138,9 +130,7 @@ describe('UpdateRelationsResolver', () => { id: 'record-id', stringField: 'foo' }; - when(mockService.setRelations('relations', input.id, deepEqual(input.relationIds), undefined)).thenResolve( - output - ); + when(mockService.setRelations('relations', input.id, deepEqual(input.relationIds), undefined)).thenResolve(output); // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-call const result = await resolver.setRelationsOnTestResolverDTO({ input }); @@ -157,9 +147,7 @@ describe('UpdateRelationsResolver', () => { id: 'record-id', stringField: 'foo' }; - when(mockService.setRelations('relations', input.id, deepEqual(input.relationIds), undefined)).thenResolve( - output - ); + when(mockService.setRelations('relations', input.id, deepEqual(input.relationIds), undefined)).thenResolve(output); // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-call const result = await resolver.setRelationsOnTestResolverDTO({ input }); diff --git a/packages/query-graphql/__tests__/resolvers/update.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/update.resolver.spec.ts index 37f23c9a1..68675c364 100644 --- a/packages/query-graphql/__tests__/resolvers/update.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/update.resolver.spec.ts @@ -12,13 +12,7 @@ import { } from '@ptc-org/nestjs-query-graphql'; import { UpdatedEvent } from '../../src/resolvers/update.resolver'; import { EventType, getDTOEventName } from '../../src/subscription'; -import { - generateSchema, - createResolverFromNest, - TestResolverDTO, - TestResolverInputDTO, - TestService -} from '../__fixtures__'; +import { generateSchema, createResolverFromNest, TestResolverDTO, TestResolverInputDTO, TestService } from '../__fixtures__'; describe('UpdateResolver', () => { const expectResolverSDL = async (opts?: UpdateResolverOpts) => { @@ -83,9 +77,7 @@ describe('UpdateResolver', () => { id: 'id-1', stringField: 'foo' }; - when(mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: {} }))).thenResolve( - output - ); + when(mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: {} }))).thenResolve(output); const result = await resolver.updateOne({ input }); return expect(result).toEqual(output); }); @@ -103,9 +95,9 @@ describe('UpdateResolver', () => { stringField: 'foo' }; const authorizeFilter: Filter = { stringField: { eq: 'foo' } }; - when( - mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: authorizeFilter })) - ).thenResolve(output); + when(mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: authorizeFilter }))).thenResolve( + output + ); const result = await resolver.updateOne({ input }, authorizeFilter); return expect(result).toEqual(output); }); @@ -134,9 +126,9 @@ describe('UpdateResolver', () => { } }; const output: UpdateManyResponse = { updatedCount: 1 }; - when( - mockService.updateMany(objectContaining(input.input.update), objectContaining(input.input.filter)) - ).thenResolve(output); + when(mockService.updateMany(objectContaining(input.input.update), objectContaining(input.input.filter))).thenResolve( + output + ); const result = await resolver.updateMany(input); return expect(result).toEqual(output); }); @@ -165,8 +157,7 @@ describe('UpdateResolver', () => { }); describe('updated subscription', () => { - it('should add subscription types if enableSubscriptions is true', () => - expectResolverSDL({ enableSubscriptions: true })); + it('should add subscription types if enableSubscriptions is true', () => expectResolverSDL({ enableSubscriptions: true })); it('should add subscription types if one.enableSubscriptions is true', () => expectResolverSDL({ one: { enableSubscriptions: true } })); @@ -194,9 +185,7 @@ describe('UpdateResolver', () => { }; const eventName = getDTOEventName(EventType.UPDATED_ONE, TestResolverDTO); const event = { [eventName]: output }; - when(mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: {} }))).thenResolve( - output - ); + when(mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: {} }))).thenResolve(output); when(mockPubSub.publish(eventName, deepEqual(event))).thenResolve(); const result = await resolver.updateOne({ input }); verify(mockPubSub.publish(eventName, deepEqual(event))).once(); @@ -219,9 +208,7 @@ describe('UpdateResolver', () => { }; const eventName = getDTOEventName(EventType.UPDATED_ONE, TestResolverDTO); const event = { [eventName]: output }; - when(mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: {} }))).thenResolve( - output - ); + when(mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: {} }))).thenResolve(output); when(mockPubSub.publish(eventName, deepEqual(event))).thenResolve(); const result = await resolver.updateOne({ input }); verify(mockPubSub.publish(eventName, deepEqual(event))).once(); @@ -243,9 +230,7 @@ describe('UpdateResolver', () => { stringField: 'foo' }; const context = {}; - when(mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: {} }))).thenResolve( - output - ); + when(mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: {} }))).thenResolve(output); const result = await resolver.updateOne({ input }, context); verify(mockPubSub.publish(anything(), anything())).never(); return expect(result).toEqual(output); @@ -266,9 +251,7 @@ describe('UpdateResolver', () => { id: 'id-1', stringField: 'foo' }; - when(mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: {} }))).thenResolve( - output - ); + when(mockService.updateOne(input.id, objectContaining(input.update), deepEqual({ filter: {} }))).thenResolve(output); const result = await resolver.updateOne({ input }); verify(mockPubSub.publish(anything(), anything())).never(); return expect(result).toEqual(output); @@ -289,9 +272,9 @@ describe('UpdateResolver', () => { const output: UpdateManyResponse = { updatedCount: 1 }; const eventName = getDTOEventName(EventType.UPDATED_MANY, TestResolverDTO); const event = { [eventName]: output }; - when( - mockService.updateMany(objectContaining(input.input.update), objectContaining(input.input.filter)) - ).thenResolve(output); + when(mockService.updateMany(objectContaining(input.input.update), objectContaining(input.input.filter))).thenResolve( + output + ); when(mockPubSub.publish(eventName, deepEqual(event))).thenResolve(); const result = await resolver.updateMany(input); verify(mockPubSub.publish(eventName, deepEqual(event))).once(); @@ -311,9 +294,9 @@ describe('UpdateResolver', () => { const output: UpdateManyResponse = { updatedCount: 1 }; const eventName = getDTOEventName(EventType.UPDATED_MANY, TestResolverDTO); const event = { [eventName]: output }; - when( - mockService.updateMany(objectContaining(input.input.update), objectContaining(input.input.filter)) - ).thenResolve(output); + when(mockService.updateMany(objectContaining(input.input.update), objectContaining(input.input.filter))).thenResolve( + output + ); when(mockPubSub.publish(eventName, deepEqual(event))).thenResolve(); const result = await resolver.updateMany(input); verify(mockPubSub.publish(eventName, deepEqual(event))).once(); @@ -331,9 +314,9 @@ describe('UpdateResolver', () => { } }; const output: UpdateManyResponse = { updatedCount: 1 }; - when( - mockService.updateMany(objectContaining(input.input.update), objectContaining(input.input.filter)) - ).thenResolve(output); + when(mockService.updateMany(objectContaining(input.input.update), objectContaining(input.input.filter))).thenResolve( + output + ); const result = await resolver.updateMany(input); verify(mockPubSub.publish(anything(), anything())).never(); return expect(result).toEqual(output); @@ -353,9 +336,9 @@ describe('UpdateResolver', () => { } }; const output: UpdateManyResponse = { updatedCount: 1 }; - when( - mockService.updateMany(objectContaining(input.input.update), objectContaining(input.input.filter)) - ).thenResolve(output); + when(mockService.updateMany(objectContaining(input.input.update), objectContaining(input.input.filter))).thenResolve( + output + ); const result = await resolver.updateMany(input); verify(mockPubSub.publish(anything(), anything())).never(); return expect(result).toEqual(output); diff --git a/packages/query-graphql/__tests__/types/connection/cursor-connection.type.spec.ts b/packages/query-graphql/__tests__/types/connection/cursor-connection.type.spec.ts index f8ae5dbdb..7fecb302d 100644 --- a/packages/query-graphql/__tests__/types/connection/cursor-connection.type.spec.ts +++ b/packages/query-graphql/__tests__/types/connection/cursor-connection.type.spec.ts @@ -2,12 +2,7 @@ import { Field, ObjectType, Query, Resolver } from '@nestjs/graphql'; import { plainToClass } from 'class-transformer'; import { SortDirection } from '@ptc-org/nestjs-query-core'; -import { - CursorConnectionType, - CursorPagingType, - PagingStrategies, - StaticConnectionType -} from '@ptc-org/nestjs-query-graphql'; +import { CursorConnectionType, CursorPagingType, PagingStrategies, StaticConnectionType } from '@ptc-org/nestjs-query-graphql'; import { generateSchema } from '../../__fixtures__'; import { KeySet } from '../../../src/decorators'; import { getOrCreateCursorConnectionType } from '../../../src/types/connection'; @@ -32,8 +27,7 @@ describe('CursorConnectionType', (): void => { stringField!: string; } - const createPage = (paging: CursorPagingType): CursorPagingType => - plainToClass(getOrCreateCursorPagingType(), paging); + const createPage = (paging: CursorPagingType): CursorPagingType => plainToClass(getOrCreateCursorPagingType(), paging); const createTestDTO = (index: number): TestDto => ({ stringField: `foo${index}`, @@ -313,8 +307,7 @@ describe('CursorConnectionType', (): void => { } ], pageInfo: { - startCursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28xIn1dfQ==', + startCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28xIn1dfQ==', endCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', hasNextPage: false, hasPreviousPage: false @@ -346,8 +339,7 @@ describe('CursorConnectionType', (): void => { } ], pageInfo: { - startCursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28xIn1dfQ==', + startCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28xIn1dfQ==', endCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', hasNextPage: true, hasPreviousPage: false @@ -384,8 +376,7 @@ describe('CursorConnectionType', (): void => { } ], pageInfo: { - startCursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', + startCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', endCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28zIn1dfQ==', hasNextPage: true, hasPreviousPage: true @@ -415,21 +406,17 @@ describe('CursorConnectionType', (): void => { expect(response).toEqual({ edges: [ { - cursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', + cursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', node: dtos[0] }, { - cursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28zIn1dfQ==', + cursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28zIn1dfQ==', node: dtos[1] } ], pageInfo: { - startCursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', - endCursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28zIn1dfQ==', + startCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', + endCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28zIn1dfQ==', hasNextPage: true, hasPreviousPage: true }, @@ -526,8 +513,7 @@ describe('CursorConnectionType', (): void => { endCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28xIn1dfQ==', hasNextPage: true, hasPreviousPage: false, - startCursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28xIn1dfQ==' + startCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28xIn1dfQ==' }, totalCountFn: expect.any(Function) }); @@ -561,8 +547,7 @@ describe('CursorConnectionType', (): void => { } ], pageInfo: { - startCursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', + startCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', endCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28zIn1dfQ==', hasNextPage: true, hasPreviousPage: true @@ -592,21 +577,17 @@ describe('CursorConnectionType', (): void => { expect(response).toEqual({ edges: [ { - cursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', + cursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', node: dtos[1] }, { - cursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28zIn1dfQ==', + cursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28zIn1dfQ==', node: dtos[2] } ], pageInfo: { - startCursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', - endCursor: - 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28zIn1dfQ==', + startCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28yIn1dfQ==', + endCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN0cmluZ0ZpZWxkIiwidmFsdWUiOiJmb28zIn1dfQ==', hasNextPage: true, hasPreviousPage: true }, diff --git a/packages/query-graphql/__tests__/types/connection/offset-connection.type.spec.ts b/packages/query-graphql/__tests__/types/connection/offset-connection.type.spec.ts index 0ad642c2e..39db3653b 100644 --- a/packages/query-graphql/__tests__/types/connection/offset-connection.type.spec.ts +++ b/packages/query-graphql/__tests__/types/connection/offset-connection.type.spec.ts @@ -25,8 +25,7 @@ describe('OffsetConnectionType', (): void => { stringField!: string; } - const createPage = (paging: OffsetPagingType): OffsetPagingType => - plainToClass(getOrCreateOffsetPagingType(), paging); + const createPage = (paging: OffsetPagingType): OffsetPagingType => plainToClass(getOrCreateOffsetPagingType(), paging); const createTestDTO = (index: number): TestDto => ({ stringField: `foo${index}`, diff --git a/packages/query-graphql/src/auth/default-crud.authorizer.ts b/packages/query-graphql/src/auth/default-crud.authorizer.ts index 0057f54e3..c359d9eda 100644 --- a/packages/query-graphql/src/auth/default-crud.authorizer.ts +++ b/packages/query-graphql/src/auth/default-crud.authorizer.ts @@ -75,10 +75,7 @@ export function createDefaultAuthorizer( if (relation.auth) { this.relationsAuthorizers.set(relationName, createRelationAuthorizer(relation.auth)); } else if (getAuthorizer(relation.DTO)) { - this.relationsAuthorizers.set( - relationName, - this.moduleRef.get(getAuthorizerToken(relation.DTO), { strict: false }) - ); + this.relationsAuthorizers.set(relationName, this.moduleRef.get(getAuthorizerToken(relation.DTO), { strict: false })); } } } diff --git a/packages/query-graphql/src/common/dto.utils.ts b/packages/query-graphql/src/common/dto.utils.ts index a60c529be..9971e69a8 100644 --- a/packages/query-graphql/src/common/dto.utils.ts +++ b/packages/query-graphql/src/common/dto.utils.ts @@ -32,10 +32,7 @@ export const getDTONames = (DTOClass: Class, opts?: DTONamesOpts): DTO }; }; -export const getDTOIdTypeOrDefault = ( - DTOS: Class[], - defaultType: ReturnTypeFuncValue = ID -): ReturnTypeFuncValue => { +export const getDTOIdTypeOrDefault = (DTOS: Class[], defaultType: ReturnTypeFuncValue = ID): ReturnTypeFuncValue => { const dtoWithIDField = DTOS.find((dto) => !!getIDField(dto)); if (dtoWithIDField) { return getIDField(dtoWithIDField)?.returnTypeFunc() ?? defaultType; diff --git a/packages/query-graphql/src/common/resolver.utils.ts b/packages/query-graphql/src/common/resolver.utils.ts index 67fcabb5e..77b912543 100644 --- a/packages/query-graphql/src/common/resolver.utils.ts +++ b/packages/query-graphql/src/common/resolver.utils.ts @@ -7,10 +7,7 @@ const mergeArrays = (arr1?: T[], arr2?: T[]): T[] | undefined => { return undefined; }; -export const mergeBaseResolverOpts = ( - into: Into, - from: BaseResolverOptions -): Into => { +export const mergeBaseResolverOpts = (into: Into, from: BaseResolverOptions): Into => { const guards = mergeArrays(from.guards, into.guards); const interceptors = mergeArrays(from.interceptors, into.interceptors); const pipes = mergeArrays(from.pipes, into.pipes); diff --git a/packages/query-graphql/src/decorators/authorize-filter.decorator.ts b/packages/query-graphql/src/decorators/authorize-filter.decorator.ts index cdfd11ad8..0f88f78c0 100644 --- a/packages/query-graphql/src/decorators/authorize-filter.decorator.ts +++ b/packages/query-graphql/src/decorators/authorize-filter.decorator.ts @@ -4,18 +4,14 @@ import { GqlExecutionContext } from '@nestjs/graphql'; import { AuthorizationContext, OperationGroup } from '../auth'; import { AuthorizerContext } from '../interceptors'; -type PartialAuthorizationContext = Partial & - Pick; +type PartialAuthorizationContext = Partial & Pick; function getContext(executionContext: ExecutionContext): C { const gqlExecutionContext = GqlExecutionContext.create(executionContext); return gqlExecutionContext.getContext(); } -function getAuthorizerFilter>( - context: C, - authorizationContext: AuthorizationContext -) { +function getAuthorizerFilter>(context: C, authorizationContext: AuthorizationContext) { if (!context.authorizer) { return undefined; } @@ -49,8 +45,7 @@ function getAuthorizationContext( return { operationName: methodName.toString(), readonly: - partialAuthContext.operationGroup === OperationGroup.READ || - partialAuthContext.operationGroup === OperationGroup.AGGREGATE, + partialAuthContext.operationGroup === OperationGroup.READ || partialAuthContext.operationGroup === OperationGroup.AGGREGATE, ...partialAuthContext }; } diff --git a/packages/query-graphql/src/decorators/hook.decorator.ts b/packages/query-graphql/src/decorators/hook.decorator.ts index 2566d440a..e2007cce0 100644 --- a/packages/query-graphql/src/decorators/hook.decorator.ts +++ b/packages/query-graphql/src/decorators/hook.decorator.ts @@ -44,7 +44,5 @@ export const BeforeDeleteMany = hookDecorator>(HookTyp export const BeforeQueryMany = hookDecorator>(HookTypes.BEFORE_QUERY_MANY); export const BeforeFindOne = hookDecorator(HookTypes.BEFORE_FIND_ONE); -export const getHookForType = >( - hookType: HookTypes, - DTOClass: Class -): HookMetaValue => getClassMetadata(DTOClass, hookMetaDataKey(hookType), true); +export const getHookForType = >(hookType: HookTypes, DTOClass: Class): HookMetaValue => + getClassMetadata(DTOClass, hookMetaDataKey(hookType), true); diff --git a/packages/query-graphql/src/hooks/tokens.ts b/packages/query-graphql/src/hooks/tokens.ts index 27278e6c6..c7ffd666e 100644 --- a/packages/query-graphql/src/hooks/tokens.ts +++ b/packages/query-graphql/src/hooks/tokens.ts @@ -1,5 +1,4 @@ import { Class } from '@ptc-org/nestjs-query-core'; import { HookTypes } from './types'; -export const getHookToken = (hookType: HookTypes, DTOClass: Class): string => - `${DTOClass.name}${hookType}Hook`; +export const getHookToken = (hookType: HookTypes, DTOClass: Class): string => `${DTOClass.name}${hookType}Hook`; diff --git a/packages/query-graphql/src/loader/aggregate-relations.loader.ts b/packages/query-graphql/src/loader/aggregate-relations.loader.ts index 67db79195..a36c006af 100644 --- a/packages/query-graphql/src/loader/aggregate-relations.loader.ts +++ b/packages/query-graphql/src/loader/aggregate-relations.loader.ts @@ -32,13 +32,7 @@ export class AggregateRelationsLoader [...queryRelationsMap.values()].map(async (args) => { const { filter, aggregate } = args[0]; const dtos = args.map((a) => a.dto); - const aggregationResults = await service.aggregateRelations( - this.RelationDTO, - this.relationName, - dtos, - filter, - aggregate - ); + const aggregationResults = await service.aggregateRelations(this.RelationDTO, this.relationName, dtos, filter, aggregate); const dtoRelationAggregates = dtos.map((dto) => aggregationResults.get(dto) ?? {}); dtoRelationAggregates.forEach((relationAggregate, index) => { results[args[index].index] = relationAggregate; @@ -48,9 +42,7 @@ export class AggregateRelationsLoader return results; } - private groupQueries( - queryArgs: ReadonlyArray> - ): AggregateRelationsMap { + private groupQueries(queryArgs: ReadonlyArray>): AggregateRelationsMap { // group return queryArgs.reduce((map, args, index) => { const queryJson = JSON.stringify({ filter: args.filter, aggregate: args.aggregate }); diff --git a/packages/query-graphql/src/providers/resolver.provider.ts b/packages/query-graphql/src/providers/resolver.provider.ts index 921009516..e4fc81c34 100644 --- a/packages/query-graphql/src/providers/resolver.provider.ts +++ b/packages/query-graphql/src/providers/resolver.provider.ts @@ -66,8 +66,7 @@ export const isFederatedResolverOpts = ( opts: AutoResolverOpts -): opts is AssemblerCRUDAutoResolverOpts => - 'DTOClass' in opts && 'AssemblerClass' in opts; +): opts is AssemblerCRUDAutoResolverOpts => 'DTOClass' in opts && 'AssemblerClass' in opts; export const isServiceCRUDAutoResolverOpts = ( opts: AutoResolverOpts @@ -106,10 +105,7 @@ function createEntityAutoResolver DTOClass) class AutoResolver extends CRUDResolver(DTOClass, resolverOpts) { - constructor( - @InjectQueryService(EntityClass) service: QueryService, - @InjectPubSub() readonly pubSub: PubSub - ) { + constructor(@InjectQueryService(EntityClass) service: QueryService, @InjectPubSub() readonly pubSub: PubSub) { super(new Service(service)); } } diff --git a/packages/query-graphql/src/resolvers/aggregate.resolver.ts b/packages/query-graphql/src/resolvers/aggregate.resolver.ts index c1eb1dc58..2a65488fc 100644 --- a/packages/query-graphql/src/resolvers/aggregate.resolver.ts +++ b/packages/query-graphql/src/resolvers/aggregate.resolver.ts @@ -1,11 +1,4 @@ -import { - AggregateQuery, - AggregateResponse, - Class, - Filter, - mergeFilter, - QueryService -} from '@ptc-org/nestjs-query-core'; +import { AggregateQuery, AggregateResponse, Class, Filter, mergeFilter, QueryService } from '@ptc-org/nestjs-query-core'; import { Args, ArgsType, Resolver } from '@nestjs/graphql'; import omit from 'lodash.omit'; import { AuthorizerInterceptor } from '../interceptors'; @@ -20,8 +13,7 @@ export type AggregateResolverOpts = { enabled?: boolean; } & ResolverMethodOpts; -export interface AggregateResolver> - extends ServiceResolver { +export interface AggregateResolver> extends ServiceResolver { aggregate( filter: AggregateArgsType, aggregateQuery: AggregateQuery, @@ -72,10 +64,7 @@ export const Aggregateable = return AggregateResolverBase; }; // eslint-disable-next-line @typescript-eslint/no-redeclare -- intentional -export const AggregateResolver = < - DTO, - QS extends QueryService = QueryService ->( +export const AggregateResolver = = QueryService>( DTOClass: Class, opts?: AggregateResolverOpts ): ResolverClass> => Aggregateable(DTOClass, opts)(BaseServiceResolver); diff --git a/packages/query-graphql/src/resolvers/create.resolver.ts b/packages/query-graphql/src/resolvers/create.resolver.ts index 99a34ebb1..173f91697 100644 --- a/packages/query-graphql/src/resolvers/create.resolver.ts +++ b/packages/query-graphql/src/resolvers/create.resolver.ts @@ -47,10 +47,7 @@ export interface CreateResolver createMany(input: MutationArgsType>, authorizeFilter?: Filter): Promise; - createdSubscription( - input?: SubscriptionArgsType, - authorizeFilter?: Filter - ): AsyncIterator>; + createdSubscription(input?: SubscriptionArgsType, authorizeFilter?: Filter): AsyncIterator>; } /** @internal */ @@ -99,15 +96,7 @@ export const Creatable = } = opts; const createOneMutationName = opts.one?.name ?? `createOne${baseName}`; const createManyMutationName = opts.many?.name ?? `createMany${pluralBaseName}`; - const commonResolverOpts = omit( - opts, - 'dtoName', - 'one', - 'many', - 'CreateDTOClass', - 'CreateOneInput', - 'CreateManyInput' - ); + const commonResolverOpts = omit(opts, 'dtoName', 'one', 'many', 'CreateDTOClass', 'CreateOneInput', 'CreateManyInput'); @ArgsType() class CO extends MutationArgsType(CreateOneInput) {} @@ -131,10 +120,7 @@ export const Creatable = { name: createOneMutationName, description: opts?.one?.description }, commonResolverOpts, { - interceptors: [ - HookInterceptor(HookTypes.BEFORE_CREATE_ONE, CreateDTOClass, DTOClass), - AuthorizerInterceptor(DTOClass) - ] + interceptors: [HookInterceptor(HookTypes.BEFORE_CREATE_ONE, CreateDTOClass, DTOClass), AuthorizerInterceptor(DTOClass)] }, opts.one ?? {} ) @@ -159,10 +145,7 @@ export const Creatable = { name: createManyMutationName, description: opts?.many?.description }, { ...commonResolverOpts }, { - interceptors: [ - HookInterceptor(HookTypes.BEFORE_CREATE_MANY, CreateDTOClass, DTOClass), - AuthorizerInterceptor(DTOClass) - ] + interceptors: [HookInterceptor(HookTypes.BEFORE_CREATE_MANY, CreateDTOClass, DTOClass), AuthorizerInterceptor(DTOClass)] }, opts.many ?? {} ) diff --git a/packages/query-graphql/src/resolvers/crud.resolver.ts b/packages/query-graphql/src/resolvers/crud.resolver.ts index 52b9b5610..c810ee428 100644 --- a/packages/query-graphql/src/resolvers/crud.resolver.ts +++ b/packages/query-graphql/src/resolvers/crud.resolver.ts @@ -76,10 +76,7 @@ function extractReadResolverOpts, PS extend opts: CRUDResolverOpts ): MergePagingStrategyOpts { const { enableTotalCount, pagingStrategy, read } = opts; - return mergeBaseResolverOpts( - { enableTotalCount, pagingStrategy, ...read } as MergePagingStrategyOpts, - opts - ); + return mergeBaseResolverOpts({ enableTotalCount, pagingStrategy, ...read } as MergePagingStrategyOpts, opts); } function extractUpdateResolverOpts( diff --git a/packages/query-graphql/src/resolvers/delete.resolver.ts b/packages/query-graphql/src/resolvers/delete.resolver.ts index 6f5ce07ba..9c134aab1 100644 --- a/packages/query-graphql/src/resolvers/delete.resolver.ts +++ b/packages/query-graphql/src/resolvers/delete.resolver.ts @@ -39,10 +39,7 @@ export interface DeleteResolverOpts extends SubscriptionResolverOpts { export interface DeleteResolver> extends ServiceResolver { deleteOne(input: MutationArgsType, authorizeFilter?: Filter): Promise>; - deleteMany( - input: MutationArgsType>, - authorizeFilter?: Filter - ): Promise; + deleteMany(input: MutationArgsType>, authorizeFilter?: Filter): Promise; deletedOneSubscription( input?: SubscriptionArgsType, @@ -94,15 +91,7 @@ export const Deletable = const deleteManyMutationName = opts.many?.name ?? `deleteMany${pluralBaseName}`; const DMR = DeleteManyResponseType(); - const commonResolverOpts = omit( - opts, - 'dtoName', - 'one', - 'many', - 'DeleteOneInput', - 'DeleteManyInput', - 'useSoftDelete' - ); + const commonResolverOpts = omit(opts, 'dtoName', 'one', 'many', 'DeleteOneInput', 'DeleteManyInput', 'useSoftDelete'); @ObjectType(`${baseName}DeleteResponse`) // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -166,12 +155,9 @@ export const Deletable = }) authorizeFilter?: Filter ): Promise { - const deleteManyResponse = await this.service.deleteMany( - mergeFilter(input.input.filter, authorizeFilter ?? {}), - { - useSoftDelete: opts?.useSoftDelete ?? false - } - ); + const deleteManyResponse = await this.service.deleteMany(mergeFilter(input.input.filter, authorizeFilter ?? {}), { + useSoftDelete: opts?.useSoftDelete ?? false + }); if (enableManySubscriptions) { await this.publishDeletedManyEvent(deleteManyResponse, authorizeFilter); } @@ -232,10 +218,7 @@ export const Deletable = return DeleteResolverBase; }; // eslint-disable-next-line @typescript-eslint/no-redeclare -- intentional -export const DeleteResolver = < - DTO, - QS extends QueryService = QueryService ->( +export const DeleteResolver = = QueryService>( DTOClass: Class, opts: DeleteResolverOpts = {} ): ResolverClass> => Deletable(DTOClass, opts)(BaseServiceResolver); diff --git a/packages/query-graphql/src/resolvers/federation/federation.resolver.ts b/packages/query-graphql/src/resolvers/federation/federation.resolver.ts index 38223c052..83640f28e 100644 --- a/packages/query-graphql/src/resolvers/federation/federation.resolver.ts +++ b/packages/query-graphql/src/resolvers/federation/federation.resolver.ts @@ -4,10 +4,7 @@ import { ServiceResolver } from '../resolver.interface'; import { getRelations } from '../../decorators'; import { BaseResolverOptions } from '../../decorators/resolver-method.decorator'; -export const FederationResolver = < - DTO, - QS extends QueryService = QueryService ->( +export const FederationResolver = = QueryService>( DTOClass: Class, opts: BaseResolverOptions = {} ): Class> => ReadRelationsResolver(DTOClass, getRelations(DTOClass, opts)); diff --git a/packages/query-graphql/src/resolvers/read.resolver.ts b/packages/query-graphql/src/resolvers/read.resolver.ts index 1ab44ab0a..dce0b8f40 100644 --- a/packages/query-graphql/src/resolvers/read.resolver.ts +++ b/packages/query-graphql/src/resolvers/read.resolver.ts @@ -12,13 +12,7 @@ import { InferConnectionTypeFromStrategy } from '../types'; import { CursorQueryArgsTypeOpts, QueryType, StaticQueryType } from '../types/query'; -import { - BaseServiceResolver, - ExtractPagingStrategy, - ResolverClass, - ResolverOpts, - ServiceResolver -} from './resolver.interface'; +import { BaseServiceResolver, ExtractPagingStrategy, ResolverClass, ResolverOpts, ServiceResolver } from './resolver.interface'; import { AuthorizerInterceptor, HookInterceptor } from '../interceptors'; import { HookTypes } from '../hooks'; import { OperationGroup } from '../auth'; diff --git a/packages/query-graphql/src/resolvers/reference.resolver.ts b/packages/query-graphql/src/resolvers/reference.resolver.ts index c3dc7dc1c..bc33d19c1 100644 --- a/packages/query-graphql/src/resolvers/reference.resolver.ts +++ b/packages/query-graphql/src/resolvers/reference.resolver.ts @@ -36,10 +36,7 @@ export const Referenceable = return ResolveReferenceResolverBase; }; -export const ReferenceResolver = < - DTO, - QS extends QueryService = QueryService ->( +export const ReferenceResolver = = QueryService>( DTOClass: Class, opts: ReferenceResolverOpts = {} ): ResolverClass> => Referenceable(DTOClass, opts)(BaseServiceResolver); diff --git a/packages/query-graphql/src/resolvers/relations/aggregate-relations.resolver.ts b/packages/query-graphql/src/resolvers/relations/aggregate-relations.resolver.ts index 910708a02..c2c0bc4be 100644 --- a/packages/query-graphql/src/resolvers/relations/aggregate-relations.resolver.ts +++ b/packages/query-graphql/src/resolvers/relations/aggregate-relations.resolver.ts @@ -1,11 +1,4 @@ -import { - AggregateQuery, - AggregateResponse, - Class, - Filter, - mergeFilter, - QueryService -} from '@ptc-org/nestjs-query-core'; +import { AggregateQuery, AggregateResponse, Class, Filter, mergeFilter, QueryService } from '@ptc-org/nestjs-query-core'; import { ExecutionContext } from '@nestjs/common'; import { Args, ArgsType, Context, Parent, Resolver } from '@nestjs/graphql'; import { OperationGroup } from '../../auth'; diff --git a/packages/query-graphql/src/resolvers/relations/read-relations.resolver.ts b/packages/query-graphql/src/resolvers/relations/read-relations.resolver.ts index 3bc4b359b..64e58db19 100644 --- a/packages/query-graphql/src/resolvers/relations/read-relations.resolver.ts +++ b/packages/query-graphql/src/resolvers/relations/read-relations.resolver.ts @@ -131,17 +131,11 @@ export const ReadRelationsMixin = const { many, one, enableTotalCount } = relations; const manyRelations = flattenRelations(many ?? {}); const oneRelations = flattenRelations(one ?? {}); - const WithMany = manyRelations.reduce( - (RB, a) => ReadManyRelationMixin(DTOClass, { enableTotalCount, ...a })(RB), - Base - ); + const WithMany = manyRelations.reduce((RB, a) => ReadManyRelationMixin(DTOClass, { enableTotalCount, ...a })(RB), Base); return oneRelations.reduce((RB, a) => ReadOneRelationMixin(DTOClass, a)(RB), WithMany); }; -export const ReadRelationsResolver = < - DTO, - QS extends QueryService = QueryService ->( +export const ReadRelationsResolver = = QueryService>( DTOClass: Class, relations: ReadRelationsResolverOpts ): Class> => ReadRelationsMixin(DTOClass, relations)(BaseServiceResolver); diff --git a/packages/query-graphql/src/resolvers/resolver.interface.ts b/packages/query-graphql/src/resolvers/resolver.interface.ts index d80006665..53cb38684 100644 --- a/packages/query-graphql/src/resolvers/resolver.interface.ts +++ b/packages/query-graphql/src/resolvers/resolver.interface.ts @@ -34,11 +34,7 @@ export interface ServiceResolver, - Resolver extends ServiceResolver -> { +export interface ResolverClass, Resolver extends ServiceResolver> { new (service: QS): Resolver; } @@ -50,10 +46,9 @@ export class BaseServiceResolver -> = Opts['pagingStrategy'] extends PagingStrategies ? Opts['pagingStrategy'] : PagingStrategies.CURSOR; +export type ExtractPagingStrategy> = Opts['pagingStrategy'] extends PagingStrategies + ? Opts['pagingStrategy'] + : PagingStrategies.CURSOR; export type MergePagingStrategyOpts< DTO, diff --git a/packages/query-graphql/src/resolvers/update.resolver.ts b/packages/query-graphql/src/resolvers/update.resolver.ts index 87958a692..66eb1479b 100644 --- a/packages/query-graphql/src/resolvers/update.resolver.ts +++ b/packages/query-graphql/src/resolvers/update.resolver.ts @@ -37,10 +37,7 @@ export interface UpdateResolverOpts> extends Subscript export interface UpdateResolver> extends ServiceResolver { updateOne(input: MutationArgsType>, authFilter?: Filter): Promise; - updateMany( - input: MutationArgsType>, - authFilter?: Filter - ): Promise; + updateMany(input: MutationArgsType>, authFilter?: Filter): Promise; updatedOneSubscription(input?: SubscriptionArgsType, authFilter?: Filter): AsyncIterator>; @@ -108,15 +105,7 @@ export const Updateable = const updateOneMutationName = opts.one?.name ?? `updateOne${baseName}`; const updateManyMutationName = opts.many?.name ?? `updateMany${pluralBaseName}`; - const commonResolverOpts = omit( - opts, - 'dtoName', - 'one', - 'many', - 'UpdateDTOClass', - 'UpdateOneInput', - 'UpdateManyInput' - ); + const commonResolverOpts = omit(opts, 'dtoName', 'one', 'many', 'UpdateDTOClass', 'UpdateOneInput', 'UpdateManyInput'); @ArgsType() class UO extends MutationArgsType(UpdateOneInput) {} @@ -138,10 +127,7 @@ export const Updateable = () => DTOClass, { name: updateOneMutationName, description: opts?.one?.description }, { - interceptors: [ - HookInterceptor(HookTypes.BEFORE_UPDATE_ONE, UpdateDTOClass, DTOClass), - AuthorizerInterceptor(DTOClass) - ] + interceptors: [HookInterceptor(HookTypes.BEFORE_UPDATE_ONE, UpdateDTOClass, DTOClass), AuthorizerInterceptor(DTOClass)] }, commonResolverOpts, opts.one ?? {} @@ -166,10 +152,7 @@ export const Updateable = () => UMR, { name: updateManyMutationName, description: opts?.many?.description }, { - interceptors: [ - HookInterceptor(HookTypes.BEFORE_UPDATE_MANY, UpdateDTOClass, DTOClass), - AuthorizerInterceptor(DTOClass) - ] + interceptors: [HookInterceptor(HookTypes.BEFORE_UPDATE_MANY, UpdateDTOClass, DTOClass), AuthorizerInterceptor(DTOClass)] }, commonResolverOpts, opts.many ?? {} @@ -204,15 +187,10 @@ export const Updateable = } } - @ResolverSubscription( - () => DTOClass, - { name: updateOneEvent, filter: updateOneSubscriptionFilter }, - commonResolverOpts, - { - enableSubscriptions: enableOneSubscriptions, - interceptors: [AuthorizerInterceptor(DTOClass)] - } - ) + @ResolverSubscription(() => DTOClass, { name: updateOneEvent, filter: updateOneSubscriptionFilter }, commonResolverOpts, { + enableSubscriptions: enableOneSubscriptions, + interceptors: [AuthorizerInterceptor(DTOClass)] + }) // input required so graphql subscription filtering will work. // eslint-disable-next-line @typescript-eslint/no-unused-vars updatedOneSubscription( diff --git a/packages/query-graphql/src/types/aggregate/aggregate-response.type.ts b/packages/query-graphql/src/types/aggregate/aggregate-response.type.ts index 7ee62d102..29759f858 100644 --- a/packages/query-graphql/src/types/aggregate/aggregate-response.type.ts +++ b/packages/query-graphql/src/types/aggregate/aggregate-response.type.ts @@ -44,10 +44,7 @@ function AggregatedType(name: string, fields: FilterableFieldDescriptor[]): export type AggregateResponseOpts = { prefix: string }; -export function AggregateResponseType( - DTOClass: Class, - opts?: AggregateResponseOpts -): Class> { +export function AggregateResponseType(DTOClass: Class, opts?: AggregateResponseOpts): Class> { const objName = getGraphqlObjectName(DTOClass, 'Unable to make AggregationResponseType.'); const prefix = opts?.prefix ?? objName; const aggName = `${prefix}AggregateResponse`; diff --git a/packages/query-graphql/src/types/connection/array-connection.type.ts b/packages/query-graphql/src/types/connection/array-connection.type.ts index d099c5455..03de5fe53 100644 --- a/packages/query-graphql/src/types/connection/array-connection.type.ts +++ b/packages/query-graphql/src/types/connection/array-connection.type.ts @@ -4,17 +4,12 @@ import { QueryMany, StaticConnectionType } from './interfaces'; const reflector = new ValueReflector('nestjs-query:array-connection-type'); -export function getOrCreateArrayConnectionType( - TItemClass: Class -): StaticConnectionType { +export function getOrCreateArrayConnectionType(TItemClass: Class): StaticConnectionType { return reflector.memoize(TItemClass, () => { class AbstractConnection extends Array { static resolveType = [TItemClass]; - static async createFromPromise>( - queryMany: QueryMany, - query: Q - ): Promise { + static async createFromPromise>(queryMany: QueryMany, query: Q): Promise { // remove paging from the query because the ArrayConnection does not support paging. const { paging, ...rest } = query; return queryMany(rest as Q); diff --git a/packages/query-graphql/src/types/connection/cursor/pager/strategies/keyset.pager-strategy.ts b/packages/query-graphql/src/types/connection/cursor/pager/strategies/keyset.pager-strategy.ts index 2a63576c0..22a6f4c6e 100644 --- a/packages/query-graphql/src/types/connection/cursor/pager/strategies/keyset.pager-strategy.ts +++ b/packages/query-graphql/src/types/connection/cursor/pager/strategies/keyset.pager-strategy.ts @@ -27,10 +27,7 @@ export class KeysetPagerStrategy implements PagerStrategy { } toCursor(dto: DTO, index: number, opts: KeySetPagingOpts, query: Query): string { - const cursorFields: (keyof DTO)[] = [ - ...(query.sorting ?? []).map((f: SortField) => f.field), - ...this.pageFields - ]; + const cursorFields: (keyof DTO)[] = [...(query.sorting ?? []).map((f: SortField) => f.field), ...this.pageFields]; return this.encodeCursor(this.createKeySetPayload(dto, cursorFields)); } @@ -99,9 +96,7 @@ export class KeysetPagerStrategy implements PagerStrategy { const keySetField = fields[index]; if (keySetField.field !== sortField.field) { throw new Error( - `Cursor Payload does not match query sort expected ${keySetField.field as string} found ${ - sortField.field as string - }` + `Cursor Payload does not match query sort expected ${keySetField.field as string} found ${sortField.field as string}` ); } const isAsc = sortField.direction === SortDirection.ASC; diff --git a/packages/query-graphql/src/types/connection/interfaces.ts b/packages/query-graphql/src/types/connection/interfaces.ts index cc3604ebf..a39154e9e 100644 --- a/packages/query-graphql/src/types/connection/interfaces.ts +++ b/packages/query-graphql/src/types/connection/interfaces.ts @@ -76,8 +76,7 @@ export interface Pager { page>(queryMany: QueryMany, query: Q, count: Count): Promise; } -export interface StaticConnectionType - extends Class> { +export interface StaticConnectionType extends Class> { resolveType: ReturnTypeFuncValue; createFromPromise>( queryMany: QueryMany, diff --git a/packages/query-graphql/src/types/connection/offset/pager/pager.ts b/packages/query-graphql/src/types/connection/offset/pager/pager.ts index 903a6c018..3489db5bb 100644 --- a/packages/query-graphql/src/types/connection/offset/pager/pager.ts +++ b/packages/query-graphql/src/types/connection/offset/pager/pager.ts @@ -15,11 +15,7 @@ const DEFAULT_PAGING_META = (query: Query): OffsetPagingMeta => ( }); export class OffsetPager implements Pager> { - async page>( - queryMany: QueryMany, - query: Q, - count: Count - ): Promise> { + async page>(queryMany: QueryMany, query: Q, count: Count): Promise> { const pagingMeta = this.getPageMeta(query); if (!this.isValidPaging(pagingMeta)) { return EMPTY_PAGING_RESULTS(); diff --git a/packages/query-graphql/src/types/query/field-comparison/field-comparison.factory.ts b/packages/query-graphql/src/types/query/field-comparison/field-comparison.factory.ts index 731f6cc3d..d0b4b8a64 100644 --- a/packages/query-graphql/src/types/query/field-comparison/field-comparison.factory.ts +++ b/packages/query-graphql/src/types/query/field-comparison/field-comparison.factory.ts @@ -48,14 +48,7 @@ const knownTypes: Set = new Set([ GraphQLTimestamp ]); -const allowedBetweenTypes: Set = new Set([ - Number, - Int, - Float, - Date, - GraphQLISODateTime, - GraphQLTimestamp -]); +const allowedBetweenTypes: Set = new Set([Number, Int, Float, Date, GraphQLISODateTime, GraphQLTimestamp]); /** @internal */ const getTypeName = (SomeType: ReturnTypeFuncValue): string => { diff --git a/packages/query-graphql/src/types/query/paging/index.ts b/packages/query-graphql/src/types/query/paging/index.ts index 8f7f6a21f..c653efeab 100644 --- a/packages/query-graphql/src/types/query/paging/index.ts +++ b/packages/query-graphql/src/types/query/paging/index.ts @@ -2,10 +2,4 @@ export { getOrCreateCursorPagingType } from './cursor-paging.type'; export { getOrCreateOffsetPagingType } from './offset-paging.type'; export { getOrCreateNonePagingType } from './none-paging.type'; export { PagingStrategies } from './constants'; -export { - OffsetPagingType, - NonePagingType, - CursorPagingType, - PagingTypes, - InferPagingTypeFromStrategy -} from './interfaces'; +export { OffsetPagingType, NonePagingType, CursorPagingType, PagingTypes, InferPagingTypeFromStrategy } from './interfaces'; diff --git a/packages/query-graphql/src/types/query/query-args/interfaces.ts b/packages/query-graphql/src/types/query/query-args/interfaces.ts index 3fbf5e346..e527e5f37 100644 --- a/packages/query-graphql/src/types/query/query-args/interfaces.ts +++ b/packages/query-graphql/src/types/query/query-args/interfaces.ts @@ -1,12 +1,7 @@ import { Class, Filter, Query, SortField } from '@ptc-org/nestjs-query-core'; import { PagingStrategies, InferPagingTypeFromStrategy } from '../paging'; import { FilterTypeOptions } from '../filter.type'; -import { - ArrayConnectionOptions, - CursorConnectionOptions, - OffsetConnectionOptions, - StaticConnectionType -} from '../../connection'; +import { ArrayConnectionOptions, CursorConnectionOptions, OffsetConnectionOptions, StaticConnectionType } from '../../connection'; export type BaseQueryArgsTypeOpts = { /** diff --git a/packages/query-graphql/src/types/update-many-input.type.ts b/packages/query-graphql/src/types/update-many-input.type.ts index 0bce80b2a..8d28904c8 100644 --- a/packages/query-graphql/src/types/update-many-input.type.ts +++ b/packages/query-graphql/src/types/update-many-input.type.ts @@ -15,10 +15,7 @@ export interface UpdateManyInputType { * @param UpdateType - The InputType to use for the update field. */ // eslint-disable-next-line @typescript-eslint/no-redeclare -- intentional -export function UpdateManyInputType( - DTOClass: Class, - UpdateType: Class -): Class> { +export function UpdateManyInputType(DTOClass: Class, UpdateType: Class): Class> { const F = UpdateFilterType(DTOClass); @InputType({ isAbstract: true }) diff --git a/packages/query-graphql/src/types/validators/property-max.validator.ts b/packages/query-graphql/src/types/validators/property-max.validator.ts index cb6bdfefa..7c1adbe2a 100644 --- a/packages/query-graphql/src/types/validators/property-max.validator.ts +++ b/packages/query-graphql/src/types/validators/property-max.validator.ts @@ -19,8 +19,6 @@ export class PropertyMax implements ValidatorConstraintInterface { } defaultMessage(args: ValidationArguments): string { - return `Field ${args.property}.${args.constraints[0] as number} max allowed value is \`${ - args.constraints[1] as number - }\`.`; + return `Field ${args.property}.${args.constraints[0] as number} max allowed value is \`${args.constraints[1] as number}\`.`; } } diff --git a/packages/query-mongoose/__tests__/providers.spec.ts b/packages/query-mongoose/__tests__/providers.spec.ts index 0e5aaf87a..d175a8ded 100644 --- a/packages/query-mongoose/__tests__/providers.spec.ts +++ b/packages/query-mongoose/__tests__/providers.spec.ts @@ -7,9 +7,7 @@ import { MongooseQueryService } from '../src/services'; describe('createTypegooseQueryServiceProviders', () => { it('should create a provider for the entity', () => { class TestEntity extends Document {} - const providers = createMongooseQueryServiceProviders([ - { document: TestEntity, name: TestEntity.name, schema: null } - ]); + const providers = createMongooseQueryServiceProviders([{ document: TestEntity, name: TestEntity.name, schema: null }]); expect(providers).toHaveLength(1); expect(providers[0].provide).toBe(getQueryServiceToken(TestEntity)); expect(providers[0].inject).toEqual([`${TestEntity.name}Model`]); diff --git a/packages/query-mongoose/__tests__/query/where.builder.spec.ts b/packages/query-mongoose/__tests__/query/where.builder.spec.ts index eac2b0f89..5d202c7af 100644 --- a/packages/query-mongoose/__tests__/query/where.builder.spec.ts +++ b/packages/query-mongoose/__tests__/query/where.builder.spec.ts @@ -56,12 +56,7 @@ describe('WhereBuilder', (): void => { it('and multiple expressions together', (): void => { expectFilter( { - and: [ - { numberType: { gt: 10 } }, - { numberType: { lt: 20 } }, - { numberType: { gte: 30 } }, - { numberType: { lte: 40 } } - ] + and: [{ numberType: { gt: 10 } }, { numberType: { lt: 20 } }, { numberType: { gte: 30 } }, { numberType: { lte: 40 } }] }, { $and: [ @@ -117,12 +112,7 @@ describe('WhereBuilder', (): void => { it('or multiple expressions together', (): void => { expectFilter( { - or: [ - { numberType: { gt: 10 } }, - { numberType: { lt: 20 } }, - { numberType: { gte: 30 } }, - { numberType: { lte: 40 } } - ] + or: [{ numberType: { gt: 10 } }, { numberType: { lt: 20 } }, { numberType: { gte: 30 } }, { numberType: { lte: 40 } }] }, { $or: [ diff --git a/packages/query-mongoose/__tests__/services/mongoose-query.service.spec.ts b/packages/query-mongoose/__tests__/services/mongoose-query.service.spec.ts index bc4871f01..ba519abf1 100644 --- a/packages/query-mongoose/__tests__/services/mongoose-query.service.spec.ts +++ b/packages/query-mongoose/__tests__/services/mongoose-query.service.spec.ts @@ -424,9 +424,7 @@ describe('MongooseQueryService', () => { it('should reject if the entities already exist', async () => { const queryService = moduleRef.get(TestEntityService); - return expect(queryService.createMany(TEST_ENTITIES)).rejects.toThrow( - 'Id cannot be specified when updating or creating' - ); + return expect(queryService.createMany(TEST_ENTITIES)).rejects.toThrow('Id cannot be specified when updating or creating'); }); }); @@ -448,9 +446,7 @@ describe('MongooseQueryService', () => { it('should reject if the entity contains an id', async () => { const entity = TEST_ENTITIES[0]; const queryService = moduleRef.get(TestEntityService); - return expect(queryService.createOne({ ...entity })).rejects.toThrow( - 'Id cannot be specified when updating or creating' - ); + return expect(queryService.createOne({ ...entity })).rejects.toThrow('Id cannot be specified when updating or creating'); }); }); @@ -550,9 +546,9 @@ describe('MongooseQueryService', () => { it('should reject if the update contains the ID', async () => { const queryService = moduleRef.get(TestEntityService); - return expect( - queryService.updateOne(TEST_ENTITIES[0]._id, { id: new Types.ObjectId().toHexString() }) - ).rejects.toThrow('Id cannot be specified when updating'); + return expect(queryService.updateOne(TEST_ENTITIES[0]._id, { id: new Types.ObjectId().toHexString() })).rejects.toThrow( + 'Id cannot be specified when updating' + ); }); it('call fail if the entity is not found', async () => { @@ -749,42 +745,27 @@ describe('MongooseQueryService', () => { describe('with virtual entity', () => { it('call select and return the result', async () => { const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'virtualTestReferences', - TEST_ENTITIES[0], - { - filter: { referenceName: { isNot: null } } - } - ); + const queryResult = await queryService.queryRelations(TestReference, 'virtualTestReferences', TEST_ENTITIES[0], { + filter: { referenceName: { isNot: null } } + }); // eslint-disable-next-line @typescript-eslint/no-unsafe-return return expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_REFERENCES.slice(0, 3))); }); it('should apply a filter', async () => { const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'virtualTestReferences', - TEST_ENTITIES[0], - { - filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } - } - ); + const queryResult = await queryService.queryRelations(TestReference, 'virtualTestReferences', TEST_ENTITIES[0], { + filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } + }); expect(convertDocuments(queryResult)).toEqual([TEST_REFERENCES[1]]); }); it('should apply paging', async () => { const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'virtualTestReferences', - TEST_ENTITIES[0], - { - paging: { limit: 2, offset: 1 }, - sorting: [{ field: 'referenceName', direction: SortDirection.ASC }] - } - ); + const queryResult = await queryService.queryRelations(TestReference, 'virtualTestReferences', TEST_ENTITIES[0], { + paging: { limit: 2, offset: 1 }, + sorting: [{ field: 'referenceName', direction: SortDirection.ASC }] + }); expect(convertDocuments(queryResult)).toEqual(TEST_REFERENCES.slice(1, 3)); }); }); diff --git a/packages/query-mongoose/src/mongoose-types.helper.ts b/packages/query-mongoose/src/mongoose-types.helper.ts index c5aa97338..95f082230 100644 --- a/packages/query-mongoose/src/mongoose-types.helper.ts +++ b/packages/query-mongoose/src/mongoose-types.helper.ts @@ -49,11 +49,7 @@ export type VirtualReferenceOptions = { // eslint-disable-next-line @typescript-eslint/no-explicit-any export function isVirtualReferenceOptions(options: unknown): options is VirtualReferenceOptions { return ( - typeof options === 'object' && - options !== null && - 'ref' in options && - 'localField' in options && - 'foreignField' in options + typeof options === 'object' && options !== null && 'ref' in options && 'localField' in options && 'foreignField' in options ); } diff --git a/packages/query-mongoose/src/providers.ts b/packages/query-mongoose/src/providers.ts index 41af20687..48eac94dc 100644 --- a/packages/query-mongoose/src/providers.ts +++ b/packages/query-mongoose/src/providers.ts @@ -12,9 +12,7 @@ export type NestjsQueryModelDefinition = { // eslint-disable-next-line @typescript-eslint/no-unsafe-return AssemblerSerializer((obj: Document) => obj.toObject({ virtuals: true }))(Document); -function createMongooseQueryServiceProvider( - model: NestjsQueryModelDefinition -): FactoryProvider { +function createMongooseQueryServiceProvider(model: NestjsQueryModelDefinition): FactoryProvider { return { provide: getQueryServiceToken(model.document), useFactory(ModelClass: Model) { @@ -27,6 +25,5 @@ function createMongooseQueryServiceProvider( }; } -export const createMongooseQueryServiceProviders = ( - models: NestjsQueryModelDefinition[] -): FactoryProvider[] => models.map((model) => createMongooseQueryServiceProvider(model)); +export const createMongooseQueryServiceProviders = (models: NestjsQueryModelDefinition[]): FactoryProvider[] => + models.map((model) => createMongooseQueryServiceProvider(model)); diff --git a/packages/query-mongoose/src/query/comparison.builder.ts b/packages/query-mongoose/src/query/comparison.builder.ts index 6c2537524..1606c99e9 100644 --- a/packages/query-mongoose/src/query/comparison.builder.ts +++ b/packages/query-mongoose/src/query/comparison.builder.ts @@ -95,10 +95,7 @@ export class ComparisonBuilder { return val !== null && typeof val === 'object' && 'lower' in val && 'upper' in val; } - private likeComparison( - cmp: string, - val: EntityComparisonField - ): FilterQuery { + private likeComparison(cmp: string, val: EntityComparisonField): FilterQuery { const regExpStr = escapeRegExp(`${String(val)}`).replace(/%/g, '.*'); const regExp = new RegExp(regExpStr, cmp.includes('ilike') ? 'i' : undefined); diff --git a/packages/query-mongoose/src/query/where.builder.ts b/packages/query-mongoose/src/query/where.builder.ts index ff2530b94..8fce22223 100644 --- a/packages/query-mongoose/src/query/where.builder.ts +++ b/packages/query-mongoose/src/query/where.builder.ts @@ -64,19 +64,14 @@ export class WhereBuilder { return obj[field] as FilterFieldComparison; } - private withFilterComparison( - field: T, - cmp: FilterFieldComparison - ): FilterQuery { + private withFilterComparison(field: T, cmp: FilterFieldComparison): FilterQuery { const opts = Object.keys(cmp) as (keyof FilterFieldComparison)[]; if (opts.length === 1) { const cmpType = opts[0]; return this.comparisonBuilder.build(field, cmpType, cmp[cmpType] as EntityComparisonField); } return { - $or: opts.map((cmpType) => - this.comparisonBuilder.build(field, cmpType, cmp[cmpType] as EntityComparisonField) - ) + $or: opts.map((cmpType) => this.comparisonBuilder.build(field, cmpType, cmp[cmpType] as EntityComparisonField)) } as FilterQuery; } } diff --git a/packages/query-mongoose/src/services/mongoose-query.service.ts b/packages/query-mongoose/src/services/mongoose-query.service.ts index 13fb0015d..cb76c945f 100644 --- a/packages/query-mongoose/src/services/mongoose-query.service.ts +++ b/packages/query-mongoose/src/services/mongoose-query.service.ts @@ -71,10 +71,7 @@ export class MongooseQueryService return this.Model.find(filterQuery, {}, options).exec(); } - async aggregate( - filter: Filter, - aggregateQuery: AggregateQuery - ): Promise[]> { + async aggregate(filter: Filter, aggregateQuery: AggregateQuery): Promise[]> { const { aggregate, filterQuery, options } = this.filterQueryBuilder.buildAggregateQuery(aggregateQuery, filter); const aggPipeline: PipelineStage[] = [{ $match: filterQuery }, { $group: aggregate }]; if (options.sort) { diff --git a/packages/query-mongoose/src/services/reference-query.service.ts b/packages/query-mongoose/src/services/reference-query.service.ts index 8777227f5..368bcc403 100644 --- a/packages/query-mongoose/src/services/reference-query.service.ts +++ b/packages/query-mongoose/src/services/reference-query.service.ts @@ -356,11 +356,7 @@ export abstract class ReferenceQueryService { return undefined; } - private getObjectIdReferenceFilter( - refName: string, - entity: Entity, - filter?: Filter - ): Filter { + private getObjectIdReferenceFilter(refName: string, entity: Entity, filter?: Filter): Filter { const referenceIds = entity[refName as keyof Entity]; const refFilter = { _id: { [Array.isArray(referenceIds) ? 'in' : 'eq']: referenceIds } diff --git a/packages/query-sequelize/__tests__/__fixtures__/seeds.ts b/packages/query-sequelize/__tests__/__fixtures__/seeds.ts index 2f2303ba8..e0f7bf204 100644 --- a/packages/query-sequelize/__tests__/__fixtures__/seeds.ts +++ b/packages/query-sequelize/__tests__/__fixtures__/seeds.ts @@ -4,10 +4,9 @@ import { truncate } from './sequelize.fixture'; import { TestRelation } from './test-relation.entity'; import { TestEntity } from './test.entity'; -export const PLAIN_TEST_ENTITIES: Pick< - TestEntity, - 'testEntityPk' | 'boolType' | 'dateType' | 'numberType' | 'stringType' ->[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((i) => { +export const PLAIN_TEST_ENTITIES: Pick[] = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +].map((i) => { const testEntityPk = `test-entity-${i}`; return { testEntityPk, diff --git a/packages/query-sequelize/__tests__/query/where.builder.spec.ts b/packages/query-sequelize/__tests__/query/where.builder.spec.ts index 364ca5905..21c7829e2 100644 --- a/packages/query-sequelize/__tests__/query/where.builder.spec.ts +++ b/packages/query-sequelize/__tests__/query/where.builder.spec.ts @@ -55,12 +55,7 @@ describe('WhereBuilder', (): void => { it('and multiple expressions together', (): void => { expectWhereQuery( { - and: [ - { numberType: { gt: 10 } }, - { numberType: { lt: 20 } }, - { numberType: { gte: 30 } }, - { numberType: { lte: 40 } } - ] + and: [{ numberType: { gt: 10 } }, { numberType: { lt: 20 } }, { numberType: { gte: 30 } }, { numberType: { lte: 40 } }] }, { [Op.and]: [ @@ -101,16 +96,10 @@ describe('WhereBuilder', (): void => { { [Op.and]: [ { - [Op.or]: [ - { [Op.and]: [{ numberType: { [Op.gt]: 10 } }] }, - { [Op.and]: [{ numberType: { [Op.lt]: 20 } }] } - ] + [Op.or]: [{ [Op.and]: [{ numberType: { [Op.gt]: 10 } }] }, { [Op.and]: [{ numberType: { [Op.lt]: 20 } }] }] }, { - [Op.or]: [ - { [Op.and]: [{ numberType: { [Op.gte]: 30 } }] }, - { [Op.and]: [{ numberType: { [Op.lte]: 40 } }] } - ] + [Op.or]: [{ [Op.and]: [{ numberType: { [Op.gte]: 30 } }] }, { [Op.and]: [{ numberType: { [Op.lte]: 40 } }] }] } ] } @@ -122,12 +111,7 @@ describe('WhereBuilder', (): void => { it('or multiple expressions together', (): void => { expectWhereQuery( { - or: [ - { numberType: { gt: 10 } }, - { numberType: { lt: 20 } }, - { numberType: { gte: 30 } }, - { numberType: { lte: 40 } } - ] + or: [{ numberType: { gt: 10 } }, { numberType: { lt: 20 } }, { numberType: { gte: 30 } }, { numberType: { lte: 40 } }] }, { [Op.or]: [ @@ -180,16 +164,10 @@ describe('WhereBuilder', (): void => { { [Op.or]: [ { - [Op.and]: [ - { [Op.and]: [{ numberType: { [Op.gt]: 10 } }] }, - { [Op.and]: [{ numberType: { [Op.lt]: 20 } }] } - ] + [Op.and]: [{ [Op.and]: [{ numberType: { [Op.gt]: 10 } }] }, { [Op.and]: [{ numberType: { [Op.lt]: 20 } }] }] }, { - [Op.and]: [ - { [Op.and]: [{ numberType: { [Op.gte]: 30 } }] }, - { [Op.and]: [{ numberType: { [Op.lte]: 40 } }] } - ] + [Op.and]: [{ [Op.and]: [{ numberType: { [Op.gte]: 30 } }] }, { [Op.and]: [{ numberType: { [Op.lte]: 40 } }] }] } ] } diff --git a/packages/query-sequelize/__tests__/services/sequelize-query.service.spec.ts b/packages/query-sequelize/__tests__/services/sequelize-query.service.spec.ts index 8d62d09a5..a15fb2ed3 100644 --- a/packages/query-sequelize/__tests__/services/sequelize-query.service.spec.ts +++ b/packages/query-sequelize/__tests__/services/sequelize-query.service.spec.ts @@ -409,10 +409,7 @@ describe('SequelizeQueryService', (): void => { }); it('should return an empty array if no results are found.', async () => { - const entities: TestEntity[] = [ - PLAIN_TEST_ENTITIES[0] as TestEntity, - { testEntityPk: 'does-not-exist' } as TestEntity - ]; + const entities: TestEntity[] = [PLAIN_TEST_ENTITIES[0] as TestEntity, { testEntityPk: 'does-not-exist' } as TestEntity]; const queryService = moduleRef.get(TestEntityService); const queryResult = await queryService.queryRelations(TestRelation, 'testRelations', entities, { filter: { relationName: { isNot: null } } @@ -634,10 +631,7 @@ describe('SequelizeQueryService', (): void => { }); it('should return an empty array if no results are found.', async () => { - const entities: TestEntity[] = [ - PLAIN_TEST_ENTITIES[0] as TestEntity, - { testEntityPk: 'does-not-exist' } as TestEntity - ]; + const entities: TestEntity[] = [PLAIN_TEST_ENTITIES[0] as TestEntity, { testEntityPk: 'does-not-exist' } as TestEntity]; const queryService = moduleRef.get(TestEntityService); const queryResult = await queryService.aggregateRelations( TestRelation, @@ -807,10 +801,7 @@ describe('SequelizeQueryService', (): void => { }); it('should return undefined select if no results are found.', async () => { - const entities: TestEntity[] = [ - PLAIN_TEST_ENTITIES[0] as TestEntity, - { testEntityPk: 'does-not-exist' } as TestEntity - ]; + const entities: TestEntity[] = [PLAIN_TEST_ENTITIES[0] as TestEntity, { testEntityPk: 'does-not-exist' } as TestEntity]; const queryService = moduleRef.get(TestEntityService); const queryResult = await queryService.findRelation(TestRelation, 'oneTestRelation', entities); @@ -962,14 +953,9 @@ describe('SequelizeQueryService', (): void => { const entity = PLAIN_TEST_ENTITIES[0]; const queryService = moduleRef.get(TestEntityService); return expect( - queryService.setRelation( - 'oneTestRelation', - entity.testEntityPk, - PLAIN_TEST_RELATIONS[1].testRelationPk, - { - relationFilter: { relationName: { like: '%-one' } } - } - ) + queryService.setRelation('oneTestRelation', entity.testEntityPk, PLAIN_TEST_RELATIONS[1].testRelationPk, { + relationFilter: { relationName: { like: '%-one' } } + }) ).rejects.toThrow('Unable to find oneTestRelation to set on TestEntity'); }); }); @@ -1054,14 +1040,9 @@ describe('SequelizeQueryService', (): void => { const entity = PLAIN_TEST_ENTITIES[0]; const queryService = moduleRef.get(TestEntityService); return expect( - queryService.removeRelation( - 'oneTestRelation', - entity.testEntityPk, - PLAIN_TEST_RELATIONS[1].testRelationPk, - { - filter: { stringType: { eq: PLAIN_TEST_ENTITIES[1].stringType } } - } - ) + queryService.removeRelation('oneTestRelation', entity.testEntityPk, PLAIN_TEST_RELATIONS[1].testRelationPk, { + filter: { stringType: { eq: PLAIN_TEST_ENTITIES[1].stringType } } + }) ).rejects.toThrow('Unable to find TestEntity with id: test-entity-1'); }); @@ -1355,9 +1336,9 @@ describe('SequelizeQueryService', (): void => { it('should reject if the update contains a primary key', async () => { const queryService = moduleRef.get(TestEntityService); - return expect( - queryService.updateOne(PLAIN_TEST_ENTITIES[0].testEntityPk, { testEntityPk: 'bad-id' }) - ).rejects.toThrow('Id cannot be specified when updating'); + return expect(queryService.updateOne(PLAIN_TEST_ENTITIES[0].testEntityPk, { testEntityPk: 'bad-id' })).rejects.toThrow( + 'Id cannot be specified when updating' + ); }); it('call fail if the entity is not found', async () => { diff --git a/packages/query-sequelize/src/query/filter-query.builder.ts b/packages/query-sequelize/src/query/filter-query.builder.ts index 1a15e2528..a7edc0750 100644 --- a/packages/query-sequelize/src/query/filter-query.builder.ts +++ b/packages/query-sequelize/src/query/filter-query.builder.ts @@ -222,10 +222,7 @@ export class FilterQueryBuilder>> { return qb; } - private applyAssociationIncludes( - findOpts: Opts, - filter?: Filter - ): Opts { + private applyAssociationIncludes(findOpts: Opts, filter?: Filter): Opts { if (!filter) { return findOpts; } @@ -242,10 +239,7 @@ export class FilterQueryBuilder>> { const { relationNames } = this; const referencedFields = getFilterFields(filter); const referencedRelations = referencedFields.filter((f) => relationNames.includes(f)); - return referencedRelations.reduce( - (map, r) => map.set(r, this.model.associations[r]), - new Map() - ); + return referencedRelations.reduce((map, r) => map.set(r, this.model.associations[r]), new Map()); } private get relationNames(): string[] { diff --git a/packages/query-sequelize/src/query/sql-comparison.builder.ts b/packages/query-sequelize/src/query/sql-comparison.builder.ts index 82dc2502f..e183dc400 100644 --- a/packages/query-sequelize/src/query/sql-comparison.builder.ts +++ b/packages/query-sequelize/src/query/sql-comparison.builder.ts @@ -66,10 +66,7 @@ export class SQLComparisonBuilder { throw new Error(`unknown operator ${JSON.stringify(cmp)}`); } - private betweenComparisonSQL( - col: string, - val: EntityComparisonField - ): WhereOptions { + private betweenComparisonSQL(col: string, val: EntityComparisonField): WhereOptions { if (this.isBetweenVal(val)) { return { [col]: { [Op.between]: [val.lower, val.upper] as unknown as Rangable } @@ -78,10 +75,7 @@ export class SQLComparisonBuilder { throw new Error(`Invalid value for between expected {lower: val, upper: val} got ${JSON.stringify(val)}`); } - private notBetweenComparisonSQL( - col: string, - val: EntityComparisonField - ): WhereOptions { + private notBetweenComparisonSQL(col: string, val: EntityComparisonField): WhereOptions { if (this.isBetweenVal(val)) { return { [col]: { [Op.notBetween]: [val.lower, val.upper] as unknown as Rangable } diff --git a/packages/query-sequelize/src/query/where.builder.ts b/packages/query-sequelize/src/query/where.builder.ts index 02a1ac347..1f9f2e87b 100644 --- a/packages/query-sequelize/src/query/where.builder.ts +++ b/packages/query-sequelize/src/query/where.builder.ts @@ -42,20 +42,11 @@ export class WhereBuilder { * Creates field comparisons from a filter. This method will ignore and/or properties. * @param filter - the filter with fields to create comparisons for. */ - private filterFields( - filter: Filter, - associations: Map, - alias?: string - ): WhereOptions | undefined { + private filterFields(filter: Filter, associations: Map, alias?: string): WhereOptions | undefined { const ands = Object.keys(filter) .filter((f) => f !== 'and' && f !== 'or') .map((field) => - this.withFilterComparison( - field as keyof Entity, - this.getField(filter, field as keyof Entity), - associations, - alias - ) + this.withFilterComparison(field as keyof Entity, this.getField(filter, field as keyof Entity), associations, alias) ); if (ands.length === 1) { return ands[0]; diff --git a/packages/query-sequelize/src/services/relation-query.service.ts b/packages/query-sequelize/src/services/relation-query.service.ts index 84a84051c..8798df26e 100644 --- a/packages/query-sequelize/src/services/relation-query.service.ts +++ b/packages/query-sequelize/src/services/relation-query.service.ts @@ -19,6 +19,7 @@ interface SequelizeAssociation { isMultiAssociation: boolean; isSingleAssociation: boolean; } + /** * Base class to house relations loading. * @internal @@ -31,7 +32,8 @@ export abstract class RelationQueryService): Promise; /** - * Query for relations for an array of Entities. This method will return a map with the Entity as the key and the relations as the value. + * Query for relations for an array of Entities. This method will return a map + * with the Entity as the key and the relations as the value. * @param RelationClass - The class of the relation. * @param relationName - The name of the relation to load. * @param entities - the dtos to find relations for. @@ -150,10 +152,7 @@ export abstract class RelationQueryService(relationEntity); - return this.ensureIsEntity(dto).$count( - relationName, - relationQueryBuilder.countOptions(assembler.convertQuery({ filter })) - ); + return this.ensureIsEntity(dto).$count(relationName, relationQueryBuilder.countOptions(assembler.convertQuery({ filter }))); } /** @@ -321,7 +320,8 @@ export abstract class RelationQueryService { const map = await mapPromise; - const results = (await this.ensureIsEntity(e).$get(relationName as keyof Entity, findOptions)) as Record< - string, - unknown - >[]; + const results = (await this.ensureIsEntity(e).$get(relationName as keyof Entity, findOptions)) as Record[]; const aggResponse = AggregateBuilder.convertToAggregateResponse(results).map((agg) => assembler.convertAggregateResponse(agg) ); @@ -445,11 +442,7 @@ export abstract class RelationQueryService( - relationName: string, - ids: (string | number)[], - filter?: Filter - ): Promise { + private getRelations(relationName: string, ids: (string | number)[], filter?: Filter): Promise { const relationEntity = this.getRelationEntity(relationName); const relationQueryBuilder = this.getRelationQueryBuilder(relationEntity); const findOptions = relationQueryBuilder.findByIdOptions(ids, { filter }); diff --git a/packages/query-sequelize/src/services/sequelize-query.service.ts b/packages/query-sequelize/src/services/sequelize-query.service.ts index 646bf8db8..66b0ba35e 100644 --- a/packages/query-sequelize/src/services/sequelize-query.service.ts +++ b/packages/query-sequelize/src/services/sequelize-query.service.ts @@ -182,10 +182,7 @@ export class SequelizeQueryService> */ async updateMany(update: DeepPartial, filter: Filter): Promise { this.ensureIdIsNotPresent(update); - const [count] = await this.model.update( - this.getChangedValues(update), - this.filterQueryBuilder.updateOptions({ filter }) - ); + const [count] = await this.model.update(this.getChangedValues(update), this.filterQueryBuilder.updateOptions({ filter })); return { updatedCount: count }; } diff --git a/packages/query-typegoose/__tests__/__fixtures__/seeds.ts b/packages/query-typegoose/__tests__/__fixtures__/seeds.ts index 171b41870..85ed492ca 100644 --- a/packages/query-typegoose/__tests__/__fixtures__/seeds.ts +++ b/packages/query-typegoose/__tests__/__fixtures__/seeds.ts @@ -14,9 +14,7 @@ export const TEST_ENTITIES: DocumentType[] = [1, 2, 3, 4, 5, 6, 7, 8 } as DocumentType) ); -export const TEST_DISCRIMINATED_ENTITIES: DocumentType[] = [ - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 -].map( +export const TEST_DISCRIMINATED_ENTITIES: DocumentType[] = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map( (i) => ({ boolType: i % 2 === 0, diff --git a/packages/query-typegoose/__tests__/query/where.builder.spec.ts b/packages/query-typegoose/__tests__/query/where.builder.spec.ts index b59eb8be5..fd955bcd3 100644 --- a/packages/query-typegoose/__tests__/query/where.builder.spec.ts +++ b/packages/query-typegoose/__tests__/query/where.builder.spec.ts @@ -6,10 +6,7 @@ import { WhereBuilder } from '../../src/query'; describe('WhereBuilder', (): void => { const createWhereBuilder = () => new WhereBuilder(getModelForClass(TestEntity)); - const expectFilterQuery = ( - filter: Filter, - expectedFilterQuery: mongoose.FilterQuery - ): void => { + const expectFilterQuery = (filter: Filter, expectedFilterQuery: mongoose.FilterQuery): void => { const actual = createWhereBuilder().build(filter); expect(actual).toEqual(expectedFilterQuery); }; @@ -59,12 +56,7 @@ describe('WhereBuilder', (): void => { it('and multiple expressions together', (): void => { expectFilterQuery( { - and: [ - { numberType: { gt: 10 } }, - { numberType: { lt: 20 } }, - { numberType: { gte: 30 } }, - { numberType: { lte: 40 } } - ] + and: [{ numberType: { gt: 10 } }, { numberType: { lt: 20 } }, { numberType: { gte: 30 } }, { numberType: { lte: 40 } }] }, { $and: [ @@ -120,12 +112,7 @@ describe('WhereBuilder', (): void => { it('or multiple expressions together', (): void => { expectFilterQuery( { - or: [ - { numberType: { gt: 10 } }, - { numberType: { lt: 20 } }, - { numberType: { gte: 30 } }, - { numberType: { lte: 40 } } - ] + or: [{ numberType: { gt: 10 } }, { numberType: { lt: 20 } }, { numberType: { gte: 30 } }, { numberType: { lte: 40 } }] }, { $or: [ diff --git a/packages/query-typegoose/src/providers.ts b/packages/query-typegoose/src/providers.ts index 54b870056..2b1e642a4 100644 --- a/packages/query-typegoose/src/providers.ts +++ b/packages/query-typegoose/src/providers.ts @@ -50,6 +50,5 @@ function createTypegooseQueryServiceProvider( }; } -export const createTypegooseQueryServiceProviders = ( - models: (TypegooseClass | TypegooseClassWithOptions)[] -): FactoryProvider[] => models.map((model) => createTypegooseQueryServiceProvider(model)); +export const createTypegooseQueryServiceProviders = (models: (TypegooseClass | TypegooseClassWithOptions)[]): FactoryProvider[] => + models.map((model) => createTypegooseQueryServiceProvider(model)); diff --git a/packages/query-typegoose/src/query/where.builder.ts b/packages/query-typegoose/src/query/where.builder.ts index fc6ee123f..cb13af361 100644 --- a/packages/query-typegoose/src/query/where.builder.ts +++ b/packages/query-typegoose/src/query/where.builder.ts @@ -82,9 +82,7 @@ export class WhereBuilder { return this.comparisonBuilder.build(field, cmpType, cmp[cmpType] as EntityComparisonField); } return { - $or: opts.map((cmpType) => - this.comparisonBuilder.build(field, cmpType, cmp[cmpType] as EntityComparisonField) - ) + $or: opts.map((cmpType) => this.comparisonBuilder.build(field, cmpType, cmp[cmpType] as EntityComparisonField)) } as mongoose.FilterQuery; } } diff --git a/packages/query-typegoose/src/services/reference-query.service.ts b/packages/query-typegoose/src/services/reference-query.service.ts index 8c4e00528..3e4dfd971 100644 --- a/packages/query-typegoose/src/services/reference-query.service.ts +++ b/packages/query-typegoose/src/services/reference-query.service.ts @@ -239,9 +239,7 @@ export abstract class ReferenceQueryService { throw new Error(`Unable to find all ${relationName} to set on ${this.Model.modelName}`); } - return this.findAndUpdate(id, opts?.filter, { [relationName]: relationIds } as mongoose.UpdateQuery< - DocumentType - >); + return this.findAndUpdate(id, opts?.filter, { [relationName]: relationIds } as mongoose.UpdateQuery>); } public async setRelation( @@ -256,9 +254,7 @@ export abstract class ReferenceQueryService { throw new Error(`Unable to find ${relationName} to set on ${this.Model.modelName}`); } - return this.findAndUpdate(id, opts?.filter, { [relationName]: relationId } as mongoose.UpdateQuery< - DocumentType - >); + return this.findAndUpdate(id, opts?.filter, { [relationName]: relationId } as mongoose.UpdateQuery>); } public async removeRelation( @@ -316,11 +312,7 @@ export abstract class ReferenceQueryService { return !!this.Model.schema.virtualpath(refName); } - private getReferenceFilter( - refName: string, - entity: Entity, - filter?: Filter - ): Filter | undefined { + private getReferenceFilter(refName: string, entity: Entity, filter?: Filter): Filter | undefined { if (this.isReferencePath(refName)) { return this.getObjectIdReferenceFilter(refName, entity, filter); } @@ -342,11 +334,7 @@ export abstract class ReferenceQueryService { return mergeFilter(filter ?? ({} as Filter), refFilter); } - private getVirtualReferenceFilter( - virtualType: VirtualTypeWithOptions, - entity: Entity, - filter?: Filter - ): Filter { + private getVirtualReferenceFilter(virtualType: VirtualTypeWithOptions, entity: Entity, filter?: Filter): Filter { const { foreignField, localField } = virtualType.options; const refVal = entity[localField as keyof Entity]; const isArray = Array.isArray(refVal); diff --git a/packages/query-typegoose/src/services/typegoose-query-service.ts b/packages/query-typegoose/src/services/typegoose-query-service.ts index 6053d1808..fb61596ac 100644 --- a/packages/query-typegoose/src/services/typegoose-query-service.ts +++ b/packages/query-typegoose/src/services/typegoose-query-service.ts @@ -24,10 +24,7 @@ export interface TypegooseQueryServiceOpts { toObjectOptions?: mongoose.ToObjectOptions; } -export class TypegooseQueryService - extends ReferenceQueryService - implements QueryService -{ +export class TypegooseQueryService extends ReferenceQueryService implements QueryService { constructor( readonly Model: ReturnModelType Entity>, readonly filterQueryBuilder: FilterQueryBuilder = new FilterQueryBuilder(Model) @@ -54,10 +51,7 @@ export class TypegooseQueryService return entities; } - async aggregate( - filter: Filter, - aggregateQuery: AggregateQuery - ): Promise[]> { + async aggregate(filter: Filter, aggregateQuery: AggregateQuery): Promise[]> { const { aggregate, filterQuery, options } = this.filterQueryBuilder.buildAggregateQuery(aggregateQuery, filter); const aggPipeline: PipelineStage[] = [{ $match: filterQuery }, { $group: aggregate }]; if (options.sort) { @@ -157,11 +151,7 @@ export class TypegooseQueryService * @param update - A `Partial` of the entity with fields to update. * @param opts - Additional options */ - async updateOne( - id: string, - update: DeepPartial, - opts?: UpdateOneOptions - ): Promise> { + async updateOne(id: string, update: DeepPartial, opts?: UpdateOneOptions): Promise> { this.ensureIdIsNotPresent(update); const filterQuery = this.filterQueryBuilder.buildIdFilterQuery(id, opts?.filter); const doc = await this.Model.findOneAndUpdate(filterQuery, this.getUpdateQuery(update as DocumentType), { @@ -260,10 +250,7 @@ export class TypegooseQueryService } as UpdateArrayQuery; Object.keys(entity).forEach((key) => { - if ( - this.Model.schema.path(key) instanceof mongoose.Schema.Types.Array && - typeof entity[key as keyof Entity] === 'object' - ) { + if (this.Model.schema.path(key) instanceof mongoose.Schema.Types.Array && typeof entity[key as keyof Entity] === 'object') { // Converting the type of the object as it has the custom array input type. const convert = entity[key as keyof Entity] as unknown as { push: Entity[]; pull: Entity[] }; diff --git a/packages/query-typegoose/src/typegoose-types.helper.ts b/packages/query-typegoose/src/typegoose-types.helper.ts index 983db6468..460e1aaca 100644 --- a/packages/query-typegoose/src/typegoose-types.helper.ts +++ b/packages/query-typegoose/src/typegoose-types.helper.ts @@ -63,11 +63,7 @@ export type VirtualReferenceOptions = { export function isVirtualReferenceOptions(options: unknown): options is VirtualReferenceOptions { return ( - typeof options === 'object' && - options !== null && - 'ref' in options && - 'localField' in options && - 'foreignField' in options + typeof options === 'object' && options !== null && 'ref' in options && 'localField' in options && 'foreignField' in options ); } diff --git a/packages/query-typeorm/__tests__/__fixtures__/seeds.ts b/packages/query-typeorm/__tests__/__fixtures__/seeds.ts index 4bbd61c56..47c1242bf 100644 --- a/packages/query-typeorm/__tests__/__fixtures__/seeds.ts +++ b/packages/query-typeorm/__tests__/__fixtures__/seeds.ts @@ -78,9 +78,7 @@ export const seed = async (connection: Connection = getConnection()): Promise ({ ...r })) ); - await relationOfTestRelationRepo.save( - TEST_RELATIONS_OF_RELATION.map((r: RelationOfTestRelationEntity) => ({ ...r })) - ); + await relationOfTestRelationRepo.save(TEST_RELATIONS_OF_RELATION.map((r: RelationOfTestRelationEntity) => ({ ...r }))); await Promise.all( testEntities.map((te) => { @@ -102,9 +100,7 @@ export const seed = async (connection: Connection = getConnection()): Promise { - const relationOfTestRelationEntity = TEST_RELATIONS_OF_RELATION.find( - (r) => r.testRelationId === te.testRelationPk - ); + const relationOfTestRelationEntity = TEST_RELATIONS_OF_RELATION.find((r) => r.testRelationId === te.testRelationPk); te.relationOfTestRelationId = relationOfTestRelationEntity?.id; return testRelationRepo.save(te); }) diff --git a/packages/query-typeorm/__tests__/__fixtures__/test.entity.ts b/packages/query-typeorm/__tests__/__fixtures__/test.entity.ts index 517496947..25b67e45e 100644 --- a/packages/query-typeorm/__tests__/__fixtures__/test.entity.ts +++ b/packages/query-typeorm/__tests__/__fixtures__/test.entity.ts @@ -1,14 +1,4 @@ -import { - Column, - Entity, - OneToMany, - ManyToMany, - JoinTable, - OneToOne, - JoinColumn, - PrimaryColumn, - ManyToOne -} from 'typeorm'; +import { Column, Entity, OneToMany, ManyToMany, JoinTable, OneToOne, JoinColumn, PrimaryColumn, ManyToOne } from 'typeorm'; import { TestEntityRelationEntity } from './test-entity-relation.entity'; import { TestRelation } from './test-relation.entity'; import { TestSoftDeleteRelation } from './test-soft-delete.relation'; diff --git a/packages/query-typeorm/__tests__/query/filter-query.builder.spec.ts b/packages/query-typeorm/__tests__/query/filter-query.builder.spec.ts index 04d9b5d10..b6aae20f3 100644 --- a/packages/query-typeorm/__tests__/query/filter-query.builder.spec.ts +++ b/packages/query-typeorm/__tests__/query/filter-query.builder.spec.ts @@ -11,10 +11,8 @@ describe('FilterQueryBuilder', (): void => { beforeEach(createTestConnection); afterEach(closeTestConnection); - const getEntityQueryBuilder = ( - entity: Class, - whereBuilder: WhereBuilder - ): FilterQueryBuilder => new FilterQueryBuilder(getTestConnection().getRepository(entity), whereBuilder); + const getEntityQueryBuilder = (entity: Class, whereBuilder: WhereBuilder): FilterQueryBuilder => + new FilterQueryBuilder(getTestConnection().getRepository(entity), whereBuilder); const expectSQLSnapshot = (query: QueryBuilder): void => { const [sql, params] = query.getQueryAndParameters(); @@ -136,10 +134,7 @@ describe('FilterQueryBuilder', (): void => { describe('with sorting', () => { it('should apply ASC sorting', () => { const mockWhereBuilder = mock>(WhereBuilder); - expectSelectSQLSnapshot( - { sorting: [{ field: 'numberType', direction: SortDirection.ASC }] }, - instance(mockWhereBuilder) - ); + expectSelectSQLSnapshot({ sorting: [{ field: 'numberType', direction: SortDirection.ASC }] }, instance(mockWhereBuilder)); verify(mockWhereBuilder.build(anything(), anything(), {}, 'TestEntity')).never(); }); @@ -215,8 +210,8 @@ describe('FilterQueryBuilder', (): void => { it('should call whereBuilder#build if there is a filter', () => { const mockWhereBuilder = mock>(WhereBuilder); const query = { filter: { stringType: { eq: 'foo' } } }; - when(mockWhereBuilder.build(anything(), query.filter, deepEqual({}), undefined)).thenCall( - (where: WhereExpression) => where.andWhere(`stringType = 'foo'`) + when(mockWhereBuilder.build(anything(), query.filter, deepEqual({}), undefined)).thenCall((where: WhereExpression) => + where.andWhere(`stringType = 'foo'`) ); expectUpdateSQLSnapshot(query, instance(mockWhereBuilder)); }); @@ -232,10 +227,7 @@ describe('FilterQueryBuilder', (): void => { describe('with sorting', () => { it('should apply ASC sorting', () => { const mockWhereBuilder = mock>(WhereBuilder); - expectUpdateSQLSnapshot( - { sorting: [{ field: 'numberType', direction: SortDirection.ASC }] }, - instance(mockWhereBuilder) - ); + expectUpdateSQLSnapshot({ sorting: [{ field: 'numberType', direction: SortDirection.ASC }] }, instance(mockWhereBuilder)); verify(mockWhereBuilder.build(anything(), anything(), anything())).never(); }); @@ -312,8 +304,8 @@ describe('FilterQueryBuilder', (): void => { it('should call whereBuilder#build if there is a filter', () => { const mockWhereBuilder = mock>(WhereBuilder); const query = { filter: { stringType: { eq: 'foo' } } }; - when(mockWhereBuilder.build(anything(), query.filter, deepEqual({}), undefined)).thenCall( - (where: WhereExpression) => where.andWhere(`stringType = 'foo'`) + when(mockWhereBuilder.build(anything(), query.filter, deepEqual({}), undefined)).thenCall((where: WhereExpression) => + where.andWhere(`stringType = 'foo'`) ); expectDeleteSQLSnapshot(query, instance(mockWhereBuilder)); }); @@ -358,8 +350,8 @@ describe('FilterQueryBuilder', (): void => { it('should call whereBuilder#build if there is a filter', () => { const mockWhereBuilder = mock>(WhereBuilder); const query = { filter: { stringType: { eq: 'foo' } } }; - when(mockWhereBuilder.build(anything(), query.filter, deepEqual({}), undefined)).thenCall( - (where: WhereExpression) => where.andWhere(`stringType = 'foo'`) + when(mockWhereBuilder.build(anything(), query.filter, deepEqual({}), undefined)).thenCall((where: WhereExpression) => + where.andWhere(`stringType = 'foo'`) ); expectSoftDeleteSQLSnapshot(query, instance(mockWhereBuilder)); }); diff --git a/packages/query-typeorm/__tests__/query/relation-query.builder.spec.ts b/packages/query-typeorm/__tests__/query/relation-query.builder.spec.ts index 0e2b0f1c4..69242e8ee 100644 --- a/packages/query-typeorm/__tests__/query/relation-query.builder.spec.ts +++ b/packages/query-typeorm/__tests__/query/relation-query.builder.spec.ts @@ -34,10 +34,7 @@ describe('RelationQueryBuilder', (): void => { relation: string, query: Query ): void => { - const selectQueryBuilder = getRelationQueryBuilder(EntityClass, relation).batchSelect( - entities, - query - ); + const selectQueryBuilder = getRelationQueryBuilder(EntityClass, relation).batchSelect(entities, query); const [sql, params] = selectQueryBuilder.getQueryAndParameters(); expect(formatSql(sql, { params })).toMatchSnapshot(); diff --git a/packages/query-typeorm/__tests__/query/sql-comparison.builder.spec.ts b/packages/query-typeorm/__tests__/query/sql-comparison.builder.spec.ts index ce7cdbc25..40fb3f960 100644 --- a/packages/query-typeorm/__tests__/query/sql-comparison.builder.spec.ts +++ b/packages/query-typeorm/__tests__/query/sql-comparison.builder.spec.ts @@ -19,9 +19,7 @@ describe('SQLComparisonBuilder', (): void => { it('should throw an error for an invalid comparison type', () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - expect(() => createSQLComparisonBuilder().build('stringType', 'bad', 'foo', 'TestEntity')).toThrow( - 'unknown operator "bad"' - ); + expect(() => createSQLComparisonBuilder().build('stringType', 'bad', 'foo', 'TestEntity')).toThrow('unknown operator "bad"'); }); describe('eq comparisons', () => { diff --git a/packages/query-typeorm/__tests__/query/where.builder.spec.ts b/packages/query-typeorm/__tests__/query/where.builder.spec.ts index 55b705c40..416cdb89c 100644 --- a/packages/query-typeorm/__tests__/query/where.builder.spec.ts +++ b/packages/query-typeorm/__tests__/query/where.builder.spec.ts @@ -34,12 +34,7 @@ describe('WhereBuilder', (): void => { describe('and', (): void => { it('and multiple expressions together', (): void => { expectSQLSnapshot({ - and: [ - { numberType: { gt: 10 } }, - { numberType: { lt: 20 } }, - { numberType: { gte: 30 } }, - { numberType: { lte: 40 } } - ] + and: [{ numberType: { gt: 10 } }, { numberType: { lt: 20 } }, { numberType: { gte: 30 } }, { numberType: { lte: 40 } }] }); }); @@ -69,12 +64,7 @@ describe('WhereBuilder', (): void => { describe('or', (): void => { it('or multiple expressions together', (): void => { expectSQLSnapshot({ - or: [ - { numberType: { gt: 10 } }, - { numberType: { lt: 20 } }, - { numberType: { gte: 30 } }, - { numberType: { lte: 40 } } - ] + or: [{ numberType: { gt: 10 } }, { numberType: { lt: 20 } }, { numberType: { gte: 30 } }, { numberType: { lte: 40 } }] }); }); 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 be4b3046a..4c8128b31 100644 --- a/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts +++ b/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts @@ -261,13 +261,7 @@ describe('TypeOrmQueryService', (): void => { } } }); - expect(queryResult).toEqual([ - TEST_ENTITIES[1], - TEST_ENTITIES[3], - TEST_ENTITIES[5], - TEST_ENTITIES[7], - TEST_ENTITIES[9] - ]); + expect(queryResult).toEqual([TEST_ENTITIES[1], TEST_ENTITIES[3], TEST_ENTITIES[5], TEST_ENTITIES[7], TEST_ENTITIES[9]]); }); it('should allow filtering on a many to many uni-directional relation', async () => { @@ -638,12 +632,12 @@ describe('TypeOrmQueryService', (): void => { it('call select and return the with a uni-directional relation', async () => { const entity = TEST_ENTITIES[2]; const queryService = moduleRef.get(TestEntityService); - const queryResult = ( - await queryService.queryRelations(TestRelation, 'manyToManyUniDirectional', entity, {}) - ).map((r) => { - delete r.relationOfTestRelationId; - return r; - }); + const queryResult = (await queryService.queryRelations(TestRelation, 'manyToManyUniDirectional', entity, {})).map( + (r) => { + delete r.relationOfTestRelationId; + return r; + } + ); TEST_RELATIONS.filter((tr) => tr.relationName.endsWith('three')).forEach((tr) => { expect(queryResult).toContainEqual(tr); @@ -700,12 +694,7 @@ describe('TypeOrmQueryService', (): void => { describe('manyToMany', () => { it('call select and return the with owning side of the relations', async () => { const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations( - TestEntity, - 'manyTestRelations', - [TEST_ENTITIES[1]], - {} - ); + const queryResult = await queryService.queryRelations(TestEntity, 'manyTestRelations', [TEST_ENTITIES[1]], {}); const adaptedQueryResult = new Map(); queryResult.forEach((relations, key) => { @@ -1205,12 +1194,9 @@ describe('TypeOrmQueryService', (): void => { it('call select and return undefined', async () => { const entity = TEST_ENTITIES[0]; const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.findRelation( - TestSoftDeleteRelation, - 'oneSoftDeleteTestRelation', - entity, - { withDeleted: false } - ); + const queryResult = await queryService.findRelation(TestSoftDeleteRelation, 'oneSoftDeleteTestRelation', entity, { + withDeleted: false + }); expect(queryResult).toBeUndefined(); }); @@ -1218,12 +1204,9 @@ describe('TypeOrmQueryService', (): void => { it('call select and return the deleted relation', async () => { const entity = TEST_ENTITIES[0]; const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.findRelation( - TestSoftDeleteRelation, - 'oneSoftDeleteTestRelation', - entity, - { withDeleted: true } - ); + const queryResult = await queryService.findRelation(TestSoftDeleteRelation, 'oneSoftDeleteTestRelation', entity, { + withDeleted: true + }); expect(queryResult).toEqual({ ...TEST_SOFT_DELETE_RELATION_ENTITIES[0], @@ -1297,12 +1280,9 @@ describe('TypeOrmQueryService', (): void => { it('call select and return undefined', async () => { const entities = [TEST_ENTITIES[0]]; const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.findRelation( - TestSoftDeleteRelation, - 'oneSoftDeleteTestRelation', - entities, - { withDeleted: false } - ); + const queryResult = await queryService.findRelation(TestSoftDeleteRelation, 'oneSoftDeleteTestRelation', entities, { + withDeleted: false + }); expect(queryResult).toEqual(new Map([[entities[0], undefined]])); }); @@ -1310,12 +1290,9 @@ describe('TypeOrmQueryService', (): void => { it('call select and return the deleted relation', async () => { const entities = [TEST_ENTITIES[0]]; const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.findRelation( - TestSoftDeleteRelation, - 'oneSoftDeleteTestRelation', - entities, - { withDeleted: true } - ); + const queryResult = await queryService.findRelation(TestSoftDeleteRelation, 'oneSoftDeleteTestRelation', entities, { + withDeleted: true + }); expect(queryResult).toEqual( new Map([ @@ -1476,14 +1453,9 @@ describe('TypeOrmQueryService', (): void => { const entity = TEST_ENTITIES[0]; const queryService = moduleRef.get(TestEntityService); return expect( - queryService.setRelation( - 'oneTestRelation', - entity.testEntityPk, - TEST_RELATIONS[1].testRelationPk, - { - relationFilter: { relationName: { like: '%-one' } } - } - ) + queryService.setRelation('oneTestRelation', entity.testEntityPk, TEST_RELATIONS[1].testRelationPk, { + relationFilter: { relationName: { like: '%-one' } } + }) ).rejects.toThrow('Unable to find oneTestRelation to set on TestEntity'); }); }); @@ -1578,14 +1550,9 @@ describe('TypeOrmQueryService', (): void => { const entity = TEST_ENTITIES[0]; const queryService = moduleRef.get(TestEntityService); return expect( - queryService.removeRelation( - 'oneTestRelation', - entity.testEntityPk, - TEST_RELATIONS[1].testRelationPk, - { - relationFilter: { relationName: { like: '%-one' } } - } - ) + queryService.removeRelation('oneTestRelation', entity.testEntityPk, TEST_RELATIONS[1].testRelationPk, { + relationFilter: { relationName: { like: '%-one' } } + }) ).rejects.toThrow('Unable to find oneTestRelation to remove from TestEntity'); }); }); @@ -1659,14 +1626,9 @@ describe('TypeOrmQueryService', (): void => { const entity = TEST_ENTITIES[0]; const queryService = moduleRef.get(TestEntityService); return expect( - queryService.removeRelation( - 'testRelations', - entity.testEntityPk, - TEST_RELATIONS[4].testRelationPk, - { - relationFilter: { relationName: { like: '%-one' } } - } - ) + queryService.removeRelation('testRelations', entity.testEntityPk, TEST_RELATIONS[4].testRelationPk, { + relationFilter: { relationName: { like: '%-one' } } + }) ).rejects.toThrow('Unable to find testRelations to remove from TestEntity'); }); }); @@ -1953,9 +1915,7 @@ describe('TypeOrmQueryService', (): void => { it('should fail if the entity is not found', async () => { const queryService = moduleRef.get(TestSoftDeleteEntityService); - return expect(queryService.deleteOne('bad-id')).rejects.toThrow( - 'Unable to find TestSoftDeleteEntity with id: bad-id' - ); + return expect(queryService.deleteOne('bad-id')).rejects.toThrow('Unable to find TestSoftDeleteEntity with id: bad-id'); }); }); @@ -1972,9 +1932,7 @@ describe('TypeOrmQueryService', (): void => { it('should fail if the entity is not found', async () => { const queryService = moduleRef.get(TestSoftDeleteEntityService); - return expect(queryService.restoreOne('bad-id')).rejects.toThrow( - 'Unable to find TestSoftDeleteEntity with id: bad-id' - ); + return expect(queryService.restoreOne('bad-id')).rejects.toThrow('Unable to find TestSoftDeleteEntity with id: bad-id'); }); it('should fail if the useSoftDelete is not enabled', async () => { diff --git a/packages/query-typeorm/src/query/aggregate.builder.ts b/packages/query-typeorm/src/query/aggregate.builder.ts index 3b4040e2c..ade72f1c8 100644 --- a/packages/query-typeorm/src/query/aggregate.builder.ts +++ b/packages/query-typeorm/src/query/aggregate.builder.ts @@ -101,10 +101,7 @@ export class AggregateBuilder { throw new BadRequestException('No aggregate fields found.'); } const [head, ...tail] = selects; - return tail.reduce( - (acc: Qb, [select, selectAlias]) => acc.addSelect(select, selectAlias), - qb.select(head[0], head[1]) - ); + return tail.reduce((acc: Qb, [select, selectAlias]) => acc.addSelect(select, selectAlias), qb.select(head[0], head[1])); } private createAggSelect(func: AggregateFuncs, fields?: (keyof Entity)[], alias?: string): [string, string][] { diff --git a/packages/query-typeorm/src/query/filter-query.builder.ts b/packages/query-typeorm/src/query/filter-query.builder.ts index 0ea334072..f15905ed5 100644 --- a/packages/query-typeorm/src/query/filter-query.builder.ts +++ b/packages/query-typeorm/src/query/filter-query.builder.ts @@ -113,10 +113,7 @@ export class FilterQueryBuilder { * @param query - the query to apply. */ softDelete(query: Query): SoftDeleteQueryBuilder { - return this.applyFilter( - this.repo.createQueryBuilder().softDelete() as SoftDeleteQueryBuilder, - query.filter - ); + return this.applyFilter(this.repo.createQueryBuilder().softDelete() as SoftDeleteQueryBuilder, query.filter); } /** diff --git a/packages/query-typeorm/src/query/relation-query.builder.ts b/packages/query-typeorm/src/query/relation-query.builder.ts index 35d9f8e23..38cd0e9da 100644 --- a/packages/query-typeorm/src/query/relation-query.builder.ts +++ b/packages/query-typeorm/src/query/relation-query.builder.ts @@ -156,11 +156,7 @@ export class RelationQueryBuilder { aggregateQuery.groupBy, relationBuilder.alias ); - relationBuilder = this.filterQueryBuilder.applyGroupBy( - relationBuilder, - aggregateQuery.groupBy, - relationBuilder.alias - ); + relationBuilder = this.filterQueryBuilder.applyGroupBy(relationBuilder, aggregateQuery.groupBy, relationBuilder.alias); return relationBuilder; } @@ -424,13 +420,7 @@ export class RelationQueryBuilder { joins, mapRelations: (entity: Entity, relations: Relation[], rawRelations: RawRelation[]): Relation[] => { - return this.batchMapRelationsManyToMany( - joinAlias, - relation.joinColumns, - entity, - relations, - rawRelations - ); + return this.batchMapRelationsManyToMany(joinAlias, relation.joinColumns, entity, relations, rawRelations); }, batchSelect: (qb, entities: Entity[]) => { diff --git a/packages/query-typeorm/src/query/sql-comparison.builder.ts b/packages/query-typeorm/src/query/sql-comparison.builder.ts index 1d02688a0..db626200f 100644 --- a/packages/query-typeorm/src/query/sql-comparison.builder.ts +++ b/packages/query-typeorm/src/query/sql-comparison.builder.ts @@ -168,10 +168,7 @@ export class SQLComparisonBuilder { throw new Error(`Invalid value for between expected {lower: val, upper: val} got ${JSON.stringify(val)}`); } - private notBetweenComparisonSQL( - col: string, - val: EntityComparisonField - ): CmpSQLType { + private notBetweenComparisonSQL(col: string, val: EntityComparisonField): CmpSQLType { if (this.isBetweenVal(val)) { const { paramName: lowerParamName } = this; const { paramName: upperParamName } = this; diff --git a/packages/query-typeorm/src/query/where.builder.ts b/packages/query-typeorm/src/query/where.builder.ts index a3d821019..56f4e9b91 100644 --- a/packages/query-typeorm/src/query/where.builder.ts +++ b/packages/query-typeorm/src/query/where.builder.ts @@ -17,12 +17,7 @@ export class WhereBuilder { * @param relationNames - the relations tree. * @param alias - optional alias to use to qualify an identifier */ - build( - where: Where, - filter: Filter, - relationNames: NestedRecord, - alias?: string - ): Where { + build(where: Where, filter: Filter, relationNames: NestedRecord, alias?: string): Where { const { and, or } = filter; if (and && and.length) { this.filterAnd(where, and, relationNames, alias); diff --git a/packages/query-typeorm/src/services/relation-query.service.ts b/packages/query-typeorm/src/services/relation-query.service.ts index 8a98ccc3d..97bd87df2 100644 --- a/packages/query-typeorm/src/services/relation-query.service.ts +++ b/packages/query-typeorm/src/services/relation-query.service.ts @@ -34,7 +34,8 @@ export abstract class RelationQueryService { abstract getById(id: string | number, opts?: GetByIdOptions): Promise; /** - * Query for relations for an array of Entities. This method will return a map with the Entity as the key and the relations as the value. + * Query for relations for an array of Entities. This method will return a map with + * the Entity as the key and the relations as the value. * @param RelationClass - The class of the relation. * @param relationName - The name of the relation to load. * @param entities - the dtos to find relations for. @@ -341,16 +342,10 @@ export abstract class RelationQueryService { const convertedQuery = assembler.convertQuery(query); const relationQueryBuilder = this.getRelationQueryBuilder(relationName); - const entityRelations = await relationQueryBuilder - .batchSelect(entities, convertedQuery, withDeleted) - .getRawAndEntities(); + const entityRelations = await relationQueryBuilder.batchSelect(entities, convertedQuery, withDeleted).getRawAndEntities(); return entities.reduce((results, entity) => { - const relations = relationQueryBuilder.relationMeta.mapRelations( - entity, - entityRelations.entities, - entityRelations.raw - ); + const relations = relationQueryBuilder.relationMeta.mapRelations(entity, entityRelations.entities, entityRelations.raw); return results.set(entity, assembler.convertToDTOs(relations)); }, new Map()); @@ -386,9 +381,7 @@ export abstract class RelationQueryService { const resultingAgg = results.get(e) ?? []; results.set(e, [ ...resultingAgg, - ...AggregateBuilder.convertToAggregateResponse([ - lodashOmit(relationAgg, relationQueryBuilder.entityIndexColName) - ]) + ...AggregateBuilder.convertToAggregateResponse([lodashOmit(relationAgg, relationQueryBuilder.entityIndexColName)]) ]); return results; }, new Map[]>()); @@ -411,9 +404,7 @@ export abstract class RelationQueryService { const relationQueryBuilder = this.getRelationQueryBuilder(relationName); const convertedQuery = assembler.convertQuery({ filter }); - const entityRelations = await Promise.all( - entities.map((e) => relationQueryBuilder.select(e, convertedQuery).getCount()) - ); + const entityRelations = await Promise.all(entities.map((e) => relationQueryBuilder.select(e, convertedQuery).getCount())); return entityRelations.reduce((results, relationCount, index) => { const e = entities[index]; @@ -477,11 +468,7 @@ export abstract class RelationQueryService { return relationMeta.type as Class; } - private getRelations( - relationName: string, - ids: (string | number)[], - filter?: Filter - ): Promise { + private getRelations(relationName: string, ids: (string | number)[], filter?: Filter): Promise { const relationQueryBuilder = this.getRelationQueryBuilder(relationName).filterQueryBuilder; return relationQueryBuilder.selectById(ids, { filter }).getMany(); } From 9a8d9d0d1b152e1b876d0a2bc30a4a37ccdf2e02 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Fri, 27 May 2022 17:33:15 +0200 Subject: [PATCH 4/8] docs(query-graphql): Updated some jsdoc --- .../query-graphql/src/types/query/query-args/interfaces.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/query-graphql/src/types/query/query-args/interfaces.ts b/packages/query-graphql/src/types/query/query-args/interfaces.ts index e527e5f37..8d691f75a 100644 --- a/packages/query-graphql/src/types/query/query-args/interfaces.ts +++ b/packages/query-graphql/src/types/query/query-args/interfaces.ts @@ -20,7 +20,7 @@ export type BaseQueryArgsTypeOpts = { */ defaultSort?: SortField[]; /** - * Disable the sorting of this relation. + * Disable the sorting */ disableSort?: boolean; /** @@ -29,7 +29,7 @@ export type BaseQueryArgsTypeOpts = { */ defaultFilter?: Filter; /** - * Disable the filtering of this relation. + * Disable the filtering */ disableFilter?: boolean; } & FilterTypeOptions; From 81c257fa0afe2d0d1422379cac03b9ba2e2aca31 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Fri, 27 May 2022 17:35:16 +0200 Subject: [PATCH 5/8] chore: Bump versions --- packages/core/package.json | 2 +- packages/query-graphql/package.json | 2 +- packages/query-typeorm/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 6c6b2aeca..6b807e4ba 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@ptc-org/nestjs-query-core", - "version": "1.0.0-alpha.1", + "version": "1.0.0-alpha.2", "description": "Base query package", "author": "doug-martin ", "homepage": "https://github.com/tripss/nestjs-query#readme", diff --git a/packages/query-graphql/package.json b/packages/query-graphql/package.json index f160370f3..ae3205b73 100644 --- a/packages/query-graphql/package.json +++ b/packages/query-graphql/package.json @@ -1,6 +1,6 @@ { "name": "@ptc-org/nestjs-query-graphql", - "version": "1.0.0-alpha.2", + "version": "1.0.0-alpha.3", "description": "Nestjs graphql query adapter", "author": "doug-martin ", "homepage": "https://github.com/tripss/nestjs-query#readme", diff --git a/packages/query-typeorm/package.json b/packages/query-typeorm/package.json index aaf09204f..3181be2b4 100644 --- a/packages/query-typeorm/package.json +++ b/packages/query-typeorm/package.json @@ -1,6 +1,6 @@ { "name": "@ptc-org/nestjs-query-typeorm", - "version": "1.0.0-alpha.3", + "version": "1.0.0-alpha.4", "description": "Typeorm adapter for @ptc-org/nestjs-query-core", "author": "doug-martin ", "homepage": "https://github.com/tripss/nestjs-query#readme", From 6b3722be5b0c44d51831d39263ecb0fbccbc72a2 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Fri, 27 May 2022 17:49:10 +0200 Subject: [PATCH 6/8] test(query-typegoose): Re-enable more tests --- ...egoose-query-descriminated.service.spec.ts | 1590 ++++++++++++++++ ...ery-descriminated.service.spec.ts.disabled | 1636 ----------------- .../services/typegoose-query.service.spec.ts | 1560 ++++++++++++++++ .../typegoose-query.service.spec.ts.disabled | 1564 ---------------- 4 files changed, 3150 insertions(+), 3200 deletions(-) create mode 100644 packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts delete mode 100644 packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts.disabled create mode 100644 packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts delete mode 100644 packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts.disabled diff --git a/packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts b/packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts new file mode 100644 index 000000000..1e0a7c44c --- /dev/null +++ b/packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts @@ -0,0 +1,1590 @@ +/* eslint-disable no-underscore-dangle,@typescript-eslint/no-unsafe-return */ +import { getModelForClass, DocumentType, mongoose } from '@typegoose/typegoose'; +import { Test, TestingModule } from '@nestjs/testing'; +import { ReturnModelType } from '@typegoose/typegoose/lib/types'; +import { InjectModel, TypegooseModule } from 'nestjs-typegoose'; +import { FindRelationOptions, SortDirection } from '@ptc-org/nestjs-query-core'; +import { + TestReference, + TestEntity, + TEST_REFERENCES_FOR_DISCRIMINATES, + TEST_DISCRIMINATED_ENTITIES, + getConnectionUri, + prepareDb, + closeDbConnection, + dropDatabase +} from '../__fixtures__'; +import { NestjsQueryTypegooseModule } from '../../src'; +import { TypegooseQueryService } from '../../src/services'; +import { TestDiscriminatedEntity } from '../__fixtures__/test-discriminated.entity'; + +const { Types } = mongoose; + +describe('TypegooseQueryService', () => { + let moduleRef: TestingModule; + let TestReferenceModel: ReturnModelType; + let TestDiscriminatedEntityModel: ReturnModelType; + + class TestReferenceService extends TypegooseQueryService { + constructor(@InjectModel(TestReference) readonly model: ReturnModelType) { + super(model); + TestReferenceModel = model; + } + } + + class TestDiscriminatedEntityService extends TypegooseQueryService { + constructor(@InjectModel(TestDiscriminatedEntity) readonly model: ReturnModelType) { + super(model); + TestDiscriminatedEntityModel = model; + } + } + + beforeAll(async () => { + moduleRef = await Test.createTestingModule({ + imports: [ + TypegooseModule.forRoot(await getConnectionUri()), + NestjsQueryTypegooseModule.forFeature([ + { + typegooseClass: TestEntity, + discriminators: [TestDiscriminatedEntity] + }, + TestReference + ]) + ], + providers: [TestDiscriminatedEntityService, TestReferenceService] + }).compile(); + }); + + function convertDocument(doc: DocumentType): Doc { + return doc.toObject({ virtuals: true }) as Doc; + } + + function convertDocuments(docs: DocumentType[]): Doc[] { + return docs.map((doc) => convertDocument(doc)); + } + + function testDiscriminatedEntityToObject(tde: TestDiscriminatedEntity): Partial { + return { + _id: tde._id, + stringType: tde.stringType, + boolType: tde.boolType, + numberType: tde.numberType, + dateType: tde.dateType, + stringType2: tde.stringType2 + }; + } + + function testDiscriminatedEntityToCreate(tde: TestDiscriminatedEntity): Partial { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { _id, ...insert } = testDiscriminatedEntityToObject(tde); + return insert; + } + + function expectEqualCreate(result: TestDiscriminatedEntity[], expected: TestDiscriminatedEntity[]) { + const cleansedResults = result.map(testDiscriminatedEntityToCreate); + const cleansedExpected = expected.map(testDiscriminatedEntityToCreate); + expect(cleansedResults).toEqual(cleansedExpected); + } + + afterAll(async () => closeDbConnection()); + + beforeEach(async () => prepareDb()); + + afterEach(async () => dropDatabase()); + + describe('#query with discriminated entity', () => { + it('call find and return the result', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({}); + + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES)); + }); + + it('should support eq operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { stringType: { eq: 'foo11' } } }); + expect(convertDocuments(queryResult)).toEqual([TEST_DISCRIMINATED_ENTITIES[0]]); + }); + + it('should support neq operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { stringType: { neq: 'foo1' } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(1))); + }); + + it('should support gt operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { numberType: { gt: 5 } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(5))); + }); + + it('should support gte operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { numberType: { gte: 5 } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(4))); + }); + + it('should support lt operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { numberType: { lt: 15 } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(0, 4))); + }); + + it('should support lte operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { numberType: { lte: 15 } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(0, 5))); + }); + + it('should support in operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { numberType: { in: [11, 12, 13] } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(0, 3))); + }); + + it('should support notIn operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { numberType: { notIn: [11, 12, 13] } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(4))); + }); + + it('should support is operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { boolType: { is: true } } }); + expect(convertDocuments(queryResult)).toEqual( + expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.filter((e) => e.boolType)) + ); + }); + + it('should support isNot operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { boolType: { isNot: true } } }); + expect(convertDocuments(queryResult)).toEqual( + expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.filter((e) => !e.boolType)) + ); + }); + + it('should support like operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { stringType: { like: 'foo%' } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES)); + }); + + it('should support notLike operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { stringType: { notLike: 'foo%' } } }); + expect(convertDocuments(queryResult)).toEqual([]); + }); + + it('should support iLike operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { stringType: { iLike: 'FOO%' } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES)); + }); + + it('should support notILike operator', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.query({ filter: { stringType: { notILike: 'FOO%' } } }); + expect(convertDocuments(queryResult)).toEqual([]); + }); + }); + + describe('#aggregate with discriminated entity', () => { + it('call select with the aggregate columns and return the result', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.aggregate( + {}, + { + count: ['id'], + avg: ['numberType'], + sum: ['numberType'], + max: ['id', 'dateType', 'numberType', 'stringType'], + min: ['id', 'dateType', 'numberType', 'stringType'] + } + ); + return expect(queryResult).toEqual([ + { + avg: { + numberType: 15.5 + }, + count: { + id: 10 + }, + max: { + dateType: TEST_DISCRIMINATED_ENTITIES[9].dateType, + numberType: 20, + stringType: 'foo20', + id: expect.any(Types.ObjectId) + }, + min: { + dateType: TEST_DISCRIMINATED_ENTITIES[0].dateType, + numberType: 11, + stringType: 'foo11', + id: expect.any(Types.ObjectId) + }, + sum: { + numberType: 155 + } + } + ]); + }); + + it('allow aggregates with groupBy', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.aggregate( + {}, + { + groupBy: ['boolType'], + count: ['id'], + avg: ['numberType'], + sum: ['numberType'], + max: ['id', 'dateType', 'numberType', 'stringType'], + min: ['id', 'dateType', 'numberType', 'stringType'] + } + ); + return expect(queryResult).toEqual([ + { + groupBy: { + boolType: false + }, + avg: { + numberType: 15 + }, + count: { + id: 5 + }, + max: { + dateType: TEST_DISCRIMINATED_ENTITIES[8].dateType, + numberType: 19, + stringType: 'foo19', + id: expect.any(Types.ObjectId) + }, + min: { + dateType: TEST_DISCRIMINATED_ENTITIES[0].dateType, + numberType: 11, + stringType: 'foo11', + id: expect.any(Types.ObjectId) + }, + sum: { + numberType: 75 + } + }, + { + groupBy: { + boolType: true + }, + avg: { + numberType: 16 + }, + count: { + id: 5 + }, + max: { + dateType: TEST_DISCRIMINATED_ENTITIES[9].dateType, + numberType: 20, + stringType: 'foo20', + id: expect.any(Types.ObjectId) + }, + min: { + dateType: TEST_DISCRIMINATED_ENTITIES[1].dateType, + numberType: 12, + stringType: 'foo12', + id: expect.any(Types.ObjectId) + }, + sum: { + numberType: 80 + } + } + ]); + }); + + it('call select with the aggregate columns and return the result with a filter', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const queryResult = await queryService.aggregate( + { stringType: { in: ['foo11', 'foo12', 'foo13'] } }, + { + count: ['id'], + avg: ['numberType'], + sum: ['numberType'], + max: ['id', 'dateType', 'numberType', 'stringType'], + min: ['id', 'dateType', 'numberType', 'stringType'] + } + ); + return expect(queryResult).toEqual([ + { + avg: { + numberType: 12 + }, + count: { + id: 3 + }, + max: { + dateType: TEST_DISCRIMINATED_ENTITIES[2].dateType, + numberType: 13, + stringType: 'foo13', + id: expect.any(Types.ObjectId) + }, + min: { + dateType: TEST_DISCRIMINATED_ENTITIES[0].dateType, + numberType: 11, + stringType: 'foo11', + id: expect.any(Types.ObjectId) + }, + sum: { + numberType: 36 + } + } + ]); + }); + }); + + describe('#count with discriminated entity', () => { + it('should return number of elements matching a query', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const expectedEntities = TEST_DISCRIMINATED_ENTITIES.slice(0, 2); + const count = await queryService.count({ stringType: { in: expectedEntities.map((e) => e.stringType) } }); + expect(count).toBe(2); + }); + }); + + describe('#findById with discriminated entity', () => { + it('return the entity if found', async () => { + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const found = await queryService.findById(entity._id.toString()); + expect(convertDocument(found!)).toEqual(entity); + }); + + it('return undefined if not found', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const found = await queryService.findById(new Types.ObjectId().toString()); + expect(found).toBeUndefined(); + }); + + describe('with filter on discriminated entity', () => { + it('should return an entity if all filters match', async () => { + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const found = await queryService.findById(entity._id.toString(), { + filter: { stringType: { eq: entity.stringType } } + }); + expect(convertDocument(found!)).toEqual(entity); + }); + + it('should return an undefined if an entity with the pk and filter is not found', async () => { + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const found = await queryService.findById(entity._id.toHexString(), { + filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } + }); + expect(found).toBeUndefined(); + }); + }); + }); + + describe('#getById with discriminated entity', () => { + it('return the entity if found', async () => { + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const found = await queryService.getById(entity._id.toHexString()); + expect(convertDocument(found)).toEqual(entity); + }); + + it('return undefined if not found', () => { + const badId = new Types.ObjectId().toHexString(); + const queryService = moduleRef.get(TestDiscriminatedEntityService); + return expect(queryService.getById(badId)).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${badId}`); + }); + + describe('with filter on discriminated entity', () => { + it('should return an entity if all filters match', async () => { + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const found = await queryService.getById(entity._id.toHexString(), { + filter: { stringType: { eq: entity.stringType } } + }); + expect(convertDocument(found)).toEqual(entity); + }); + + it('should return an undefined if an entity with the pk and filter is not found', async () => { + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const queryService = moduleRef.get(TestDiscriminatedEntityService); + return expect( + queryService.getById(entity._id.toHexString(), { + filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } + }) + ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); + }); + }); + }); + + describe('#createMany with discriminated entity', () => { + it('call save on the repo with instances of entities when passed plain objects', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const created = await queryService.createMany(TEST_DISCRIMINATED_ENTITIES.map(testDiscriminatedEntityToCreate)); + expectEqualCreate(created, TEST_DISCRIMINATED_ENTITIES); + }); + + it('call save on the repo with instances of entities when passed instances', async () => { + const instances = TEST_DISCRIMINATED_ENTITIES.map((e) => testDiscriminatedEntityToCreate(e)); + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const created = await queryService.createMany(instances); + expectEqualCreate(created, TEST_DISCRIMINATED_ENTITIES); + }); + + it('should reject if the entities already exist', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + return expect(queryService.createMany(TEST_DISCRIMINATED_ENTITIES)).rejects.toThrow( + 'Id cannot be specified when updating or creating' + ); + }); + }); + + describe('#createOne with discriminated entity', () => { + it('call save on the repo with an instance of the entity when passed a plain object', async () => { + const entity = testDiscriminatedEntityToCreate(TEST_DISCRIMINATED_ENTITIES[0]); + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const created = await queryService.createOne(entity); + expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); + }); + + it('call save on the repo with an instance of the entity when passed an instance', async () => { + const Model = getModelForClass(TestDiscriminatedEntity); + const entity = new Model(testDiscriminatedEntityToCreate(TEST_DISCRIMINATED_ENTITIES[0])); + const outcome = testDiscriminatedEntityToObject(entity); + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const created = await queryService.createOne(entity); + expect(convertDocument(created)).toEqual(expect.objectContaining(outcome)); + }); + + it('should reject if the entity contains an id', async () => { + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const queryService = moduleRef.get(TestDiscriminatedEntityService); + return expect(queryService.createOne({ ...entity })).rejects.toThrow('Id cannot be specified when updating or creating'); + }); + + it('should not reject if the entity contains an undefined id', async () => { + const entity = testDiscriminatedEntityToCreate(TEST_DISCRIMINATED_ENTITIES[0]); + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const created = await queryService.createOne({ ...entity, id: undefined }); + expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); + }); + + it('should not reject if the entity contains an undefined _id', async () => { + const entity = testDiscriminatedEntityToCreate(TEST_DISCRIMINATED_ENTITIES[0]); + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const created = await queryService.createOne({ ...entity, _id: undefined }); + expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); + }); + }); + + describe('#deleteMany with discriminated entity', () => { + it('delete all records that match the query', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 5); + const { deletedCount } = await queryService.deleteMany({ + stringType: { in: entities.map((e) => e.stringType) } + }); + expect(deletedCount).toBe(5); + const allCount = await queryService.count({}); + expect(allCount).toBe(5); + }); + }); + + describe('#deleteOne with discriminated entity', () => { + it('remove the entity', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const deleted = await queryService.deleteOne(TEST_DISCRIMINATED_ENTITIES[0]._id.toHexString()); + expect(convertDocument(deleted)).toEqual(TEST_DISCRIMINATED_ENTITIES[0]); + }); + + it('call fail if the entity is not found', async () => { + const badId = new Types.ObjectId().toHexString(); + const queryService = moduleRef.get(TestDiscriminatedEntityService); + return expect(queryService.deleteOne(badId)).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${badId}`); + }); + + describe('with filter on discriminated entity', () => { + it('should delete the entity if all filters match', async () => { + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const deleted = await queryService.deleteOne(entity._id.toHexString(), { + filter: { stringType: { eq: entity.stringType } } + }); + expect(convertDocument(deleted)).toEqual(TEST_DISCRIMINATED_ENTITIES[0]); + }); + + it('should return throw an error if unable to find', async () => { + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const queryService = moduleRef.get(TestDiscriminatedEntityService); + return expect( + queryService.deleteOne(entity._id.toHexString(), { + filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } + }) + ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); + }); + }); + }); + + describe('#updateMany with discriminated entity', () => { + it('update all entities in the filter', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const filter = { + stringType: { in: TEST_DISCRIMINATED_ENTITIES.slice(0, 5).map((e) => e.stringType) } + }; + await queryService.updateMany({ stringType: 'updated' }, filter); + const entities = await queryService.query({ filter: { stringType: { eq: 'updated' } } }); + expect(entities).toHaveLength(5); + }); + + it('should reject if the update contains the ID', () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + return expect(queryService.updateMany({ id: new Types.ObjectId().toHexString() }, {})).rejects.toThrow( + 'Id cannot be specified when updating' + ); + }); + + it('should not reject if the update contains an undefined id', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const entitiesToUpdate = TEST_DISCRIMINATED_ENTITIES.slice(0, 5); + const filter = { + stringType: { in: entitiesToUpdate.map((e) => e.stringType) } + }; + await queryService.updateMany({ stringType: 'updated', id: undefined }, filter); + const entities = await queryService.query({ filter: { stringType: { eq: 'updated' } } }); + expect(entities).toHaveLength(entitiesToUpdate.length); + expect(new Set(entities.map((e) => e.id))).toEqual(new Set(entitiesToUpdate.map((e) => e.id))); + }); + }); + + describe('#updateOne with discriminated entity', () => { + it('update the entity', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const update = { stringType: 'updated' }; + const updated = await queryService.updateOne(entity._id.toHexString(), update); + expect(updated).toEqual( + expect.objectContaining({ + _id: entity._id, + ...update + }) + ); + }); + + it('update the entity with an instance of the entity', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const Model = getModelForClass(TestDiscriminatedEntity); + const update = new Model({ stringType: 'updated' }); + const updated = await queryService.updateOne(entity._id.toHexString(), update); + expect(updated).toEqual( + expect.objectContaining({ + _id: entity._id, + stringType: 'updated' + }) + ); + }); + + it('should reject if the update contains the ID', async () => { + const queryService = moduleRef.get(TestDiscriminatedEntityService); + return expect( + queryService.updateOne(TEST_DISCRIMINATED_ENTITIES[0]._id.toHexString(), { _id: new Types.ObjectId() }) + ).rejects.toThrow('Id cannot be specified when updating'); + }); + + it('call fail if the entity is not found', async () => { + const badId = new Types.ObjectId().toHexString(); + const queryService = moduleRef.get(TestDiscriminatedEntityService); + return expect(queryService.updateOne(badId, { stringType: 'updated' })).rejects.toThrow( + `Unable to find TestDiscriminatedEntity with id: ${badId}` + ); + }); + + describe('with filter on discriminated entity', () => { + it('should update the entity if all filters match', async () => { + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const queryService = moduleRef.get(TestDiscriminatedEntityService); + const update = { stringType: 'updated' }; + const updated = await queryService.updateOne(entity._id.toHexString(), update, { + filter: { stringType: { eq: entity.stringType } } + }); + expect(updated).toEqual(expect.objectContaining({ _id: entity._id, ...update })); + }); + + it('should throw an error if unable to find the entity', async () => { + const entity = TEST_DISCRIMINATED_ENTITIES[0]; + const queryService = moduleRef.get(TestDiscriminatedEntityService); + return expect( + queryService.updateOne( + entity._id.toHexString(), + { stringType: 'updated' }, + { filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } } + ) + ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); + }); + }); + }); + + // describe('#findRelation with discriminated entity', () => { + // describe('with one entity', () => { + // it('call select and return the result', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.findRelation(TestReference, 'testReference', entity); + // expect(queryResult).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES[0]); + // }); + // + // it('apply the filter option', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult1 = await queryService.findRelation(TestReference, 'testReference', entity, { + // filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[0].referenceName } } + // }); + // expect(queryResult1).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES[0]); + // + // const queryResult2 = await queryService.findRelation(TestReference, 'testReference', entity, { + // filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName } } + // }); + // expect(queryResult2).toBeUndefined(); + // }); + // + // it('should return undefined select if no results are found.', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // await TestDiscriminatedEntityModel.updateOne({ _id: entity._id }, { $set: { testReference: undefined } }); + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.findRelation(TestReference, 'testReference', entity); + // expect(queryResult).toBeUndefined(); + // }); + // + // it('throw an error if a relation with that name is not found.', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // return expect(queryService.findRelation(TestReference, 'badReference', entity)).rejects.toThrow( + // 'Unable to find reference badReference on TestDiscriminatedEntity' + // ); + // }); + // + // describe('virtual reference', () => { + // it('call select and return the result', async () => { + // const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // const queryResult = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity); + // + // expect(queryResult).toEqual(TEST_DISCRIMINATED_ENTITIES[0]); + // }); + // + // it('apply the filter option', async () => { + // const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // const queryResult1 = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity, { + // filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[0].stringType } } + // }); + // expect(queryResult1).toEqual(TEST_DISCRIMINATED_ENTITIES[0]); + // + // const queryResult2 = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity, { + // filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } + // }); + // expect(queryResult2).toBeUndefined(); + // }); + // + // it('should return undefined select if no results are found.', async () => { + // const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; + // await TestReferenceModel.updateOne({ _id: entity._id }, { $set: { testEntity: undefined } }); + // const queryService = moduleRef.get(TestReferenceService); + // const queryResult = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity); + // expect(queryResult).toBeUndefined(); + // }); + // + // it('throw an error if a relation with that name is not found.', async () => { + // const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect(queryService.findRelation(TestEntity, 'badReference', entity)).rejects.toThrow( + // 'Unable to find reference badReference on TestReference' + // ); + // }); + // }); + // }); + // + // describe('with multiple discriminated entities', () => { + // it('call select and return the result', async () => { + // const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.findRelation(TestReference, 'testReference', entities, {}); + // + // expect(queryResult).toEqual( + // new Map([ + // [entities[0], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[0])], + // [entities[1], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[3])], + // [entities[2], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[6])] + // ]) + // ); + // }); + // + // it('should apply the filter option', async () => { + // const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const options: FindRelationOptions = { + // filter: { + // _id: { in: [TEST_REFERENCES_FOR_DISCRIMINATES[0]._id, TEST_REFERENCES_FOR_DISCRIMINATES[6]._id] } + // } + // }; + // const queryResult = await queryService.findRelation(TestReference, 'testReference', entities, options); + // expect(queryResult).toEqual( + // new Map([ + // [entities[0], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[0])], + // [entities[1], undefined], + // [entities[2], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[6])] + // ]) + // ); + // }); + // + // it('should return undefined select if no results are found.', async () => { + // const entities: DocumentType[] = [ + // TEST_DISCRIMINATED_ENTITIES[0], + // { _id: new Types.ObjectId() } as DocumentType + // ]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.findRelation(TestReference, 'testReference', entities); + // + // expect(queryResult).toEqual( + // new Map([ + // [entities[0], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[0])], + // [entities[1], undefined] + // ]) + // ); + // }); + // }); + // }); + // + // describe('#queryRelations on discriminated entities', () => { + // describe('with one entity', () => { + // it('call select and return the result', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', TEST_DISCRIMINATED_ENTITIES[0], { + // filter: { referenceName: { isNot: null } } + // }); + // return expect(queryResult).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3)); + // }); + // + // it('should apply a filter', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', TEST_DISCRIMINATED_ENTITIES[0], { + // filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName } } + // }); + // expect(queryResult).toEqual([TEST_REFERENCES_FOR_DISCRIMINATES[1]]); + // }); + // + // it('should apply paging', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', TEST_DISCRIMINATED_ENTITIES[0], { + // paging: { limit: 2, offset: 1 } + // }); + // expect(queryResult).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(1, 3)); + // }); + // }); + // + // describe('with virtual discriminated entity', () => { + // it('call select and return the result', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.queryRelations( + // TestReference, + // 'virtualTestReferences', + // TEST_DISCRIMINATED_ENTITIES[0], + // { + // filter: { referenceName: { isNot: null } } + // } + // ); + // return expect(queryResult).toEqual(expect.arrayContaining(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3))); + // }); + // + // it('should apply a filter', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.queryRelations( + // TestReference, + // 'virtualTestReferences', + // TEST_DISCRIMINATED_ENTITIES[0], + // { + // filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName } } + // } + // ); + // expect(queryResult).toEqual([TEST_REFERENCES_FOR_DISCRIMINATES[1]]); + // }); + // + // it('should apply paging', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.queryRelations( + // TestReference, + // 'virtualTestReferences', + // TEST_DISCRIMINATED_ENTITIES[0], + // { + // paging: { limit: 2, offset: 1 }, + // sorting: [{ field: 'referenceName', direction: SortDirection.ASC }] + // } + // ); + // expect(queryResult).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(1, 3)); + // }); + // }); + // + // describe('with multiple discriminated entities', () => { + // it('call return a map of results for each entity', async () => { + // const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { + // filter: { referenceName: { isNot: null } } + // }); + // expect(queryResult.size).toBe(3); + // expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3)); + // expect(queryResult.get(entities[1])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6)); + // expect(queryResult.get(entities[2])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(6, 9)); + // }); + // + // it('should apply a filter per entity', async () => { + // const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); + // const references = [ + // TEST_REFERENCES_FOR_DISCRIMINATES[1], + // TEST_REFERENCES_FOR_DISCRIMINATES[4], + // TEST_REFERENCES_FOR_DISCRIMINATES[7] + // ]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { + // filter: { referenceName: { in: references.map((r) => r.referenceName) } } + // }); + // expect(queryResult.size).toBe(3); + // expect(queryResult.get(entities[0])).toEqual([references[0]]); + // expect(queryResult.get(entities[1])).toEqual([references[1]]); + // expect(queryResult.get(entities[2])).toEqual([references[2]]); + // }); + // + // it('should apply paging per entity', async () => { + // const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { + // paging: { limit: 2, offset: 1 } + // }); + // expect(queryResult.size).toBe(3); + // expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(1, 3)); + // expect(queryResult.get(entities[1])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(4, 6)); + // expect(queryResult.get(entities[2])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(7, 9)); + // }); + // + // it('should return an empty array if no results are found.', async () => { + // const entities: DocumentType[] = [ + // TEST_DISCRIMINATED_ENTITIES[0], + // { _id: new Types.ObjectId() } as DocumentType + // ]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { + // filter: { referenceName: { isNot: null } } + // }); + // expect(queryResult.size).toBe(2); + // expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3)); + // expect(queryResult.get(entities[1])).toEqual([]); + // }); + // }); + // }); + // + // describe('#aggregateRelations', () => { + // describe('with one discriminated entity', () => { + // it('should return an aggregate', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const aggResult = await queryService.aggregateRelations( + // TestReference, + // 'testReferences', + // TEST_DISCRIMINATED_ENTITIES[0], + // { referenceName: { isNot: null } }, + // { count: ['id'] } + // ); + // return expect(aggResult).toEqual([ + // { + // count: { + // id: 3 + // } + // } + // ]); + // }); + // + // it('should support groupBy when aggregating relations', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const aggResult = await queryService.aggregateRelations( + // TestReference, + // 'testReferences', + // TEST_DISCRIMINATED_ENTITIES[0], + // { referenceName: { isNot: null } }, + // { groupBy: ['testEntity'], count: ['id'] } + // ); + // return expect(aggResult).toEqual([ + // { + // groupBy: { testEntity: TEST_DISCRIMINATED_ENTITIES[0]._id }, + // count: { + // id: 3 + // } + // } + // ]); + // }); + // }); + // + // describe('with virtual relation', () => { + // it('call select and return the result', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const aggResult = await queryService.aggregateRelations( + // TestReference, + // 'virtualTestReferences', + // TEST_DISCRIMINATED_ENTITIES[0], + // { referenceName: { isNot: null } }, + // { count: ['id'] } + // ); + // return expect(aggResult).toEqual([ + // { + // count: { + // id: 3 + // } + // } + // ]); + // }); + // }); + // + // describe('with multiple entities', () => { + // it('return a relation aggregate for each entity', async () => { + // const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.aggregateRelations( + // TestReference, + // 'testReferences', + // entities, + // { referenceName: { isNot: null } }, + // { + // count: ['id', 'referenceName', 'testEntity'], + // min: ['id', 'referenceName', 'testEntity'], + // max: ['id', 'referenceName', 'testEntity'] + // } + // ); + // + // expect(queryResult.size).toBe(3); + // expect(queryResult).toEqual( + // new Map([ + // [ + // entities[0], + // [ + // { + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[0].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ], + // [ + // entities[1], + // [ + // { + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[5].referenceName, + // testEntity: entities[1]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[3].referenceName, + // testEntity: entities[1]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ], + // [ + // entities[2], + // [ + // { + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[8].referenceName, + // testEntity: entities[2]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[6].referenceName, + // testEntity: entities[2]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ] + // ]) + // ); + // }); + // + // it('aggregate and group for each entities relation', async () => { + // const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.aggregateRelations( + // TestReference, + // 'testReferences', + // entities, + // { referenceName: { isNot: null } }, + // { + // groupBy: ['testEntity'], + // count: ['id', 'referenceName', 'testEntity'], + // min: ['id', 'referenceName', 'testEntity'], + // max: ['id', 'referenceName', 'testEntity'] + // } + // ); + // + // expect(queryResult.size).toBe(3); + // expect(queryResult).toEqual( + // new Map([ + // [ + // entities[0], + // [ + // { + // groupBy: { testEntity: entities[0]._id }, + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[0].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ], + // [ + // entities[1], + // [ + // { + // groupBy: { testEntity: entities[1]._id }, + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[5].referenceName, + // testEntity: entities[1]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[3].referenceName, + // testEntity: entities[1]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ], + // [ + // entities[2], + // [ + // { + // groupBy: { testEntity: entities[2]._id }, + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[8].referenceName, + // testEntity: entities[2]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[6].referenceName, + // testEntity: entities[2]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ] + // ]) + // ); + // }); + // + // it('should return an empty array if no results are found.', async () => { + // const entities: DocumentType[] = [ + // TEST_DISCRIMINATED_ENTITIES[0], + // { _id: new Types.ObjectId() } as DocumentType + // ]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.aggregateRelations( + // TestReference, + // 'testReferences', + // entities, + // { referenceName: { isNot: null } }, + // { + // count: ['id', 'referenceName', 'testEntity'], + // min: ['id', 'referenceName', 'testEntity'], + // max: ['id', 'referenceName', 'testEntity'] + // } + // ); + // + // expect(queryResult).toEqual( + // new Map([ + // [ + // entities[0], + // [ + // { + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[0].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ], + // [entities[1], []] + // ]) + // ); + // }); + // }); + // }); + // + // describe('#countRelations', () => { + // describe('with one entity', () => { + // it('count the references', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const countResult = await queryService.countRelations(TestReference, 'testReferences', entity, { + // referenceName: { + // in: [TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName, TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName] + // } + // }); + // return expect(countResult).toBe(2); + // }); + // + // it('should return a rejected promise if the relation is not found', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // return expect( + // queryService.countRelations(TestReference, 'badReferences', entity, { + // referenceName: { + // in: [TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName, TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName] + // } + // }) + // ).rejects.toThrow('Unable to find reference badReferences on TestDiscriminatedEntity'); + // }); + // }); + // + // describe('with virtual entity', () => { + // it('count references', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const countResult = await queryService.countRelations(TestReference, 'virtualTestReferences', entity, {}); + // return expect(countResult).toBe(3); + // }); + // it('count and return the result', async () => { + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const countResult = await queryService.countRelations(TestReference, 'virtualTestReferences', entity, { + // referenceName: { + // in: [TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName, TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName] + // } + // }); + // return expect(countResult).toBe(2); + // }); + // }); + // + // describe('with multiple entities', () => { + // it('call count and return the result', async () => { + // const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.countRelations(TestReference, 'testReferences', entities, { + // referenceName: { + // in: [ + // TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName, + // TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName, + // TEST_REFERENCES_FOR_DISCRIMINATES[4].referenceName, + // TEST_REFERENCES_FOR_DISCRIMINATES[5].referenceName, + // TEST_REFERENCES_FOR_DISCRIMINATES[7].referenceName, + // TEST_REFERENCES_FOR_DISCRIMINATES[8].referenceName + // ] + // } + // }); + // + // expect(queryResult).toEqual( + // new Map([ + // [entities[0], 2], + // [entities[1], 2], + // [entities[2], 2] + // ]) + // ); + // }); + // }); + // }); + // + // describe('#addRelations', () => { + // it('call select and return the result', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.addRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()) + // ); + // expect(queryResult).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: expect.arrayContaining(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 6).map((r) => r._id)) + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations).toHaveLength(6); + // }); + // + // it('should not modify relations if relationIds is empty', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.addRelations('testReferences', entity._id.toHexString(), []); + // expect(queryResult).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: expect.arrayContaining(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id)) + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations).toHaveLength(3); + // }); + // + // describe('with virtual reference', () => { + // it('should return a rejected promise', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // return expect( + // queryService.addRelations( + // 'virtualTestReferences', + // entity._id.toHexString(), + // TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()) + // ) + // ).rejects.toThrow('AddRelations not supported for virtual relation virtualTestReferences'); + // }); + // }); + // + // 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_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // return expect( + // queryService.addRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()), + // { + // filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } + // } + // ) + // ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); + // }); + // + // it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // return expect( + // queryService.addRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()), + // { + // relationFilter: { referenceName: { like: '%-one' } } + // } + // ) + // ).rejects.toThrow('Unable to find all testReferences to add to TestDiscriminatedEntity'); + // }); + // }); + // }); + // + // describe('#setRelations', () => { + // it('set all relations on the entity', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const relationIds = TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id); + // const queryResult = await queryService.setRelations( + // 'testReferences', + // entity._id.toHexString(), + // relationIds.map((id) => id.toHexString()) + // ); + // expect(queryResult).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: expect.arrayContaining(relationIds) + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations.map((r) => r._id)).toEqual(relationIds); + // }); + // + // it('should remove all relations if the relationIds is empty', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.setRelations('testReferences', entity._id.toHexString(), []); + // expect(queryResult).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: expect.arrayContaining([]) + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations.map((r) => r._id)).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_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // return expect( + // queryService.setRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()), + // { + // filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } + // } + // ) + // ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); + // }); + // + // it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // return expect( + // queryService.setRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()), + // { + // relationFilter: { referenceName: { like: '%-one' } } + // } + // ) + // ).rejects.toThrow('Unable to find all testReferences to set on TestDiscriminatedEntity'); + // }); + // }); + // }); + // + // describe('#setRelation', () => { + // it('call select and return the result', async () => { + // const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // + // const queryResult = await queryService.setRelation( + // 'testEntity', + // entity._id.toHexString(), + // TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString() + // ); + // + // expect(queryResult).toEqual(expect.objectContaining({ ...entity, testEntity: TEST_DISCRIMINATED_ENTITIES[1]._id })); + // + // const relation = await queryService.findRelation(TestEntity, 'testEntity', entity); + // expect(relation).toEqual(TEST_DISCRIMINATED_ENTITIES[1]); + // }); + // + // it('should reject with a virtual reference', async () => { + // const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.setRelation('virtualTestEntity', entity._id.toHexString(), TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString()) + // ).rejects.toThrow('SetRelation not supported for virtual relation virtualTestEntity'); + // }); + // + // 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_REFERENCES_FOR_DISCRIMINATES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.setRelation('testEntity', entity._id.toHexString(), TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString(), { + // filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName } } + // }) + // ).rejects.toThrow(`Unable to find TestReference with id: ${String(entity._id)}`); + // }); + // + // it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { + // const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.setRelation( + // 'TestDiscriminatedEntity', + // entity._id.toHexString(), + // TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString(), + // { + // relationFilter: { stringType: { like: '%-one' } } + // } + // ) + // ).rejects.toThrow('Unable to find reference TestDiscriminatedEntity on TestReference'); + // }); + // }); + // }); + // + // describe('#removeRelation', () => { + // it('call select and return the result', async () => { + // const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // const queryResult = await queryService.removeRelation( + // 'testEntity', + // entity._id.toHexString(), + // TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString() + // ); + // const { testEntity, ...expected } = entity; + // expect(queryResult).toEqual(expect.objectContaining(expected)); + // + // const relation = await queryService.findRelation(TestEntity, 'testEntity', entity); + // expect(relation).toBeUndefined(); + // }); + // + // it('should reject with a virtual reference', async () => { + // const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.removeRelation( + // 'virtualTestEntity', + // entity._id.toHexString(), + // TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString() + // ) + // ).rejects.toThrow('RemoveRelation not supported for virtual relation virtualTestEntity'); + // }); + // + // 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_REFERENCES_FOR_DISCRIMINATES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.removeRelation('testEntity', entity._id.toHexString(), TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString(), { + // filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName } } + // }) + // ).rejects.toThrow(`Unable to find TestReference with id: ${String(entity._id)}`); + // }); + // + // it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { + // const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.removeRelation( + // 'TestDiscriminatedEntity', + // entity._id.toHexString(), + // TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString(), + // { + // relationFilter: { stringType: { like: '%-one' } } + // } + // ) + // ).rejects.toThrow('Unable to find reference TestDiscriminatedEntity on TestReference'); + // }); + // }); + // }); + // + // describe('#removeRelations', () => { + // it('call select and return the result', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.removeRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id.toHexString()) + // ); + // expect(queryResult.toObject()).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: [] + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations).toHaveLength(0); + // }); + // + // it('should not modify relations if relationIds is empty', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // const queryResult = await queryService.removeRelations('testReferences', entity._id.toHexString(), []); + // expect(queryResult.toObject()).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: expect.arrayContaining(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id)) + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations).toHaveLength(3); + // }); + // + // describe('with virtual reference', () => { + // it('should return a rejected promise', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // return expect( + // queryService.removeRelations( + // 'virtualTestReferences', + // entity._id.toHexString(), + // TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id.toHexString()) + // ) + // ).rejects.toThrow('RemoveRelations not supported for virtual relation virtualTestReferences'); + // }); + // }); + // + // 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_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // return expect( + // queryService.removeRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id.toHexString()), + // { + // filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } + // } + // ) + // ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); + // }); + // + // it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { + // const entity = TEST_DISCRIMINATED_ENTITIES[0]; + // const queryService = moduleRef.get(TestDiscriminatedEntityService); + // return expect( + // queryService.removeRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id.toHexString()), + // { + // relationFilter: { referenceName: { like: '%-one' } } + // } + // ) + // ).rejects.toThrow('Unable to find all testReferences to remove from TestDiscriminatedEntity'); + // }); + // }); + // }); +}); diff --git a/packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts.disabled b/packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts.disabled deleted file mode 100644 index 8e6b74d70..000000000 --- a/packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts.disabled +++ /dev/null @@ -1,1636 +0,0 @@ -/* eslint-disable no-underscore-dangle,@typescript-eslint/no-unsafe-return */ -import { getModelForClass, DocumentType, mongoose } from '@typegoose/typegoose'; -import { Test, TestingModule } from '@nestjs/testing'; -import { ReturnModelType } from '@typegoose/typegoose/lib/types'; -import { InjectModel, TypegooseModule } from 'nestjs-typegoose'; -import { FindRelationOptions, SortDirection } from '@ptc-org/nestjs-query-core'; -import { - TestReference, - TestEntity, - TEST_REFERENCES_FOR_DISCRIMINATES, - TEST_DISCRIMINATED_ENTITIES, - getConnectionUri, - prepareDb, - closeDbConnection, - dropDatabase -} from '../__fixtures__'; -import { NestjsQueryTypegooseModule } from '../../src'; -import { TypegooseQueryService } from '../../src/services'; -import { TestDiscriminatedEntity } from '../__fixtures__/test-discriminated.entity'; - -const { Types } = mongoose; - -describe('TypegooseQueryService', () => { - let moduleRef: TestingModule; - let TestReferenceModel: ReturnModelType; - let TestDiscriminatedEntityModel: ReturnModelType; - - class TestReferenceService extends TypegooseQueryService { - constructor(@InjectModel(TestReference) readonly model: ReturnModelType) { - super(model); - TestReferenceModel = model; - } - } - - class TestDiscriminatedEntityService extends TypegooseQueryService { - constructor(@InjectModel(TestDiscriminatedEntity) readonly model: ReturnModelType) { - super(model); - TestDiscriminatedEntityModel = model; - } - } - - beforeAll(async () => { - moduleRef = await Test.createTestingModule({ - imports: [ - TypegooseModule.forRoot(await getConnectionUri()), - NestjsQueryTypegooseModule.forFeature([ - { - typegooseClass: TestEntity, - discriminators: [TestDiscriminatedEntity] - }, - TestReference - ]) - ], - providers: [TestDiscriminatedEntityService, TestReferenceService] - }).compile(); - }); - - function convertDocument(doc: DocumentType): Doc { - return doc.toObject({ virtuals: true }) as Doc; - } - - function convertDocuments(docs: DocumentType[]): Doc[] { - return docs.map((doc) => convertDocument(doc)); - } - - function testDiscriminatedEntityToObject(tde: TestDiscriminatedEntity): Partial { - return { - _id: tde._id, - stringType: tde.stringType, - boolType: tde.boolType, - numberType: tde.numberType, - dateType: tde.dateType, - stringType2: tde.stringType2 - }; - } - - function testDiscriminatedEntityToCreate(tde: TestDiscriminatedEntity): Partial { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { _id, ...insert } = testDiscriminatedEntityToObject(tde); - return insert; - } - - function expectEqualCreate(result: TestDiscriminatedEntity[], expected: TestDiscriminatedEntity[]) { - const cleansedResults = result.map(testDiscriminatedEntityToCreate); - const cleansedExpected = expected.map(testDiscriminatedEntityToCreate); - expect(cleansedResults).toEqual(cleansedExpected); - } - - afterAll(async () => closeDbConnection()); - - beforeEach(async () => prepareDb()); - - afterEach(async () => dropDatabase()); - - describe('#query with discriminated entity', () => { - it('call find and return the result', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({}); - - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES)); - }); - - it('should support eq operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { stringType: { eq: 'foo11' } } }); - expect(convertDocuments(queryResult)).toEqual([TEST_DISCRIMINATED_ENTITIES[0]]); - }); - - it('should support neq operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { stringType: { neq: 'foo1' } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(1))); - }); - - it('should support gt operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { numberType: { gt: 5 } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(5))); - }); - - it('should support gte operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { numberType: { gte: 5 } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(4))); - }); - - it('should support lt operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { numberType: { lt: 15 } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(0, 4))); - }); - - it('should support lte operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { numberType: { lte: 15 } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(0, 5))); - }); - - it('should support in operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { numberType: { in: [11, 12, 13] } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(0, 3))); - }); - - it('should support notIn operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { numberType: { notIn: [11, 12, 13] } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.slice(4))); - }); - - it('should support is operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { boolType: { is: true } } }); - expect(convertDocuments(queryResult)).toEqual( - expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.filter((e) => e.boolType)) - ); - }); - - it('should support isNot operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { boolType: { isNot: true } } }); - expect(convertDocuments(queryResult)).toEqual( - expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES.filter((e) => !e.boolType)) - ); - }); - - it('should support like operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { stringType: { like: 'foo%' } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES)); - }); - - it('should support notLike operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { stringType: { notLike: 'foo%' } } }); - expect(convertDocuments(queryResult)).toEqual([]); - }); - - it('should support iLike operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { stringType: { iLike: 'FOO%' } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_DISCRIMINATED_ENTITIES)); - }); - - it('should support notILike operator', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.query({ filter: { stringType: { notILike: 'FOO%' } } }); - expect(convertDocuments(queryResult)).toEqual([]); - }); - }); - - describe('#aggregate with discriminated entity', () => { - it('call select with the aggregate columns and return the result', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.aggregate( - {}, - { - count: ['id'], - avg: ['numberType'], - sum: ['numberType'], - max: ['id', 'dateType', 'numberType', 'stringType'], - min: ['id', 'dateType', 'numberType', 'stringType'] - } - ); - return expect(queryResult).toEqual([ - { - avg: { - numberType: 15.5 - }, - count: { - id: 10 - }, - max: { - dateType: TEST_DISCRIMINATED_ENTITIES[9].dateType, - numberType: 20, - stringType: 'foo20', - id: expect.any(Types.ObjectId) - }, - min: { - dateType: TEST_DISCRIMINATED_ENTITIES[0].dateType, - numberType: 11, - stringType: 'foo11', - id: expect.any(Types.ObjectId) - }, - sum: { - numberType: 155 - } - } - ]); - }); - - it('allow aggregates with groupBy', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.aggregate( - {}, - { - groupBy: ['boolType'], - count: ['id'], - avg: ['numberType'], - sum: ['numberType'], - max: ['id', 'dateType', 'numberType', 'stringType'], - min: ['id', 'dateType', 'numberType', 'stringType'] - } - ); - return expect(queryResult).toEqual([ - { - groupBy: { - boolType: false - }, - avg: { - numberType: 15 - }, - count: { - id: 5 - }, - max: { - dateType: TEST_DISCRIMINATED_ENTITIES[8].dateType, - numberType: 19, - stringType: 'foo19', - id: expect.any(Types.ObjectId) - }, - min: { - dateType: TEST_DISCRIMINATED_ENTITIES[0].dateType, - numberType: 11, - stringType: 'foo11', - id: expect.any(Types.ObjectId) - }, - sum: { - numberType: 75 - } - }, - { - groupBy: { - boolType: true - }, - avg: { - numberType: 16 - }, - count: { - id: 5 - }, - max: { - dateType: TEST_DISCRIMINATED_ENTITIES[9].dateType, - numberType: 20, - stringType: 'foo20', - id: expect.any(Types.ObjectId) - }, - min: { - dateType: TEST_DISCRIMINATED_ENTITIES[1].dateType, - numberType: 12, - stringType: 'foo12', - id: expect.any(Types.ObjectId) - }, - sum: { - numberType: 80 - } - } - ]); - }); - - it('call select with the aggregate columns and return the result with a filter', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.aggregate( - { stringType: { in: ['foo11', 'foo12', 'foo13'] } }, - { - count: ['id'], - avg: ['numberType'], - sum: ['numberType'], - max: ['id', 'dateType', 'numberType', 'stringType'], - min: ['id', 'dateType', 'numberType', 'stringType'] - } - ); - return expect(queryResult).toEqual([ - { - avg: { - numberType: 12 - }, - count: { - id: 3 - }, - max: { - dateType: TEST_DISCRIMINATED_ENTITIES[2].dateType, - numberType: 13, - stringType: 'foo13', - id: expect.any(Types.ObjectId) - }, - min: { - dateType: TEST_DISCRIMINATED_ENTITIES[0].dateType, - numberType: 11, - stringType: 'foo11', - id: expect.any(Types.ObjectId) - }, - sum: { - numberType: 36 - } - } - ]); - }); - }); - - describe('#count with discriminated entity', () => { - it('should return number of elements matching a query', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const expectedEntities = TEST_DISCRIMINATED_ENTITIES.slice(0, 2); - const count = await queryService.count({ stringType: { in: expectedEntities.map((e) => e.stringType) } }); - expect(count).toBe(2); - }); - }); - - describe('#findById with discriminated entity', () => { - it('return the entity if found', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const found = await queryService.findById(entity._id.toString()); - expect(convertDocument(found!)).toEqual(entity); - }); - - it('return undefined if not found', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const found = await queryService.findById(new Types.ObjectId().toString()); - expect(found).toBeUndefined(); - }); - - describe('with filter on discriminated entity', () => { - it('should return an entity if all filters match', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const found = await queryService.findById(entity._id.toString(), { - filter: { stringType: { eq: entity.stringType } } - }); - expect(convertDocument(found!)).toEqual(entity); - }); - - it('should return an undefined if an entity with the pk and filter is not found', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const found = await queryService.findById(entity._id.toHexString(), { - filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } - }); - expect(found).toBeUndefined(); - }); - }); - }); - - describe('#getById with discriminated entity', () => { - it('return the entity if found', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const found = await queryService.getById(entity._id.toHexString()); - expect(convertDocument(found)).toEqual(entity); - }); - - it('return undefined if not found', () => { - const badId = new Types.ObjectId().toHexString(); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect(queryService.getById(badId)).rejects.toThrow( - `Unable to find TestDiscriminatedEntity with id: ${badId}` - ); - }); - - describe('with filter on discriminated entity', () => { - it('should return an entity if all filters match', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const found = await queryService.getById(entity._id.toHexString(), { - filter: { stringType: { eq: entity.stringType } } - }); - expect(convertDocument(found)).toEqual(entity); - }); - - it('should return an undefined if an entity with the pk and filter is not found', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.getById(entity._id.toHexString(), { - filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } - }) - ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); - }); - }); - }); - - describe('#createMany with discriminated entity', () => { - it('call save on the repo with instances of entities when passed plain objects', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const created = await queryService.createMany(TEST_DISCRIMINATED_ENTITIES.map(testDiscriminatedEntityToCreate)); - expectEqualCreate(created, TEST_DISCRIMINATED_ENTITIES); - }); - - it('call save on the repo with instances of entities when passed instances', async () => { - const instances = TEST_DISCRIMINATED_ENTITIES.map((e) => testDiscriminatedEntityToCreate(e)); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const created = await queryService.createMany(instances); - expectEqualCreate(created, TEST_DISCRIMINATED_ENTITIES); - }); - - it('should reject if the entities already exist', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect(queryService.createMany(TEST_DISCRIMINATED_ENTITIES)).rejects.toThrow( - 'Id cannot be specified when updating or creating' - ); - }); - }); - - describe('#createOne with discriminated entity', () => { - it('call save on the repo with an instance of the entity when passed a plain object', async () => { - const entity = testDiscriminatedEntityToCreate(TEST_DISCRIMINATED_ENTITIES[0]); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const created = await queryService.createOne(entity); - expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); - }); - - it('call save on the repo with an instance of the entity when passed an instance', async () => { - const Model = getModelForClass(TestDiscriminatedEntity); - const entity = new Model(testDiscriminatedEntityToCreate(TEST_DISCRIMINATED_ENTITIES[0])); - const outcome = testDiscriminatedEntityToObject(entity); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const created = await queryService.createOne(entity); - expect(convertDocument(created)).toEqual(expect.objectContaining(outcome)); - }); - - it('should reject if the entity contains an id', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect(queryService.createOne({ ...entity })).rejects.toThrow( - 'Id cannot be specified when updating or creating' - ); - }); - - it('should not reject if the entity contains an undefined id', async () => { - const entity = testDiscriminatedEntityToCreate(TEST_DISCRIMINATED_ENTITIES[0]); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const created = await queryService.createOne({ ...entity, id: undefined }); - expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); - }); - - it('should not reject if the entity contains an undefined _id', async () => { - const entity = testDiscriminatedEntityToCreate(TEST_DISCRIMINATED_ENTITIES[0]); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const created = await queryService.createOne({ ...entity, _id: undefined }); - expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); - }); - }); - - describe('#deleteMany with discriminated entity', () => { - it('delete all records that match the query', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 5); - const { deletedCount } = await queryService.deleteMany({ - stringType: { in: entities.map((e) => e.stringType) } - }); - expect(deletedCount).toBe(5); - const allCount = await queryService.count({}); - expect(allCount).toBe(5); - }); - }); - - describe('#deleteOne with discriminated entity', () => { - it('remove the entity', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const deleted = await queryService.deleteOne(TEST_DISCRIMINATED_ENTITIES[0]._id.toHexString()); - expect(convertDocument(deleted)).toEqual(TEST_DISCRIMINATED_ENTITIES[0]); - }); - - it('call fail if the entity is not found', async () => { - const badId = new Types.ObjectId().toHexString(); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect(queryService.deleteOne(badId)).rejects.toThrow( - `Unable to find TestDiscriminatedEntity with id: ${badId}` - ); - }); - - describe('with filter on discriminated entity', () => { - it('should delete the entity if all filters match', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const deleted = await queryService.deleteOne(entity._id.toHexString(), { - filter: { stringType: { eq: entity.stringType } } - }); - expect(convertDocument(deleted)).toEqual(TEST_DISCRIMINATED_ENTITIES[0]); - }); - - it('should return throw an error if unable to find', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.deleteOne(entity._id.toHexString(), { - filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } - }) - ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); - }); - }); - }); - - describe('#updateMany with discriminated entity', () => { - it('update all entities in the filter', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const filter = { - stringType: { in: TEST_DISCRIMINATED_ENTITIES.slice(0, 5).map((e) => e.stringType) } - }; - await queryService.updateMany({ stringType: 'updated' }, filter); - const entities = await queryService.query({ filter: { stringType: { eq: 'updated' } } }); - expect(entities).toHaveLength(5); - }); - - it('should reject if the update contains the ID', () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect(queryService.updateMany({ id: new Types.ObjectId().toHexString() }, {})).rejects.toThrow( - 'Id cannot be specified when updating' - ); - }); - - it('should not reject if the update contains an undefined id', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const entitiesToUpdate = TEST_DISCRIMINATED_ENTITIES.slice(0, 5); - const filter = { - stringType: { in: entitiesToUpdate.map((e) => e.stringType) } - }; - await queryService.updateMany({ stringType: 'updated', id: undefined }, filter); - const entities = await queryService.query({ filter: { stringType: { eq: 'updated' } } }); - expect(entities).toHaveLength(entitiesToUpdate.length); - expect(new Set(entities.map((e) => e.id))).toEqual(new Set(entitiesToUpdate.map((e) => e.id))); - }); - }); - - describe('#updateOne with discriminated entity', () => { - it('update the entity', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const update = { stringType: 'updated' }; - const updated = await queryService.updateOne(entity._id.toHexString(), update); - expect(updated).toEqual( - expect.objectContaining({ - _id: entity._id, - ...update - }) - ); - }); - - it('update the entity with an instance of the entity', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const Model = getModelForClass(TestDiscriminatedEntity); - const update = new Model({ stringType: 'updated' }); - const updated = await queryService.updateOne(entity._id.toHexString(), update); - expect(updated).toEqual( - expect.objectContaining({ - _id: entity._id, - stringType: 'updated' - }) - ); - }); - - it('should reject if the update contains the ID', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.updateOne(TEST_DISCRIMINATED_ENTITIES[0]._id.toHexString(), { _id: new Types.ObjectId() }) - ).rejects.toThrow('Id cannot be specified when updating'); - }); - - it('call fail if the entity is not found', async () => { - const badId = new Types.ObjectId().toHexString(); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect(queryService.updateOne(badId, { stringType: 'updated' })).rejects.toThrow( - `Unable to find TestDiscriminatedEntity with id: ${badId}` - ); - }); - - describe('with filter on discriminated entity', () => { - it('should update the entity if all filters match', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const update = { stringType: 'updated' }; - const updated = await queryService.updateOne(entity._id.toHexString(), update, { - filter: { stringType: { eq: entity.stringType } } - }); - expect(updated).toEqual(expect.objectContaining({ _id: entity._id, ...update })); - }); - - it('should throw an error if unable to find the entity', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.updateOne( - entity._id.toHexString(), - { stringType: 'updated' }, - { filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } } - ) - ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); - }); - }); - }); - - describe('#findRelation with discriminated entity', () => { - describe('with one entity', () => { - it('call select and return the result', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.findRelation(TestReference, 'testReference', entity); - expect(queryResult).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES[0]); - }); - - it('apply the filter option', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult1 = await queryService.findRelation(TestReference, 'testReference', entity, { - filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[0].referenceName } } - }); - expect(queryResult1).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES[0]); - - const queryResult2 = await queryService.findRelation(TestReference, 'testReference', entity, { - filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName } } - }); - expect(queryResult2).toBeUndefined(); - }); - - it('should return undefined select if no results are found.', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - await TestDiscriminatedEntityModel.updateOne({ _id: entity._id }, { $set: { testReference: undefined } }); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.findRelation(TestReference, 'testReference', entity); - expect(queryResult).toBeUndefined(); - }); - - it('throw an error if a relation with that name is not found.', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - return expect(queryService.findRelation(TestReference, 'badReference', entity)).rejects.toThrow( - 'Unable to find reference badReference on TestDiscriminatedEntity' - ); - }); - - describe('virtual reference', () => { - it('call select and return the result', async () => { - const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; - const queryService = moduleRef.get(TestReferenceService); - const queryResult = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity); - - expect(queryResult).toEqual(TEST_DISCRIMINATED_ENTITIES[0]); - }); - - it('apply the filter option', async () => { - const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; - const queryService = moduleRef.get(TestReferenceService); - const queryResult1 = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity, { - filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[0].stringType } } - }); - expect(queryResult1).toEqual(TEST_DISCRIMINATED_ENTITIES[0]); - - const queryResult2 = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity, { - filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } - }); - expect(queryResult2).toBeUndefined(); - }); - - it('should return undefined select if no results are found.', async () => { - const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; - await TestReferenceModel.updateOne({ _id: entity._id }, { $set: { testEntity: undefined } }); - const queryService = moduleRef.get(TestReferenceService); - const queryResult = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity); - expect(queryResult).toBeUndefined(); - }); - - it('throw an error if a relation with that name is not found.', async () => { - const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect(queryService.findRelation(TestEntity, 'badReference', entity)).rejects.toThrow( - 'Unable to find reference badReference on TestReference' - ); - }); - }); - }); - - describe('with multiple discriminated entities', () => { - it('call select and return the result', async () => { - const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.findRelation(TestReference, 'testReference', entities, {}); - - expect(queryResult).toEqual( - new Map([ - [entities[0], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[0])], - [entities[1], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[3])], - [entities[2], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[6])] - ]) - ); - }); - - it('should apply the filter option', async () => { - const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const options: FindRelationOptions = { - filter: { - _id: { in: [TEST_REFERENCES_FOR_DISCRIMINATES[0]._id, TEST_REFERENCES_FOR_DISCRIMINATES[6]._id] } - } - }; - const queryResult = await queryService.findRelation(TestReference, 'testReference', entities, options); - expect(queryResult).toEqual( - new Map([ - [entities[0], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[0])], - [entities[1], undefined], - [entities[2], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[6])] - ]) - ); - }); - - it('should return undefined select if no results are found.', async () => { - const entities: DocumentType[] = [ - TEST_DISCRIMINATED_ENTITIES[0], - { _id: new Types.ObjectId() } as DocumentType - ]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.findRelation(TestReference, 'testReference', entities); - - expect(queryResult).toEqual( - new Map([ - [entities[0], expect.objectContaining(TEST_REFERENCES_FOR_DISCRIMINATES[0])], - [entities[1], undefined] - ]) - ); - }); - }); - }); - - describe('#queryRelations on discriminated entities', () => { - describe('with one entity', () => { - it('call select and return the result', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'testReferences', - TEST_DISCRIMINATED_ENTITIES[0], - { - filter: { referenceName: { isNot: null } } - } - ); - return expect(queryResult).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3)); - }); - - it('should apply a filter', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'testReferences', - TEST_DISCRIMINATED_ENTITIES[0], - { - filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName } } - } - ); - expect(queryResult).toEqual([TEST_REFERENCES_FOR_DISCRIMINATES[1]]); - }); - - it('should apply paging', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'testReferences', - TEST_DISCRIMINATED_ENTITIES[0], - { - paging: { limit: 2, offset: 1 } - } - ); - expect(queryResult).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(1, 3)); - }); - }); - - describe('with virtual discriminated entity', () => { - it('call select and return the result', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'virtualTestReferences', - TEST_DISCRIMINATED_ENTITIES[0], - { - filter: { referenceName: { isNot: null } } - } - ); - return expect(queryResult).toEqual(expect.arrayContaining(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3))); - }); - - it('should apply a filter', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'virtualTestReferences', - TEST_DISCRIMINATED_ENTITIES[0], - { - filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName } } - } - ); - expect(queryResult).toEqual([TEST_REFERENCES_FOR_DISCRIMINATES[1]]); - }); - - it('should apply paging', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'virtualTestReferences', - TEST_DISCRIMINATED_ENTITIES[0], - { - paging: { limit: 2, offset: 1 }, - sorting: [{ field: 'referenceName', direction: SortDirection.ASC }] - } - ); - expect(queryResult).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(1, 3)); - }); - }); - - describe('with multiple discriminated entities', () => { - it('call return a map of results for each entity', async () => { - const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { - filter: { referenceName: { isNot: null } } - }); - expect(queryResult.size).toBe(3); - expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3)); - expect(queryResult.get(entities[1])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6)); - expect(queryResult.get(entities[2])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(6, 9)); - }); - - it('should apply a filter per entity', async () => { - const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); - const references = [ - TEST_REFERENCES_FOR_DISCRIMINATES[1], - TEST_REFERENCES_FOR_DISCRIMINATES[4], - TEST_REFERENCES_FOR_DISCRIMINATES[7] - ]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { - filter: { referenceName: { in: references.map((r) => r.referenceName) } } - }); - expect(queryResult.size).toBe(3); - expect(queryResult.get(entities[0])).toEqual([references[0]]); - expect(queryResult.get(entities[1])).toEqual([references[1]]); - expect(queryResult.get(entities[2])).toEqual([references[2]]); - }); - - it('should apply paging per entity', async () => { - const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { - paging: { limit: 2, offset: 1 } - }); - expect(queryResult.size).toBe(3); - expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(1, 3)); - expect(queryResult.get(entities[1])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(4, 6)); - expect(queryResult.get(entities[2])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(7, 9)); - }); - - it('should return an empty array if no results are found.', async () => { - const entities: DocumentType[] = [ - TEST_DISCRIMINATED_ENTITIES[0], - { _id: new Types.ObjectId() } as DocumentType - ]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { - filter: { referenceName: { isNot: null } } - }); - expect(queryResult.size).toBe(2); - expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3)); - expect(queryResult.get(entities[1])).toEqual([]); - }); - }); - }); - - describe('#aggregateRelations', () => { - describe('with one discriminated entity', () => { - it('should return an aggregate', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const aggResult = await queryService.aggregateRelations( - TestReference, - 'testReferences', - TEST_DISCRIMINATED_ENTITIES[0], - { referenceName: { isNot: null } }, - { count: ['id'] } - ); - return expect(aggResult).toEqual([ - { - count: { - id: 3 - } - } - ]); - }); - - it('should support groupBy when aggregating relations', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const aggResult = await queryService.aggregateRelations( - TestReference, - 'testReferences', - TEST_DISCRIMINATED_ENTITIES[0], - { referenceName: { isNot: null } }, - { groupBy: ['testEntity'], count: ['id'] } - ); - return expect(aggResult).toEqual([ - { - groupBy: { testEntity: TEST_DISCRIMINATED_ENTITIES[0]._id }, - count: { - id: 3 - } - } - ]); - }); - }); - - describe('with virtual relation', () => { - it('call select and return the result', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const aggResult = await queryService.aggregateRelations( - TestReference, - 'virtualTestReferences', - TEST_DISCRIMINATED_ENTITIES[0], - { referenceName: { isNot: null } }, - { count: ['id'] } - ); - return expect(aggResult).toEqual([ - { - count: { - id: 3 - } - } - ]); - }); - }); - - describe('with multiple entities', () => { - it('return a relation aggregate for each entity', async () => { - const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.aggregateRelations( - TestReference, - 'testReferences', - entities, - { referenceName: { isNot: null } }, - { - count: ['id', 'referenceName', 'testEntity'], - min: ['id', 'referenceName', 'testEntity'], - max: ['id', 'referenceName', 'testEntity'] - } - ); - - expect(queryResult.size).toBe(3); - expect(queryResult).toEqual( - new Map([ - [ - entities[0], - [ - { - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[0].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ], - [ - entities[1], - [ - { - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[5].referenceName, - testEntity: entities[1]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[3].referenceName, - testEntity: entities[1]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ], - [ - entities[2], - [ - { - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[8].referenceName, - testEntity: entities[2]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[6].referenceName, - testEntity: entities[2]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ] - ]) - ); - }); - - it('aggregate and group for each entities relation', async () => { - const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.aggregateRelations( - TestReference, - 'testReferences', - entities, - { referenceName: { isNot: null } }, - { - groupBy: ['testEntity'], - count: ['id', 'referenceName', 'testEntity'], - min: ['id', 'referenceName', 'testEntity'], - max: ['id', 'referenceName', 'testEntity'] - } - ); - - expect(queryResult.size).toBe(3); - expect(queryResult).toEqual( - new Map([ - [ - entities[0], - [ - { - groupBy: { testEntity: entities[0]._id }, - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[0].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ], - [ - entities[1], - [ - { - groupBy: { testEntity: entities[1]._id }, - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[5].referenceName, - testEntity: entities[1]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[3].referenceName, - testEntity: entities[1]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ], - [ - entities[2], - [ - { - groupBy: { testEntity: entities[2]._id }, - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[8].referenceName, - testEntity: entities[2]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[6].referenceName, - testEntity: entities[2]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ] - ]) - ); - }); - - it('should return an empty array if no results are found.', async () => { - const entities: DocumentType[] = [ - TEST_DISCRIMINATED_ENTITIES[0], - { _id: new Types.ObjectId() } as DocumentType - ]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.aggregateRelations( - TestReference, - 'testReferences', - entities, - { referenceName: { isNot: null } }, - { - count: ['id', 'referenceName', 'testEntity'], - min: ['id', 'referenceName', 'testEntity'], - max: ['id', 'referenceName', 'testEntity'] - } - ); - - expect(queryResult).toEqual( - new Map([ - [ - entities[0], - [ - { - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES_FOR_DISCRIMINATES[0].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ], - [entities[1], []] - ]) - ); - }); - }); - }); - - describe('#countRelations', () => { - describe('with one entity', () => { - it('count the references', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const countResult = await queryService.countRelations(TestReference, 'testReferences', entity, { - referenceName: { - in: [ - TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName, - TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName - ] - } - }); - return expect(countResult).toBe(2); - }); - - it('should return a rejected promise if the relation is not found', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - return expect( - queryService.countRelations(TestReference, 'badReferences', entity, { - referenceName: { - in: [ - TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName, - TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName - ] - } - }) - ).rejects.toThrow('Unable to find reference badReferences on TestDiscriminatedEntity'); - }); - }); - - describe('with virtual entity', () => { - it('count references', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const countResult = await queryService.countRelations(TestReference, 'virtualTestReferences', entity, {}); - return expect(countResult).toBe(3); - }); - it('count and return the result', async () => { - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const countResult = await queryService.countRelations(TestReference, 'virtualTestReferences', entity, { - referenceName: { - in: [ - TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName, - TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName - ] - } - }); - return expect(countResult).toBe(2); - }); - }); - - describe('with multiple entities', () => { - it('call count and return the result', async () => { - const entities = TEST_DISCRIMINATED_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.countRelations(TestReference, 'testReferences', entities, { - referenceName: { - in: [ - TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName, - TEST_REFERENCES_FOR_DISCRIMINATES[2].referenceName, - TEST_REFERENCES_FOR_DISCRIMINATES[4].referenceName, - TEST_REFERENCES_FOR_DISCRIMINATES[5].referenceName, - TEST_REFERENCES_FOR_DISCRIMINATES[7].referenceName, - TEST_REFERENCES_FOR_DISCRIMINATES[8].referenceName - ] - } - }); - - expect(queryResult).toEqual( - new Map([ - [entities[0], 2], - [entities[1], 2], - [entities[2], 2] - ]) - ); - }); - }); - }); - - describe('#addRelations', () => { - it('call select and return the result', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.addRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()) - ); - expect(queryResult).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: expect.arrayContaining(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 6).map((r) => r._id)) - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations).toHaveLength(6); - }); - - it('should not modify relations if relationIds is empty', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.addRelations('testReferences', entity._id.toHexString(), []); - expect(queryResult).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: expect.arrayContaining(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id)) - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations).toHaveLength(3); - }); - - describe('with virtual reference', () => { - it('should return a rejected promise', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.addRelations( - 'virtualTestReferences', - entity._id.toHexString(), - TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()) - ) - ).rejects.toThrow('AddRelations not supported for virtual relation virtualTestReferences'); - }); - }); - - 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_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.addRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()), - { - filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } - } - ) - ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); - }); - - it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.addRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()), - { - relationFilter: { referenceName: { like: '%-one' } } - } - ) - ).rejects.toThrow('Unable to find all testReferences to add to TestDiscriminatedEntity'); - }); - }); - }); - - describe('#setRelations', () => { - it('set all relations on the entity', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const relationIds = TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id); - const queryResult = await queryService.setRelations( - 'testReferences', - entity._id.toHexString(), - relationIds.map((id) => id.toHexString()) - ); - expect(queryResult).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: expect.arrayContaining(relationIds) - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations.map((r) => r._id)).toEqual(relationIds); - }); - - it('should remove all relations if the relationIds is empty', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.setRelations('testReferences', entity._id.toHexString(), []); - expect(queryResult).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: expect.arrayContaining([]) - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations.map((r) => r._id)).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_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.setRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()), - { - filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } - } - ) - ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); - }); - - it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.setRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES_FOR_DISCRIMINATES.slice(3, 6).map((r) => r._id.toHexString()), - { - relationFilter: { referenceName: { like: '%-one' } } - } - ) - ).rejects.toThrow('Unable to find all testReferences to set on TestDiscriminatedEntity'); - }); - }); - }); - - describe('#setRelation', () => { - it('call select and return the result', async () => { - const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; - const queryService = moduleRef.get(TestReferenceService); - - const queryResult = await queryService.setRelation( - 'testEntity', - entity._id.toHexString(), - TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString() - ); - - expect(queryResult).toEqual( - expect.objectContaining({ ...entity, testEntity: TEST_DISCRIMINATED_ENTITIES[1]._id }) - ); - - const relation = await queryService.findRelation(TestEntity, 'testEntity', entity); - expect(relation).toEqual(TEST_DISCRIMINATED_ENTITIES[1]); - }); - - it('should reject with a virtual reference', async () => { - const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.setRelation( - 'virtualTestEntity', - entity._id.toHexString(), - TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString() - ) - ).rejects.toThrow('SetRelation not supported for virtual relation virtualTestEntity'); - }); - - 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_REFERENCES_FOR_DISCRIMINATES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.setRelation( - 'testEntity', - entity._id.toHexString(), - TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString(), - { - filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName } } - } - ) - ).rejects.toThrow(`Unable to find TestReference with id: ${String(entity._id)}`); - }); - - it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { - const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.setRelation( - 'TestDiscriminatedEntity', - entity._id.toHexString(), - TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString(), - { - relationFilter: { stringType: { like: '%-one' } } - } - ) - ).rejects.toThrow('Unable to find reference TestDiscriminatedEntity on TestReference'); - }); - }); - }); - - describe('#removeRelation', () => { - it('call select and return the result', async () => { - const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; - const queryService = moduleRef.get(TestReferenceService); - const queryResult = await queryService.removeRelation( - 'testEntity', - entity._id.toHexString(), - TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString() - ); - const { testEntity, ...expected } = entity; - expect(queryResult).toEqual(expect.objectContaining(expected)); - - const relation = await queryService.findRelation(TestEntity, 'testEntity', entity); - expect(relation).toBeUndefined(); - }); - - it('should reject with a virtual reference', async () => { - const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.removeRelation( - 'virtualTestEntity', - entity._id.toHexString(), - TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString() - ) - ).rejects.toThrow('RemoveRelation not supported for virtual relation virtualTestEntity'); - }); - - 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_REFERENCES_FOR_DISCRIMINATES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.removeRelation( - 'testEntity', - entity._id.toHexString(), - TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString(), - { - filter: { referenceName: { eq: TEST_REFERENCES_FOR_DISCRIMINATES[1].referenceName } } - } - ) - ).rejects.toThrow(`Unable to find TestReference with id: ${String(entity._id)}`); - }); - - it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { - const entity = TEST_REFERENCES_FOR_DISCRIMINATES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.removeRelation( - 'TestDiscriminatedEntity', - entity._id.toHexString(), - TEST_DISCRIMINATED_ENTITIES[1]._id.toHexString(), - { - relationFilter: { stringType: { like: '%-one' } } - } - ) - ).rejects.toThrow('Unable to find reference TestDiscriminatedEntity on TestReference'); - }); - }); - }); - - describe('#removeRelations', () => { - it('call select and return the result', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.removeRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id.toHexString()) - ); - expect(queryResult.toObject()).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: [] - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations).toHaveLength(0); - }); - - it('should not modify relations if relationIds is empty', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - const queryResult = await queryService.removeRelations('testReferences', entity._id.toHexString(), []); - expect(queryResult.toObject()).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: expect.arrayContaining(TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id)) - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations).toHaveLength(3); - }); - - describe('with virtual reference', () => { - it('should return a rejected promise', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.removeRelations( - 'virtualTestReferences', - entity._id.toHexString(), - TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id.toHexString()) - ) - ).rejects.toThrow('RemoveRelations not supported for virtual relation virtualTestReferences'); - }); - }); - - 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_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.removeRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id.toHexString()), - { - filter: { stringType: { eq: TEST_DISCRIMINATED_ENTITIES[1].stringType } } - } - ) - ).rejects.toThrow(`Unable to find TestDiscriminatedEntity with id: ${String(entity._id)}`); - }); - - it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { - const entity = TEST_DISCRIMINATED_ENTITIES[0]; - const queryService = moduleRef.get(TestDiscriminatedEntityService); - return expect( - queryService.removeRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES_FOR_DISCRIMINATES.slice(0, 3).map((r) => r._id.toHexString()), - { - relationFilter: { referenceName: { like: '%-one' } } - } - ) - ).rejects.toThrow('Unable to find all testReferences to remove from TestDiscriminatedEntity'); - }); - }); - }); -}); diff --git a/packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts b/packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts new file mode 100644 index 000000000..ebe444ade --- /dev/null +++ b/packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts @@ -0,0 +1,1560 @@ +/* eslint-disable no-underscore-dangle,@typescript-eslint/no-unsafe-return */ +import { getModelForClass, DocumentType, mongoose } from '@typegoose/typegoose'; +import { Test, TestingModule } from '@nestjs/testing'; +import { ReturnModelType } from '@typegoose/typegoose/lib/types'; +import { InjectModel, TypegooseModule } from 'nestjs-typegoose'; +import { FindRelationOptions, SortDirection } from '@ptc-org/nestjs-query-core'; +import { + TestReference, + TestEntity, + TEST_ENTITIES, + TEST_DISCRIMINATED_ENTITIES, + TEST_REFERENCES, + getConnectionUri, + prepareDb, + closeDbConnection, + dropDatabase +} from '../__fixtures__'; +import { NestjsQueryTypegooseModule } from '../../src'; +import { TypegooseQueryService } from '../../src/services'; + +const { Types } = mongoose; + +describe('TypegooseQueryService', () => { + let moduleRef: TestingModule; + let TestEntityModel: ReturnModelType; + let TestReferenceModel: ReturnModelType; + + class TestEntityService extends TypegooseQueryService { + constructor(@InjectModel(TestEntity) readonly model: ReturnModelType) { + super(model); + TestEntityModel = model; + } + } + + class TestReferenceService extends TypegooseQueryService { + constructor(@InjectModel(TestReference) readonly model: ReturnModelType) { + super(model); + TestReferenceModel = model; + } + } + + beforeAll(async () => { + moduleRef = await Test.createTestingModule({ + imports: [ + TypegooseModule.forRoot(await getConnectionUri()), + NestjsQueryTypegooseModule.forFeature([TestEntity, TestReference]) + ], + providers: [TestReferenceService, TestEntityService] + }).compile(); + }); + + function convertDocument(doc: DocumentType): Doc { + return doc.toObject({ virtuals: true }) as Doc; + } + + function convertDocuments(docs: DocumentType[]): Doc[] { + return docs.map((doc) => convertDocument(doc)); + } + + function testEntityToObject(te: TestEntity): Partial { + return { + _id: te._id, + stringType: te.stringType, + boolType: te.boolType, + numberType: te.numberType, + dateType: te.dateType + }; + } + + function testEntityToCreate(te: TestEntity): Partial { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { _id, ...insert } = testEntityToObject(te); + return insert; + } + + function expectEqualCreate(result: TestEntity[], expected: TestEntity[]) { + const cleansedResults = result.map(testEntityToCreate); + const cleansedExpected = expected.map(testEntityToCreate); + expect(cleansedResults).toEqual(cleansedExpected); + } + + afterAll(async () => closeDbConnection()); + + beforeEach(async () => prepareDb()); + + afterEach(async () => dropDatabase()); + + describe('#query', () => { + it('call find and return the result', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({}); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES)); + }); + + it('should support eq operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { stringType: { eq: 'foo1' } } }); + expect(convertDocuments(queryResult)).toEqual([TEST_ENTITIES[0]]); + }); + + it('should support neq operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { stringType: { neq: 'foo1' } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(1))); + }); + + it('should support gt operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { numberType: { gt: 5 } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(5))); + }); + + it('should support gte operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { numberType: { gte: 5 } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(4))); + }); + + it('should support lt operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { numberType: { lt: 5 } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(0, 4))); + }); + + it('should support lte operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { numberType: { lte: 5 } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(0, 5))); + }); + + it('should support in operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { numberType: { in: [1, 2, 3] } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(0, 3))); + }); + + it('should support notIn operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { numberType: { notIn: [1, 2, 3] } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(4))); + }); + + it('should support is operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { boolType: { is: true } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.filter((e) => e.boolType))); + }); + + it('should support isNot operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { boolType: { isNot: true } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.filter((e) => !e.boolType))); + }); + + it('should support like operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { stringType: { like: 'foo%' } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES)); + }); + + it('should support notLike operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { stringType: { notLike: 'foo%' } } }); + expect(convertDocuments(queryResult)).toEqual([]); + }); + + it('should support iLike operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { stringType: { iLike: 'FOO%' } } }); + expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES)); + }); + + it('should support notILike operator', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.query({ filter: { stringType: { notILike: 'FOO%' } } }); + expect(convertDocuments(queryResult)).toEqual([]); + }); + }); + + describe('#aggregate', () => { + it('call select with the aggregate columns and return the result', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.aggregate( + {}, + { + count: ['id'], + avg: ['numberType'], + sum: ['numberType'], + max: ['id', 'dateType', 'numberType', 'stringType'], + min: ['id', 'dateType', 'numberType', 'stringType'] + } + ); + return expect(queryResult).toEqual([ + { + avg: { + numberType: 10.5 + }, + count: { + id: 20 + }, + max: { + dateType: TEST_DISCRIMINATED_ENTITIES[9].dateType, + numberType: 20, + stringType: 'foo9', + id: expect.any(Types.ObjectId) + }, + min: { + dateType: TEST_ENTITIES[0].dateType, + numberType: 1, + stringType: 'foo1', + id: expect.any(Types.ObjectId) + }, + sum: { + numberType: 210 + } + } + ]); + }); + + it('allow aggregates with groupBy', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.aggregate( + {}, + { + groupBy: ['boolType'], + count: ['id'], + avg: ['numberType'], + sum: ['numberType'], + max: ['id', 'dateType', 'numberType', 'stringType'], + min: ['id', 'dateType', 'numberType', 'stringType'] + } + ); + return expect(queryResult).toEqual([ + { + groupBy: { + boolType: false + }, + avg: { + numberType: 10 + }, + count: { + id: 10 + }, + max: { + dateType: TEST_DISCRIMINATED_ENTITIES[8].dateType, + numberType: 19, + stringType: 'foo9', + id: expect.any(Types.ObjectId) + }, + min: { + dateType: TEST_ENTITIES[0].dateType, + numberType: 1, + stringType: 'foo1', + id: expect.any(Types.ObjectId) + }, + sum: { + numberType: 100 + } + }, + { + groupBy: { + boolType: true + }, + avg: { + numberType: 11 + }, + count: { + id: 10 + }, + max: { + dateType: TEST_DISCRIMINATED_ENTITIES[9].dateType, + numberType: 20, + stringType: 'foo8', + id: expect.any(Types.ObjectId) + }, + min: { + dateType: TEST_ENTITIES[1].dateType, + numberType: 2, + stringType: 'foo10', + id: expect.any(Types.ObjectId) + }, + sum: { + numberType: 110 + } + } + ]); + }); + + it('call select with the aggregate columns and return the result with a filter', async () => { + const queryService = moduleRef.get(TestEntityService); + const queryResult = await queryService.aggregate( + { stringType: { in: ['foo1', 'foo2', 'foo3'] } }, + { + count: ['id'], + avg: ['numberType'], + sum: ['numberType'], + max: ['id', 'dateType', 'numberType', 'stringType'], + min: ['id', 'dateType', 'numberType', 'stringType'] + } + ); + return expect(queryResult).toEqual([ + { + avg: { + numberType: 2 + }, + count: { + id: 3 + }, + max: { + dateType: TEST_ENTITIES[2].dateType, + numberType: 3, + stringType: 'foo3', + id: expect.any(Types.ObjectId) + }, + min: { + dateType: TEST_ENTITIES[0].dateType, + numberType: 1, + stringType: 'foo1', + id: expect.any(Types.ObjectId) + }, + sum: { + numberType: 6 + } + } + ]); + }); + }); + + describe('#count', () => { + it('should return number of elements matching a query', async () => { + const queryService = moduleRef.get(TestEntityService); + const expectedEntities = TEST_ENTITIES.slice(0, 2); + const count = await queryService.count({ stringType: { in: expectedEntities.map((e) => e.stringType) } }); + expect(count).toBe(2); + }); + }); + + describe('#findById', () => { + it('return the entity if found', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + const found = await queryService.findById(entity._id.toHexString()); + expect(convertDocument(found!)).toEqual(entity); + }); + + it('return undefined if not found', async () => { + const queryService = moduleRef.get(TestEntityService); + const found = await queryService.findById(new Types.ObjectId().toHexString()); + expect(found).toBeUndefined(); + }); + + describe('with filter', () => { + it('should return an entity if all filters match', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + const found = await queryService.findById(entity._id.toHexString(), { + filter: { stringType: { eq: entity.stringType } } + }); + expect(convertDocument(found!)).toEqual(entity); + }); + + it('should return an undefined if an entity with the pk and filter is not found', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + const found = await queryService.findById(entity._id.toHexString(), { + filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } + }); + expect(found).toBeUndefined(); + }); + }); + }); + + describe('#getById', () => { + it('return the entity if found', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + const found = await queryService.getById(entity._id.toHexString()); + expect(convertDocument(found)).toEqual(entity); + }); + + it('return undefined if not found', () => { + const badId = new Types.ObjectId().toHexString(); + const queryService = moduleRef.get(TestEntityService); + return expect(queryService.getById(badId)).rejects.toThrow(`Unable to find TestEntity with id: ${badId}`); + }); + + describe('with filter', () => { + it('should return an entity if all filters match', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + const found = await queryService.getById(entity._id.toHexString(), { + filter: { stringType: { eq: entity.stringType } } + }); + expect(convertDocument(found)).toEqual(entity); + }); + + it('should return an undefined if an entity with the pk and filter is not found', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + return expect( + queryService.getById(entity._id.toHexString(), { + filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } + }) + ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); + }); + }); + }); + + describe('#createMany', () => { + it('call save on the repo with instances of entities when passed plain objects', async () => { + const queryService = moduleRef.get(TestEntityService); + const created = await queryService.createMany(TEST_ENTITIES.map(testEntityToCreate)); + expectEqualCreate(created, TEST_ENTITIES); + }); + + it('call save on the repo with instances of entities when passed instances', async () => { + const instances = TEST_ENTITIES.map((e) => testEntityToCreate(e)); + const queryService = moduleRef.get(TestEntityService); + const created = await queryService.createMany(instances); + expectEqualCreate(created, TEST_ENTITIES); + }); + + it('should reject if the entities already exist', async () => { + const queryService = moduleRef.get(TestEntityService); + return expect(queryService.createMany(TEST_ENTITIES)).rejects.toThrow('Id cannot be specified when updating or creating'); + }); + }); + + describe('#createOne', () => { + it('call save on the repo with an instance of the entity when passed a plain object', async () => { + const entity = testEntityToCreate(TEST_ENTITIES[0]); + const queryService = moduleRef.get(TestEntityService); + const created = await queryService.createOne(entity); + expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); + }); + + it('call save on the repo with an instance of the entity when passed an instance', async () => { + const Model = getModelForClass(TestEntity); + const entity = new Model(testEntityToCreate(TEST_ENTITIES[0])); + const outcome = testEntityToObject(entity); + const queryService = moduleRef.get(TestEntityService); + const created = await queryService.createOne(entity); + expect(convertDocument(created)).toEqual(expect.objectContaining(outcome)); + }); + + it('should reject if the entity contains an id', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + return expect(queryService.createOne({ ...entity })).rejects.toThrow('Id cannot be specified when updating or creating'); + }); + + it('should not reject if the entity contains an undefined id', async () => { + const entity = testEntityToCreate(TEST_ENTITIES[0]); + const queryService = moduleRef.get(TestEntityService); + const created = await queryService.createOne({ ...entity, id: undefined }); + expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); + }); + + it('should not reject if the entity contains an undefined _id', async () => { + const entity = testEntityToCreate(TEST_ENTITIES[0]); + const queryService = moduleRef.get(TestEntityService); + const created = await queryService.createOne({ ...entity, _id: undefined }); + expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); + }); + }); + + describe('#deleteMany', () => { + it('delete all records that match the query', async () => { + const queryService = moduleRef.get(TestEntityService); + const entities = TEST_ENTITIES.slice(0, 5); + const { deletedCount } = await queryService.deleteMany({ + stringType: { in: entities.map((e) => e.stringType) } + }); + expect(deletedCount).toBe(5); + const allCount = await queryService.count({}); + expect(allCount).toBe(15); + }); + }); + + describe('#deleteOne', () => { + it('remove the entity', async () => { + const queryService = moduleRef.get(TestEntityService); + const deleted = await queryService.deleteOne(TEST_ENTITIES[0]._id.toHexString()); + expect(convertDocument(deleted)).toEqual(TEST_ENTITIES[0]); + }); + + it('call fail if the entity is not found', async () => { + const badId = new Types.ObjectId().toHexString(); + const queryService = moduleRef.get(TestEntityService); + return expect(queryService.deleteOne(badId)).rejects.toThrow(`Unable to find TestEntity with id: ${badId}`); + }); + + describe('with filter', () => { + it('should delete the entity if all filters match', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + const deleted = await queryService.deleteOne(entity._id.toHexString(), { + filter: { stringType: { eq: entity.stringType } } + }); + expect(convertDocument(deleted)).toEqual(TEST_ENTITIES[0]); + }); + + it('should return throw an error if unable to find', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + return expect( + queryService.deleteOne(entity._id.toHexString(), { + filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } + }) + ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); + }); + }); + }); + + describe('#updateMany', () => { + it('update all entities in the filter', async () => { + const queryService = moduleRef.get(TestEntityService); + const filter = { + stringType: { in: TEST_ENTITIES.slice(0, 5).map((e) => e.stringType) } + }; + await queryService.updateMany({ stringType: 'updated' }, filter); + const entities = await queryService.query({ filter: { stringType: { eq: 'updated' } } }); + expect(entities).toHaveLength(5); + }); + + it('should reject if the update contains the ID', () => { + const queryService = moduleRef.get(TestEntityService); + return expect(queryService.updateMany({ id: new Types.ObjectId().toHexString() }, {})).rejects.toThrow( + 'Id cannot be specified when updating' + ); + }); + + it('should not reject if the update contains an undefined id', async () => { + const queryService = moduleRef.get(TestEntityService); + const entitiesToUpdate = TEST_ENTITIES.slice(0, 5); + const filter = { + stringType: { in: entitiesToUpdate.map((e) => e.stringType) } + }; + await queryService.updateMany({ stringType: 'updated', id: undefined }, filter); + const entities = await queryService.query({ filter: { stringType: { eq: 'updated' } } }); + expect(entities).toHaveLength(entitiesToUpdate.length); + expect(new Set(entities.map((e) => e.id))).toEqual(new Set(entitiesToUpdate.map((e) => e.id))); + }); + }); + + describe('#updateOne', () => { + it('update the entity', async () => { + const queryService = moduleRef.get(TestEntityService); + const entity = TEST_ENTITIES[0]; + const update = { stringType: 'updated' }; + const updated = await queryService.updateOne(entity._id.toHexString(), update); + expect(updated).toEqual( + expect.objectContaining({ + _id: entity._id, + ...update + }) + ); + }); + + it('update the entity with an instance of the entity', async () => { + const queryService = moduleRef.get(TestEntityService); + const entity = TEST_ENTITIES[0]; + const Model = getModelForClass(TestEntity); + const update = new Model({ stringType: 'updated' }); + const updated = await queryService.updateOne(entity._id.toHexString(), update); + expect(updated).toEqual( + expect.objectContaining({ + _id: entity._id, + stringType: 'updated' + }) + ); + }); + + it('should reject if the update contains the ID', async () => { + const queryService = moduleRef.get(TestEntityService); + return expect(queryService.updateOne(TEST_ENTITIES[0]._id.toHexString(), { _id: new Types.ObjectId() })).rejects.toThrow( + 'Id cannot be specified when updating' + ); + }); + + it('call fail if the entity is not found', async () => { + const badId = new Types.ObjectId().toHexString(); + const queryService = moduleRef.get(TestEntityService); + return expect(queryService.updateOne(badId, { stringType: 'updated' })).rejects.toThrow( + `Unable to find TestEntity with id: ${badId}` + ); + }); + + describe('with filter', () => { + it('should update the entity if all filters match', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + const update = { stringType: 'updated' }; + const updated = await queryService.updateOne(entity._id.toHexString(), update, { + filter: { stringType: { eq: entity.stringType } } + }); + expect(updated).toEqual(expect.objectContaining({ _id: entity._id, ...update })); + }); + + it('should throw an error if unable to find the entity', async () => { + const entity = TEST_ENTITIES[0]; + const queryService = moduleRef.get(TestEntityService); + return expect( + queryService.updateOne( + entity._id.toHexString(), + { stringType: 'updated' }, + { filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } } + ) + ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); + }); + }); + }); + + // describe('#findRelation', () => { + // describe('with one entity', () => { + // it('call select and return the result', async () => { + // const entity = TEST_ENTITIES[0]; + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.findRelation(TestReference, 'testReference', entity); + // expect(queryResult).toEqual(TEST_REFERENCES[0]); + // }); + // + // it('apply the filter option', async () => { + // const entity = TEST_ENTITIES[0]; + // const queryService = moduleRef.get(TestEntityService); + // const queryResult1 = await queryService.findRelation(TestReference, 'testReference', entity, { + // filter: { referenceName: { eq: TEST_REFERENCES[0].referenceName } } + // }); + // expect(queryResult1).toEqual(TEST_REFERENCES[0]); + // + // const queryResult2 = await queryService.findRelation(TestReference, 'testReference', entity, { + // filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } + // }); + // expect(queryResult2).toBeUndefined(); + // }); + // + // it('should return undefined select if no results are found.', async () => { + // const entity = TEST_ENTITIES[0]; + // await TestEntityModel.updateOne({ _id: entity._id }, { $set: { testReference: undefined } }); + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.findRelation(TestReference, 'testReference', entity); + // expect(queryResult).toBeUndefined(); + // }); + // + // it('throw an error if a relation with that name is not found.', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const entity = TEST_ENTITIES[0]; + // return expect(queryService.findRelation(TestReference, 'badReference', entity)).rejects.toThrow( + // 'Unable to find reference badReference on TestEntity' + // ); + // }); + // + // describe('virtual reference', () => { + // it('call select and return the result', async () => { + // const entity = TEST_REFERENCES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // const queryResult = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity); + // + // expect(queryResult).toEqual(TEST_ENTITIES[0]); + // }); + // + // it('apply the filter option', async () => { + // const entity = TEST_REFERENCES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // const queryResult1 = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity, { + // filter: { stringType: { eq: TEST_ENTITIES[0].stringType } } + // }); + // expect(queryResult1).toEqual(TEST_ENTITIES[0]); + // + // const queryResult2 = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity, { + // filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } + // }); + // expect(queryResult2).toBeUndefined(); + // }); + // + // it('should return undefined select if no results are found.', async () => { + // const entity = TEST_REFERENCES[0]; + // await TestReferenceModel.updateOne({ _id: entity._id }, { $set: { testEntity: undefined } }); + // const queryService = moduleRef.get(TestReferenceService); + // const queryResult = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity); + // expect(queryResult).toBeUndefined(); + // }); + // + // it('throw an error if a relation with that name is not found.', async () => { + // const entity = TEST_REFERENCES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect(queryService.findRelation(TestEntity, 'badReference', entity)).rejects.toThrow( + // 'Unable to find reference badReference on TestReference' + // ); + // }); + // }); + // }); + // + // describe('with multiple entities', () => { + // it('call select and return the result', async () => { + // const entities = TEST_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.findRelation(TestReference, 'testReference', entities, {}); + // + // expect(queryResult).toEqual( + // new Map([ + // [entities[0], expect.objectContaining(TEST_REFERENCES[0])], + // [entities[1], expect.objectContaining(TEST_REFERENCES[3])], + // [entities[2], expect.objectContaining(TEST_REFERENCES[6])] + // ]) + // ); + // }); + // + // it('should apply the filter option', async () => { + // const entities = TEST_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestEntityService); + // const options: FindRelationOptions = { + // filter: { + // _id: { in: [TEST_REFERENCES[0]._id, TEST_REFERENCES[6]._id] } + // } + // }; + // const queryResult = await queryService.findRelation(TestReference, 'testReference', entities, options); + // expect(queryResult).toEqual( + // new Map([ + // [entities[0], expect.objectContaining(TEST_REFERENCES[0])], + // [entities[1], undefined], + // [entities[2], expect.objectContaining(TEST_REFERENCES[6])] + // ]) + // ); + // }); + // + // it('should return undefined select if no results are found.', async () => { + // const entities: DocumentType[] = [ + // TEST_ENTITIES[0], + // { _id: new Types.ObjectId() } as DocumentType + // ]; + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.findRelation(TestReference, 'testReference', entities); + // + // expect(queryResult).toEqual( + // new Map([ + // [entities[0], expect.objectContaining(TEST_REFERENCES[0])], + // [entities[1], undefined] + // ]) + // ); + // }); + // }); + // }); + // + // describe('#queryRelations', () => { + // describe('with one entity', () => { + // it('call select and return the result', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', TEST_ENTITIES[0], { + // filter: { referenceName: { isNot: null } } + // }); + // return expect(queryResult).toEqual(TEST_REFERENCES.slice(0, 3)); + // }); + // + // it('should apply a filter', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', TEST_ENTITIES[0], { + // filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } + // }); + // expect(queryResult).toEqual([TEST_REFERENCES[1]]); + // }); + // + // it('should apply paging', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', TEST_ENTITIES[0], { + // paging: { limit: 2, offset: 1 } + // }); + // expect(queryResult).toEqual(TEST_REFERENCES.slice(1, 3)); + // }); + // }); + // + // describe('with virtual entity', () => { + // it('call select and return the result', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.queryRelations( + // TestReference, + // 'virtualTestReferences', + // TEST_ENTITIES[0], + // { + // filter: { referenceName: { isNot: null } } + // } + // ); + // return expect(queryResult).toEqual(expect.arrayContaining(TEST_REFERENCES.slice(0, 3))); + // }); + // + // it('should apply a filter', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.queryRelations( + // TestReference, + // 'virtualTestReferences', + // TEST_ENTITIES[0], + // { + // filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } + // } + // ); + // expect(queryResult).toEqual([TEST_REFERENCES[1]]); + // }); + // + // it('should apply paging', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.queryRelations( + // TestReference, + // 'virtualTestReferences', + // TEST_ENTITIES[0], + // { + // paging: { limit: 2, offset: 1 }, + // sorting: [{ field: 'referenceName', direction: SortDirection.ASC }] + // } + // ); + // expect(queryResult).toEqual(TEST_REFERENCES.slice(1, 3)); + // }); + // }); + // + // describe('with multiple entities', () => { + // it('call return a map of results for each entity', async () => { + // const entities = TEST_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { + // filter: { referenceName: { isNot: null } } + // }); + // expect(queryResult.size).toBe(3); + // expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES.slice(0, 3)); + // expect(queryResult.get(entities[1])).toEqual(TEST_REFERENCES.slice(3, 6)); + // expect(queryResult.get(entities[2])).toEqual(TEST_REFERENCES.slice(6, 9)); + // }); + // + // it('should apply a filter per entity', async () => { + // const entities = TEST_ENTITIES.slice(0, 3); + // const references = [TEST_REFERENCES[1], TEST_REFERENCES[4], TEST_REFERENCES[7]]; + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { + // filter: { referenceName: { in: references.map((r) => r.referenceName) } } + // }); + // expect(queryResult.size).toBe(3); + // expect(queryResult.get(entities[0])).toEqual([references[0]]); + // expect(queryResult.get(entities[1])).toEqual([references[1]]); + // expect(queryResult.get(entities[2])).toEqual([references[2]]); + // }); + // + // it('should apply paging per entity', async () => { + // const entities = TEST_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { + // paging: { limit: 2, offset: 1 } + // }); + // expect(queryResult.size).toBe(3); + // expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES.slice(1, 3)); + // expect(queryResult.get(entities[1])).toEqual(TEST_REFERENCES.slice(4, 6)); + // expect(queryResult.get(entities[2])).toEqual(TEST_REFERENCES.slice(7, 9)); + // }); + // + // it('should return an empty array if no results are found.', async () => { + // const entities: DocumentType[] = [ + // TEST_ENTITIES[0], + // { _id: new Types.ObjectId() } as DocumentType + // ]; + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { + // filter: { referenceName: { isNot: null } } + // }); + // expect(queryResult.size).toBe(2); + // expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES.slice(0, 3)); + // expect(queryResult.get(entities[1])).toEqual([]); + // }); + // }); + // }); + // + // describe('#aggregateRelations', () => { + // describe('with one entity', () => { + // it('should return an aggregate', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const aggResult = await queryService.aggregateRelations( + // TestReference, + // 'testReferences', + // TEST_ENTITIES[0], + // { referenceName: { isNot: null } }, + // { count: ['id'] } + // ); + // return expect(aggResult).toEqual([ + // { + // count: { + // id: 3 + // } + // } + // ]); + // }); + // + // it('should support groupBy when aggregating relations', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const aggResult = await queryService.aggregateRelations( + // TestReference, + // 'testReferences', + // TEST_ENTITIES[0], + // { referenceName: { isNot: null } }, + // { groupBy: ['testEntity'], count: ['id'] } + // ); + // return expect(aggResult).toEqual([ + // { + // groupBy: { testEntity: TEST_ENTITIES[0]._id }, + // count: { + // id: 3 + // } + // } + // ]); + // }); + // }); + // + // describe('with virtual relation', () => { + // it('call select and return the result', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const aggResult = await queryService.aggregateRelations( + // TestReference, + // 'virtualTestReferences', + // TEST_ENTITIES[0], + // { referenceName: { isNot: null } }, + // { count: ['id'] } + // ); + // return expect(aggResult).toEqual([ + // { + // count: { + // id: 3 + // } + // } + // ]); + // }); + // }); + // + // describe('with multiple entities', () => { + // it('return a relation aggregate for each entity', async () => { + // const entities = TEST_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.aggregateRelations( + // TestReference, + // 'testReferences', + // entities, + // { referenceName: { isNot: null } }, + // { + // count: ['id', 'referenceName', 'testEntity'], + // min: ['id', 'referenceName', 'testEntity'], + // max: ['id', 'referenceName', 'testEntity'] + // } + // ); + // + // expect(queryResult.size).toBe(3); + // expect(queryResult).toEqual( + // new Map([ + // [ + // entities[0], + // [ + // { + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES[2].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES[0].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ], + // [ + // entities[1], + // [ + // { + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES[5].referenceName, + // testEntity: entities[1]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES[3].referenceName, + // testEntity: entities[1]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ], + // [ + // entities[2], + // [ + // { + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES[8].referenceName, + // testEntity: entities[2]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES[6].referenceName, + // testEntity: entities[2]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ] + // ]) + // ); + // }); + // + // it('aggregate and group for each entities relation', async () => { + // const entities = TEST_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.aggregateRelations( + // TestReference, + // 'testReferences', + // entities, + // { referenceName: { isNot: null } }, + // { + // groupBy: ['testEntity'], + // count: ['id', 'referenceName', 'testEntity'], + // min: ['id', 'referenceName', 'testEntity'], + // max: ['id', 'referenceName', 'testEntity'] + // } + // ); + // + // expect(queryResult.size).toBe(3); + // expect(queryResult).toEqual( + // new Map([ + // [ + // entities[0], + // [ + // { + // groupBy: { testEntity: entities[0]._id }, + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES[2].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES[0].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ], + // [ + // entities[1], + // [ + // { + // groupBy: { testEntity: entities[1]._id }, + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES[5].referenceName, + // testEntity: entities[1]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES[3].referenceName, + // testEntity: entities[1]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ], + // [ + // entities[2], + // [ + // { + // groupBy: { testEntity: entities[2]._id }, + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES[8].referenceName, + // testEntity: entities[2]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES[6].referenceName, + // testEntity: entities[2]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ] + // ]) + // ); + // }); + // + // it('should return an empty array if no results are found.', async () => { + // const entities: DocumentType[] = [ + // TEST_ENTITIES[0], + // { _id: new Types.ObjectId() } as DocumentType + // ]; + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.aggregateRelations( + // TestReference, + // 'testReferences', + // entities, + // { referenceName: { isNot: null } }, + // { + // count: ['id', 'referenceName', 'testEntity'], + // min: ['id', 'referenceName', 'testEntity'], + // max: ['id', 'referenceName', 'testEntity'] + // } + // ); + // + // expect(queryResult).toEqual( + // new Map([ + // [ + // entities[0], + // [ + // { + // count: { + // referenceName: 3, + // testEntity: 3, + // id: 3 + // }, + // max: { + // referenceName: TEST_REFERENCES[2].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // }, + // min: { + // referenceName: TEST_REFERENCES[0].referenceName, + // testEntity: entities[0]._id, + // id: expect.any(Types.ObjectId) + // } + // } + // ] + // ], + // [entities[1], []] + // ]) + // ); + // }); + // }); + // }); + // + // describe('#countRelations', () => { + // describe('with one entity', () => { + // it('count the references', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const entity = TEST_ENTITIES[0]; + // const countResult = await queryService.countRelations(TestReference, 'testReferences', entity, { + // referenceName: { in: [TEST_REFERENCES[1].referenceName, TEST_REFERENCES[2].referenceName] } + // }); + // return expect(countResult).toBe(2); + // }); + // + // it('should return a rejected promise if the relation is not found', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const entity = TEST_ENTITIES[0]; + // return expect( + // queryService.countRelations(TestReference, 'badReferences', entity, { + // referenceName: { in: [TEST_REFERENCES[1].referenceName, TEST_REFERENCES[2].referenceName] } + // }) + // ).rejects.toThrow('Unable to find reference badReferences on TestEntity'); + // }); + // }); + // + // describe('with virtual entity', () => { + // it('count references', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const entity = TEST_ENTITIES[0]; + // const countResult = await queryService.countRelations(TestReference, 'virtualTestReferences', entity, {}); + // return expect(countResult).toBe(3); + // }); + // it('count and return the result', async () => { + // const queryService = moduleRef.get(TestEntityService); + // const entity = TEST_ENTITIES[0]; + // const countResult = await queryService.countRelations(TestReference, 'virtualTestReferences', entity, { + // referenceName: { in: [TEST_REFERENCES[1].referenceName, TEST_REFERENCES[2].referenceName] } + // }); + // return expect(countResult).toBe(2); + // }); + // }); + // + // describe('with multiple entities', () => { + // it('call count and return the result', async () => { + // const entities = TEST_ENTITIES.slice(0, 3); + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.countRelations(TestReference, 'testReferences', entities, { + // referenceName: { + // in: [ + // TEST_REFERENCES[1].referenceName, + // TEST_REFERENCES[2].referenceName, + // TEST_REFERENCES[4].referenceName, + // TEST_REFERENCES[5].referenceName, + // TEST_REFERENCES[7].referenceName, + // TEST_REFERENCES[8].referenceName + // ] + // } + // }); + // + // expect(queryResult).toEqual( + // new Map([ + // [entities[0], 2], + // [entities[1], 2], + // [entities[2], 2] + // ]) + // ); + // }); + // }); + // }); + // + // describe('#addRelations', () => { + // it('call select and return the result', async () => { + // const entity = TEST_ENTITIES[0]; + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.addRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()) + // ); + // expect(queryResult).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: expect.arrayContaining(TEST_REFERENCES.slice(0, 6).map((r) => r._id)) + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations).toHaveLength(6); + // }); + // + // it('should not modify relations if relationIds is empty', async () => { + // const entity = TEST_ENTITIES[0]; + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.addRelations('testReferences', entity._id.toHexString(), []); + // expect(queryResult).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: expect.arrayContaining(TEST_REFERENCES.slice(0, 3).map((r) => r._id)) + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations).toHaveLength(3); + // }); + // + // describe('with virtual reference', () => { + // it('should return a rejected promise', async () => { + // const entity = TEST_ENTITIES[0]; + // const queryService = moduleRef.get(TestEntityService); + // return expect( + // queryService.addRelations( + // 'virtualTestReferences', + // entity._id.toHexString(), + // TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()) + // ) + // ).rejects.toThrow('AddRelations not supported for virtual relation virtualTestReferences'); + // }); + // }); + // + // 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.addRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()), + // { + // filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } + // } + // ) + // ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); + // }); + // + // 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.addRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()), + // { + // relationFilter: { referenceName: { like: '%-one' } } + // } + // ) + // ).rejects.toThrow('Unable to find all testReferences to add to TestEntity'); + // }); + // }); + // }); + // + // describe('#setRelations', () => { + // it('set all relations on the entity', async () => { + // const entity = TEST_ENTITIES[0]; + // const queryService = moduleRef.get(TestEntityService); + // const relationIds = TEST_REFERENCES.slice(3, 6).map((r) => r._id); + // const queryResult = await queryService.setRelations( + // 'testReferences', + // entity._id.toHexString(), + // relationIds.map((id) => id.toHexString()) + // ); + // expect(queryResult).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: expect.arrayContaining(relationIds) + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations.map((r) => r._id)).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('testReferences', entity._id.toHexString(), []); + // expect(queryResult).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: expect.arrayContaining([]) + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations.map((r) => r._id)).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( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()), + // { + // filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } + // } + // ) + // ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); + // }); + // + // 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( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()), + // { + // relationFilter: { referenceName: { like: '%-one' } } + // } + // ) + // ).rejects.toThrow('Unable to find all testReferences to set on TestEntity'); + // }); + // }); + // }); + // + // describe('#setRelation', () => { + // it('call select and return the result', async () => { + // const entity = TEST_REFERENCES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // const queryResult = await queryService.setRelation( + // 'testEntity', + // entity._id.toHexString(), + // TEST_ENTITIES[1]._id.toHexString() + // ); + // expect(queryResult).toEqual(expect.objectContaining({ ...entity, testEntity: TEST_ENTITIES[1]._id })); + // + // const relation = await queryService.findRelation(TestEntity, 'testEntity', entity); + // expect(relation).toEqual(TEST_ENTITIES[1]); + // }); + // + // it('should reject with a virtual reference', async () => { + // const entity = TEST_REFERENCES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.setRelation('virtualTestEntity', entity._id.toHexString(), TEST_ENTITIES[1]._id.toHexString()) + // ).rejects.toThrow('SetRelation not supported for virtual relation virtualTestEntity'); + // }); + // + // 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_REFERENCES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.setRelation('testEntity', entity._id.toHexString(), TEST_ENTITIES[1]._id.toHexString(), { + // filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } + // }) + // ).rejects.toThrow(`Unable to find TestReference with id: ${String(entity._id)}`); + // }); + // + // it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { + // const entity = TEST_REFERENCES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.setRelation( + // 'testEntity', + // entity._id.toHexString(), + // TEST_ENTITIES[1]._id.toHexString(), + // { + // relationFilter: { stringType: { like: '%-one' } } + // } + // ) + // ).rejects.toThrow('Unable to find testEntity to set on TestReference'); + // }); + // }); + // }); + // + // describe('#removeRelation', () => { + // it('call select and return the result', async () => { + // const entity = TEST_REFERENCES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // const queryResult = await queryService.removeRelation( + // 'testEntity', + // entity._id.toHexString(), + // TEST_ENTITIES[1]._id.toHexString() + // ); + // const { testEntity, ...expected } = entity; + // expect(queryResult).toEqual(expect.objectContaining(expected)); + // + // const relation = await queryService.findRelation(TestEntity, 'testEntity', entity); + // expect(relation).toBeUndefined(); + // }); + // + // it('should reject with a virtual reference', async () => { + // const entity = TEST_REFERENCES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.removeRelation('virtualTestEntity', entity._id.toHexString(), TEST_ENTITIES[1]._id.toHexString()) + // ).rejects.toThrow('RemoveRelation not supported for virtual relation virtualTestEntity'); + // }); + // + // 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_REFERENCES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.removeRelation('testEntity', entity._id.toHexString(), TEST_ENTITIES[1]._id.toHexString(), { + // filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } + // }) + // ).rejects.toThrow(`Unable to find TestReference with id: ${String(entity._id)}`); + // }); + // + // it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { + // const entity = TEST_REFERENCES[0]; + // const queryService = moduleRef.get(TestReferenceService); + // return expect( + // queryService.removeRelation( + // 'testEntity', + // entity._id.toHexString(), + // TEST_ENTITIES[1]._id.toHexString(), + // { + // relationFilter: { stringType: { like: '%-one' } } + // } + // ) + // ).rejects.toThrow('Unable to find testEntity to remove from TestReference'); + // }); + // }); + // }); + // + // describe('#removeRelations', () => { + // it('call select and return the result', async () => { + // const entity = TEST_ENTITIES[0]; + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.removeRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES.slice(0, 3).map((r) => r._id.toHexString()) + // ); + // expect(queryResult.toObject()).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: [] + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations).toHaveLength(0); + // }); + // + // it('should not modify relations if relationIds is empty', async () => { + // const entity = TEST_ENTITIES[0]; + // const queryService = moduleRef.get(TestEntityService); + // const queryResult = await queryService.removeRelations('testReferences', entity._id.toHexString(), []); + // expect(queryResult.toObject()).toEqual( + // expect.objectContaining({ + // _id: entity._id, + // testReferences: expect.arrayContaining(TEST_REFERENCES.slice(0, 3).map((r) => r._id)) + // }) + // ); + // + // const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); + // expect(relations).toHaveLength(3); + // }); + // + // describe('with virtual reference', () => { + // it('should return a rejected promise', async () => { + // const entity = TEST_ENTITIES[0]; + // const queryService = moduleRef.get(TestEntityService); + // return expect( + // queryService.removeRelations( + // 'virtualTestReferences', + // entity._id.toHexString(), + // TEST_REFERENCES.slice(0, 3).map((r) => r._id.toHexString()) + // ) + // ).rejects.toThrow('RemoveRelations not supported for virtual relation virtualTestReferences'); + // }); + // }); + // + // 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.removeRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES.slice(0, 3).map((r) => r._id.toHexString()), + // { + // filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } + // } + // ) + // ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); + // }); + // + // 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.removeRelations( + // 'testReferences', + // entity._id.toHexString(), + // TEST_REFERENCES.slice(0, 3).map((r) => r._id.toHexString()), + // { + // relationFilter: { referenceName: { like: '%-one' } } + // } + // ) + // ).rejects.toThrow('Unable to find all testReferences to remove from TestEntity'); + // }); + // }); + // }); +}); diff --git a/packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts.disabled b/packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts.disabled deleted file mode 100644 index a15b3e997..000000000 --- a/packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts.disabled +++ /dev/null @@ -1,1564 +0,0 @@ -/* eslint-disable no-underscore-dangle,@typescript-eslint/no-unsafe-return */ -import { getModelForClass, DocumentType, mongoose } from '@typegoose/typegoose'; -import { Test, TestingModule } from '@nestjs/testing'; -import { ReturnModelType } from '@typegoose/typegoose/lib/types'; -import { InjectModel, TypegooseModule } from 'nestjs-typegoose'; -import { FindRelationOptions, SortDirection } from '@ptc-org/nestjs-query-core'; -import { - TestReference, - TestEntity, - TEST_ENTITIES, - TEST_DISCRIMINATED_ENTITIES, - TEST_REFERENCES, - getConnectionUri, - prepareDb, - closeDbConnection, - dropDatabase -} from '../__fixtures__'; -import { NestjsQueryTypegooseModule } from '../../src'; -import { TypegooseQueryService } from '../../src/services'; - -const { Types } = mongoose; - -describe('TypegooseQueryService', () => { - let moduleRef: TestingModule; - let TestEntityModel: ReturnModelType; - let TestReferenceModel: ReturnModelType; - - class TestEntityService extends TypegooseQueryService { - constructor(@InjectModel(TestEntity) readonly model: ReturnModelType) { - super(model); - TestEntityModel = model; - } - } - - class TestReferenceService extends TypegooseQueryService { - constructor(@InjectModel(TestReference) readonly model: ReturnModelType) { - super(model); - TestReferenceModel = model; - } - } - - beforeAll(async () => { - moduleRef = await Test.createTestingModule({ - imports: [ - TypegooseModule.forRoot(await getConnectionUri()), - NestjsQueryTypegooseModule.forFeature([TestEntity, TestReference]) - ], - providers: [TestReferenceService, TestEntityService] - }).compile(); - }); - - function convertDocument(doc: DocumentType): Doc { - return doc.toObject({ virtuals: true }) as Doc; - } - - function convertDocuments(docs: DocumentType[]): Doc[] { - return docs.map((doc) => convertDocument(doc)); - } - - function testEntityToObject(te: TestEntity): Partial { - return { - _id: te._id, - stringType: te.stringType, - boolType: te.boolType, - numberType: te.numberType, - dateType: te.dateType - }; - } - - function testEntityToCreate(te: TestEntity): Partial { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { _id, ...insert } = testEntityToObject(te); - return insert; - } - - function expectEqualCreate(result: TestEntity[], expected: TestEntity[]) { - const cleansedResults = result.map(testEntityToCreate); - const cleansedExpected = expected.map(testEntityToCreate); - expect(cleansedResults).toEqual(cleansedExpected); - } - - afterAll(async () => closeDbConnection()); - - beforeEach(async () => prepareDb()); - - afterEach(async () => dropDatabase()); - - describe('#query', () => { - it('call find and return the result', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({}); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES)); - }); - - it('should support eq operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { stringType: { eq: 'foo1' } } }); - expect(convertDocuments(queryResult)).toEqual([TEST_ENTITIES[0]]); - }); - - it('should support neq operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { stringType: { neq: 'foo1' } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(1))); - }); - - it('should support gt operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { numberType: { gt: 5 } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(5))); - }); - - it('should support gte operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { numberType: { gte: 5 } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(4))); - }); - - it('should support lt operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { numberType: { lt: 5 } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(0, 4))); - }); - - it('should support lte operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { numberType: { lte: 5 } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(0, 5))); - }); - - it('should support in operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { numberType: { in: [1, 2, 3] } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(0, 3))); - }); - - it('should support notIn operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { numberType: { notIn: [1, 2, 3] } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.slice(4))); - }); - - it('should support is operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { boolType: { is: true } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.filter((e) => e.boolType))); - }); - - it('should support isNot operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { boolType: { isNot: true } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES.filter((e) => !e.boolType))); - }); - - it('should support like operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { stringType: { like: 'foo%' } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES)); - }); - - it('should support notLike operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { stringType: { notLike: 'foo%' } } }); - expect(convertDocuments(queryResult)).toEqual([]); - }); - - it('should support iLike operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { stringType: { iLike: 'FOO%' } } }); - expect(convertDocuments(queryResult)).toEqual(expect.arrayContaining(TEST_ENTITIES)); - }); - - it('should support notILike operator', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.query({ filter: { stringType: { notILike: 'FOO%' } } }); - expect(convertDocuments(queryResult)).toEqual([]); - }); - }); - - describe('#aggregate', () => { - it('call select with the aggregate columns and return the result', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.aggregate( - {}, - { - count: ['id'], - avg: ['numberType'], - sum: ['numberType'], - max: ['id', 'dateType', 'numberType', 'stringType'], - min: ['id', 'dateType', 'numberType', 'stringType'] - } - ); - return expect(queryResult).toEqual([ - { - avg: { - numberType: 10.5 - }, - count: { - id: 20 - }, - max: { - dateType: TEST_DISCRIMINATED_ENTITIES[9].dateType, - numberType: 20, - stringType: 'foo9', - id: expect.any(Types.ObjectId) - }, - min: { - dateType: TEST_ENTITIES[0].dateType, - numberType: 1, - stringType: 'foo1', - id: expect.any(Types.ObjectId) - }, - sum: { - numberType: 210 - } - } - ]); - }); - - it('allow aggregates with groupBy', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.aggregate( - {}, - { - groupBy: ['boolType'], - count: ['id'], - avg: ['numberType'], - sum: ['numberType'], - max: ['id', 'dateType', 'numberType', 'stringType'], - min: ['id', 'dateType', 'numberType', 'stringType'] - } - ); - return expect(queryResult).toEqual([ - { - groupBy: { - boolType: false - }, - avg: { - numberType: 10 - }, - count: { - id: 10 - }, - max: { - dateType: TEST_DISCRIMINATED_ENTITIES[8].dateType, - numberType: 19, - stringType: 'foo9', - id: expect.any(Types.ObjectId) - }, - min: { - dateType: TEST_ENTITIES[0].dateType, - numberType: 1, - stringType: 'foo1', - id: expect.any(Types.ObjectId) - }, - sum: { - numberType: 100 - } - }, - { - groupBy: { - boolType: true - }, - avg: { - numberType: 11 - }, - count: { - id: 10 - }, - max: { - dateType: TEST_DISCRIMINATED_ENTITIES[9].dateType, - numberType: 20, - stringType: 'foo8', - id: expect.any(Types.ObjectId) - }, - min: { - dateType: TEST_ENTITIES[1].dateType, - numberType: 2, - stringType: 'foo10', - id: expect.any(Types.ObjectId) - }, - sum: { - numberType: 110 - } - } - ]); - }); - - it('call select with the aggregate columns and return the result with a filter', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.aggregate( - { stringType: { in: ['foo1', 'foo2', 'foo3'] } }, - { - count: ['id'], - avg: ['numberType'], - sum: ['numberType'], - max: ['id', 'dateType', 'numberType', 'stringType'], - min: ['id', 'dateType', 'numberType', 'stringType'] - } - ); - return expect(queryResult).toEqual([ - { - avg: { - numberType: 2 - }, - count: { - id: 3 - }, - max: { - dateType: TEST_ENTITIES[2].dateType, - numberType: 3, - stringType: 'foo3', - id: expect.any(Types.ObjectId) - }, - min: { - dateType: TEST_ENTITIES[0].dateType, - numberType: 1, - stringType: 'foo1', - id: expect.any(Types.ObjectId) - }, - sum: { - numberType: 6 - } - } - ]); - }); - }); - - describe('#count', () => { - it('should return number of elements matching a query', async () => { - const queryService = moduleRef.get(TestEntityService); - const expectedEntities = TEST_ENTITIES.slice(0, 2); - const count = await queryService.count({ stringType: { in: expectedEntities.map((e) => e.stringType) } }); - expect(count).toBe(2); - }); - }); - - describe('#findById', () => { - it('return the entity if found', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const found = await queryService.findById(entity._id.toHexString()); - expect(convertDocument(found!)).toEqual(entity); - }); - - it('return undefined if not found', async () => { - const queryService = moduleRef.get(TestEntityService); - const found = await queryService.findById(new Types.ObjectId().toHexString()); - expect(found).toBeUndefined(); - }); - - describe('with filter', () => { - it('should return an entity if all filters match', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const found = await queryService.findById(entity._id.toHexString(), { - filter: { stringType: { eq: entity.stringType } } - }); - expect(convertDocument(found!)).toEqual(entity); - }); - - it('should return an undefined if an entity with the pk and filter is not found', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const found = await queryService.findById(entity._id.toHexString(), { - filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } - }); - expect(found).toBeUndefined(); - }); - }); - }); - - describe('#getById', () => { - it('return the entity if found', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const found = await queryService.getById(entity._id.toHexString()); - expect(convertDocument(found)).toEqual(entity); - }); - - it('return undefined if not found', () => { - const badId = new Types.ObjectId().toHexString(); - const queryService = moduleRef.get(TestEntityService); - return expect(queryService.getById(badId)).rejects.toThrow(`Unable to find TestEntity with id: ${badId}`); - }); - - describe('with filter', () => { - it('should return an entity if all filters match', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const found = await queryService.getById(entity._id.toHexString(), { - filter: { stringType: { eq: entity.stringType } } - }); - expect(convertDocument(found)).toEqual(entity); - }); - - it('should return an undefined if an entity with the pk and filter is not found', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - return expect( - queryService.getById(entity._id.toHexString(), { - filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } - }) - ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); - }); - }); - }); - - describe('#createMany', () => { - it('call save on the repo with instances of entities when passed plain objects', async () => { - const queryService = moduleRef.get(TestEntityService); - const created = await queryService.createMany(TEST_ENTITIES.map(testEntityToCreate)); - expectEqualCreate(created, TEST_ENTITIES); - }); - - it('call save on the repo with instances of entities when passed instances', async () => { - const instances = TEST_ENTITIES.map((e) => testEntityToCreate(e)); - const queryService = moduleRef.get(TestEntityService); - const created = await queryService.createMany(instances); - expectEqualCreate(created, TEST_ENTITIES); - }); - - it('should reject if the entities already exist', async () => { - const queryService = moduleRef.get(TestEntityService); - return expect(queryService.createMany(TEST_ENTITIES)).rejects.toThrow( - 'Id cannot be specified when updating or creating' - ); - }); - }); - - describe('#createOne', () => { - it('call save on the repo with an instance of the entity when passed a plain object', async () => { - const entity = testEntityToCreate(TEST_ENTITIES[0]); - const queryService = moduleRef.get(TestEntityService); - const created = await queryService.createOne(entity); - expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); - }); - - it('call save on the repo with an instance of the entity when passed an instance', async () => { - const Model = getModelForClass(TestEntity); - const entity = new Model(testEntityToCreate(TEST_ENTITIES[0])); - const outcome = testEntityToObject(entity); - const queryService = moduleRef.get(TestEntityService); - const created = await queryService.createOne(entity); - expect(convertDocument(created)).toEqual(expect.objectContaining(outcome)); - }); - - it('should reject if the entity contains an id', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - return expect(queryService.createOne({ ...entity })).rejects.toThrow( - 'Id cannot be specified when updating or creating' - ); - }); - - it('should not reject if the entity contains an undefined id', async () => { - const entity = testEntityToCreate(TEST_ENTITIES[0]); - const queryService = moduleRef.get(TestEntityService); - const created = await queryService.createOne({ ...entity, id: undefined }); - expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); - }); - - it('should not reject if the entity contains an undefined _id', async () => { - const entity = testEntityToCreate(TEST_ENTITIES[0]); - const queryService = moduleRef.get(TestEntityService); - const created = await queryService.createOne({ ...entity, _id: undefined }); - expect(convertDocument(created)).toEqual(expect.objectContaining(entity)); - }); - }); - - describe('#deleteMany', () => { - it('delete all records that match the query', async () => { - const queryService = moduleRef.get(TestEntityService); - const entities = TEST_ENTITIES.slice(0, 5); - const { deletedCount } = await queryService.deleteMany({ - stringType: { in: entities.map((e) => e.stringType) } - }); - expect(deletedCount).toBe(5); - const allCount = await queryService.count({}); - expect(allCount).toBe(15); - }); - }); - - describe('#deleteOne', () => { - it('remove the entity', async () => { - const queryService = moduleRef.get(TestEntityService); - const deleted = await queryService.deleteOne(TEST_ENTITIES[0]._id.toHexString()); - expect(convertDocument(deleted)).toEqual(TEST_ENTITIES[0]); - }); - - it('call fail if the entity is not found', async () => { - const badId = new Types.ObjectId().toHexString(); - const queryService = moduleRef.get(TestEntityService); - return expect(queryService.deleteOne(badId)).rejects.toThrow(`Unable to find TestEntity with id: ${badId}`); - }); - - describe('with filter', () => { - it('should delete the entity if all filters match', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const deleted = await queryService.deleteOne(entity._id.toHexString(), { - filter: { stringType: { eq: entity.stringType } } - }); - expect(convertDocument(deleted)).toEqual(TEST_ENTITIES[0]); - }); - - it('should return throw an error if unable to find', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - return expect( - queryService.deleteOne(entity._id.toHexString(), { - filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } - }) - ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); - }); - }); - }); - - describe('#updateMany', () => { - it('update all entities in the filter', async () => { - const queryService = moduleRef.get(TestEntityService); - const filter = { - stringType: { in: TEST_ENTITIES.slice(0, 5).map((e) => e.stringType) } - }; - await queryService.updateMany({ stringType: 'updated' }, filter); - const entities = await queryService.query({ filter: { stringType: { eq: 'updated' } } }); - expect(entities).toHaveLength(5); - }); - - it('should reject if the update contains the ID', () => { - const queryService = moduleRef.get(TestEntityService); - return expect(queryService.updateMany({ id: new Types.ObjectId().toHexString() }, {})).rejects.toThrow( - 'Id cannot be specified when updating' - ); - }); - - it('should not reject if the update contains an undefined id', async () => { - const queryService = moduleRef.get(TestEntityService); - const entitiesToUpdate = TEST_ENTITIES.slice(0, 5); - const filter = { - stringType: { in: entitiesToUpdate.map((e) => e.stringType) } - }; - await queryService.updateMany({ stringType: 'updated', id: undefined }, filter); - const entities = await queryService.query({ filter: { stringType: { eq: 'updated' } } }); - expect(entities).toHaveLength(entitiesToUpdate.length); - expect(new Set(entities.map((e) => e.id))).toEqual(new Set(entitiesToUpdate.map((e) => e.id))); - }); - }); - - describe('#updateOne', () => { - it('update the entity', async () => { - const queryService = moduleRef.get(TestEntityService); - const entity = TEST_ENTITIES[0]; - const update = { stringType: 'updated' }; - const updated = await queryService.updateOne(entity._id.toHexString(), update); - expect(updated).toEqual( - expect.objectContaining({ - _id: entity._id, - ...update - }) - ); - }); - - it('update the entity with an instance of the entity', async () => { - const queryService = moduleRef.get(TestEntityService); - const entity = TEST_ENTITIES[0]; - const Model = getModelForClass(TestEntity); - const update = new Model({ stringType: 'updated' }); - const updated = await queryService.updateOne(entity._id.toHexString(), update); - expect(updated).toEqual( - expect.objectContaining({ - _id: entity._id, - stringType: 'updated' - }) - ); - }); - - it('should reject if the update contains the ID', async () => { - const queryService = moduleRef.get(TestEntityService); - return expect( - queryService.updateOne(TEST_ENTITIES[0]._id.toHexString(), { _id: new Types.ObjectId() }) - ).rejects.toThrow('Id cannot be specified when updating'); - }); - - it('call fail if the entity is not found', async () => { - const badId = new Types.ObjectId().toHexString(); - const queryService = moduleRef.get(TestEntityService); - return expect(queryService.updateOne(badId, { stringType: 'updated' })).rejects.toThrow( - `Unable to find TestEntity with id: ${badId}` - ); - }); - - describe('with filter', () => { - it('should update the entity if all filters match', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const update = { stringType: 'updated' }; - const updated = await queryService.updateOne(entity._id.toHexString(), update, { - filter: { stringType: { eq: entity.stringType } } - }); - expect(updated).toEqual(expect.objectContaining({ _id: entity._id, ...update })); - }); - - it('should throw an error if unable to find the entity', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - return expect( - queryService.updateOne( - entity._id.toHexString(), - { stringType: 'updated' }, - { filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } } - ) - ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); - }); - }); - }); - - describe('#findRelation', () => { - describe('with one entity', () => { - it('call select and return the result', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.findRelation(TestReference, 'testReference', entity); - expect(queryResult).toEqual(TEST_REFERENCES[0]); - }); - - it('apply the filter option', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const queryResult1 = await queryService.findRelation(TestReference, 'testReference', entity, { - filter: { referenceName: { eq: TEST_REFERENCES[0].referenceName } } - }); - expect(queryResult1).toEqual(TEST_REFERENCES[0]); - - const queryResult2 = await queryService.findRelation(TestReference, 'testReference', entity, { - filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } - }); - expect(queryResult2).toBeUndefined(); - }); - - it('should return undefined select if no results are found.', async () => { - const entity = TEST_ENTITIES[0]; - await TestEntityModel.updateOne({ _id: entity._id }, { $set: { testReference: undefined } }); - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.findRelation(TestReference, 'testReference', entity); - expect(queryResult).toBeUndefined(); - }); - - it('throw an error if a relation with that name is not found.', async () => { - const queryService = moduleRef.get(TestEntityService); - const entity = TEST_ENTITIES[0]; - return expect(queryService.findRelation(TestReference, 'badReference', entity)).rejects.toThrow( - 'Unable to find reference badReference on TestEntity' - ); - }); - - describe('virtual reference', () => { - it('call select and return the result', async () => { - const entity = TEST_REFERENCES[0]; - const queryService = moduleRef.get(TestReferenceService); - const queryResult = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity); - - expect(queryResult).toEqual(TEST_ENTITIES[0]); - }); - - it('apply the filter option', async () => { - const entity = TEST_REFERENCES[0]; - const queryService = moduleRef.get(TestReferenceService); - const queryResult1 = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity, { - filter: { stringType: { eq: TEST_ENTITIES[0].stringType } } - }); - expect(queryResult1).toEqual(TEST_ENTITIES[0]); - - const queryResult2 = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity, { - filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } - }); - expect(queryResult2).toBeUndefined(); - }); - - it('should return undefined select if no results are found.', async () => { - const entity = TEST_REFERENCES[0]; - await TestReferenceModel.updateOne({ _id: entity._id }, { $set: { testEntity: undefined } }); - const queryService = moduleRef.get(TestReferenceService); - const queryResult = await queryService.findRelation(TestEntity, 'virtualTestEntity', entity); - expect(queryResult).toBeUndefined(); - }); - - it('throw an error if a relation with that name is not found.', async () => { - const entity = TEST_REFERENCES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect(queryService.findRelation(TestEntity, 'badReference', entity)).rejects.toThrow( - 'Unable to find reference badReference on TestReference' - ); - }); - }); - }); - - describe('with multiple entities', () => { - it('call select and return the result', async () => { - const entities = TEST_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.findRelation(TestReference, 'testReference', entities, {}); - - expect(queryResult).toEqual( - new Map([ - [entities[0], expect.objectContaining(TEST_REFERENCES[0])], - [entities[1], expect.objectContaining(TEST_REFERENCES[3])], - [entities[2], expect.objectContaining(TEST_REFERENCES[6])] - ]) - ); - }); - - it('should apply the filter option', async () => { - const entities = TEST_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestEntityService); - const options: FindRelationOptions = { - filter: { - _id: { in: [TEST_REFERENCES[0]._id, TEST_REFERENCES[6]._id] } - } - }; - const queryResult = await queryService.findRelation(TestReference, 'testReference', entities, options); - expect(queryResult).toEqual( - new Map([ - [entities[0], expect.objectContaining(TEST_REFERENCES[0])], - [entities[1], undefined], - [entities[2], expect.objectContaining(TEST_REFERENCES[6])] - ]) - ); - }); - - it('should return undefined select if no results are found.', async () => { - const entities: DocumentType[] = [ - TEST_ENTITIES[0], - { _id: new Types.ObjectId() } as DocumentType - ]; - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.findRelation(TestReference, 'testReference', entities); - - expect(queryResult).toEqual( - new Map([ - [entities[0], expect.objectContaining(TEST_REFERENCES[0])], - [entities[1], undefined] - ]) - ); - }); - }); - }); - - describe('#queryRelations', () => { - describe('with one entity', () => { - it('call select and return the result', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations(TestReference, 'testReferences', TEST_ENTITIES[0], { - filter: { referenceName: { isNot: null } } - }); - return expect(queryResult).toEqual(TEST_REFERENCES.slice(0, 3)); - }); - - it('should apply a filter', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations(TestReference, 'testReferences', TEST_ENTITIES[0], { - filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } - }); - expect(queryResult).toEqual([TEST_REFERENCES[1]]); - }); - - it('should apply paging', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations(TestReference, 'testReferences', TEST_ENTITIES[0], { - paging: { limit: 2, offset: 1 } - }); - expect(queryResult).toEqual(TEST_REFERENCES.slice(1, 3)); - }); - }); - - describe('with virtual entity', () => { - it('call select and return the result', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'virtualTestReferences', - TEST_ENTITIES[0], - { - filter: { referenceName: { isNot: null } } - } - ); - return expect(queryResult).toEqual(expect.arrayContaining(TEST_REFERENCES.slice(0, 3))); - }); - - it('should apply a filter', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'virtualTestReferences', - TEST_ENTITIES[0], - { - filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } - } - ); - expect(queryResult).toEqual([TEST_REFERENCES[1]]); - }); - - it('should apply paging', async () => { - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations( - TestReference, - 'virtualTestReferences', - TEST_ENTITIES[0], - { - paging: { limit: 2, offset: 1 }, - sorting: [{ field: 'referenceName', direction: SortDirection.ASC }] - } - ); - expect(queryResult).toEqual(TEST_REFERENCES.slice(1, 3)); - }); - }); - - describe('with multiple entities', () => { - it('call return a map of results for each entity', async () => { - const entities = TEST_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { - filter: { referenceName: { isNot: null } } - }); - expect(queryResult.size).toBe(3); - expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES.slice(0, 3)); - expect(queryResult.get(entities[1])).toEqual(TEST_REFERENCES.slice(3, 6)); - expect(queryResult.get(entities[2])).toEqual(TEST_REFERENCES.slice(6, 9)); - }); - - it('should apply a filter per entity', async () => { - const entities = TEST_ENTITIES.slice(0, 3); - const references = [TEST_REFERENCES[1], TEST_REFERENCES[4], TEST_REFERENCES[7]]; - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { - filter: { referenceName: { in: references.map((r) => r.referenceName) } } - }); - expect(queryResult.size).toBe(3); - expect(queryResult.get(entities[0])).toEqual([references[0]]); - expect(queryResult.get(entities[1])).toEqual([references[1]]); - expect(queryResult.get(entities[2])).toEqual([references[2]]); - }); - - it('should apply paging per entity', async () => { - const entities = TEST_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { - paging: { limit: 2, offset: 1 } - }); - expect(queryResult.size).toBe(3); - expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES.slice(1, 3)); - expect(queryResult.get(entities[1])).toEqual(TEST_REFERENCES.slice(4, 6)); - expect(queryResult.get(entities[2])).toEqual(TEST_REFERENCES.slice(7, 9)); - }); - - it('should return an empty array if no results are found.', async () => { - const entities: DocumentType[] = [ - TEST_ENTITIES[0], - { _id: new Types.ObjectId() } as DocumentType - ]; - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.queryRelations(TestReference, 'testReferences', entities, { - filter: { referenceName: { isNot: null } } - }); - expect(queryResult.size).toBe(2); - expect(queryResult.get(entities[0])).toEqual(TEST_REFERENCES.slice(0, 3)); - expect(queryResult.get(entities[1])).toEqual([]); - }); - }); - }); - - describe('#aggregateRelations', () => { - describe('with one entity', () => { - it('should return an aggregate', async () => { - const queryService = moduleRef.get(TestEntityService); - const aggResult = await queryService.aggregateRelations( - TestReference, - 'testReferences', - TEST_ENTITIES[0], - { referenceName: { isNot: null } }, - { count: ['id'] } - ); - return expect(aggResult).toEqual([ - { - count: { - id: 3 - } - } - ]); - }); - - it('should support groupBy when aggregating relations', async () => { - const queryService = moduleRef.get(TestEntityService); - const aggResult = await queryService.aggregateRelations( - TestReference, - 'testReferences', - TEST_ENTITIES[0], - { referenceName: { isNot: null } }, - { groupBy: ['testEntity'], count: ['id'] } - ); - return expect(aggResult).toEqual([ - { - groupBy: { testEntity: TEST_ENTITIES[0]._id }, - count: { - id: 3 - } - } - ]); - }); - }); - - describe('with virtual relation', () => { - it('call select and return the result', async () => { - const queryService = moduleRef.get(TestEntityService); - const aggResult = await queryService.aggregateRelations( - TestReference, - 'virtualTestReferences', - TEST_ENTITIES[0], - { referenceName: { isNot: null } }, - { count: ['id'] } - ); - return expect(aggResult).toEqual([ - { - count: { - id: 3 - } - } - ]); - }); - }); - - describe('with multiple entities', () => { - it('return a relation aggregate for each entity', async () => { - const entities = TEST_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.aggregateRelations( - TestReference, - 'testReferences', - entities, - { referenceName: { isNot: null } }, - { - count: ['id', 'referenceName', 'testEntity'], - min: ['id', 'referenceName', 'testEntity'], - max: ['id', 'referenceName', 'testEntity'] - } - ); - - expect(queryResult.size).toBe(3); - expect(queryResult).toEqual( - new Map([ - [ - entities[0], - [ - { - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES[2].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES[0].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ], - [ - entities[1], - [ - { - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES[5].referenceName, - testEntity: entities[1]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES[3].referenceName, - testEntity: entities[1]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ], - [ - entities[2], - [ - { - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES[8].referenceName, - testEntity: entities[2]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES[6].referenceName, - testEntity: entities[2]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ] - ]) - ); - }); - - it('aggregate and group for each entities relation', async () => { - const entities = TEST_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.aggregateRelations( - TestReference, - 'testReferences', - entities, - { referenceName: { isNot: null } }, - { - groupBy: ['testEntity'], - count: ['id', 'referenceName', 'testEntity'], - min: ['id', 'referenceName', 'testEntity'], - max: ['id', 'referenceName', 'testEntity'] - } - ); - - expect(queryResult.size).toBe(3); - expect(queryResult).toEqual( - new Map([ - [ - entities[0], - [ - { - groupBy: { testEntity: entities[0]._id }, - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES[2].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES[0].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ], - [ - entities[1], - [ - { - groupBy: { testEntity: entities[1]._id }, - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES[5].referenceName, - testEntity: entities[1]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES[3].referenceName, - testEntity: entities[1]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ], - [ - entities[2], - [ - { - groupBy: { testEntity: entities[2]._id }, - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES[8].referenceName, - testEntity: entities[2]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES[6].referenceName, - testEntity: entities[2]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ] - ]) - ); - }); - - it('should return an empty array if no results are found.', async () => { - const entities: DocumentType[] = [ - TEST_ENTITIES[0], - { _id: new Types.ObjectId() } as DocumentType - ]; - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.aggregateRelations( - TestReference, - 'testReferences', - entities, - { referenceName: { isNot: null } }, - { - count: ['id', 'referenceName', 'testEntity'], - min: ['id', 'referenceName', 'testEntity'], - max: ['id', 'referenceName', 'testEntity'] - } - ); - - expect(queryResult).toEqual( - new Map([ - [ - entities[0], - [ - { - count: { - referenceName: 3, - testEntity: 3, - id: 3 - }, - max: { - referenceName: TEST_REFERENCES[2].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - }, - min: { - referenceName: TEST_REFERENCES[0].referenceName, - testEntity: entities[0]._id, - id: expect.any(Types.ObjectId) - } - } - ] - ], - [entities[1], []] - ]) - ); - }); - }); - }); - - describe('#countRelations', () => { - describe('with one entity', () => { - it('count the references', async () => { - const queryService = moduleRef.get(TestEntityService); - const entity = TEST_ENTITIES[0]; - const countResult = await queryService.countRelations(TestReference, 'testReferences', entity, { - referenceName: { in: [TEST_REFERENCES[1].referenceName, TEST_REFERENCES[2].referenceName] } - }); - return expect(countResult).toBe(2); - }); - - it('should return a rejected promise if the relation is not found', async () => { - const queryService = moduleRef.get(TestEntityService); - const entity = TEST_ENTITIES[0]; - return expect( - queryService.countRelations(TestReference, 'badReferences', entity, { - referenceName: { in: [TEST_REFERENCES[1].referenceName, TEST_REFERENCES[2].referenceName] } - }) - ).rejects.toThrow('Unable to find reference badReferences on TestEntity'); - }); - }); - - describe('with virtual entity', () => { - it('count references', async () => { - const queryService = moduleRef.get(TestEntityService); - const entity = TEST_ENTITIES[0]; - const countResult = await queryService.countRelations(TestReference, 'virtualTestReferences', entity, {}); - return expect(countResult).toBe(3); - }); - it('count and return the result', async () => { - const queryService = moduleRef.get(TestEntityService); - const entity = TEST_ENTITIES[0]; - const countResult = await queryService.countRelations(TestReference, 'virtualTestReferences', entity, { - referenceName: { in: [TEST_REFERENCES[1].referenceName, TEST_REFERENCES[2].referenceName] } - }); - return expect(countResult).toBe(2); - }); - }); - - describe('with multiple entities', () => { - it('call count and return the result', async () => { - const entities = TEST_ENTITIES.slice(0, 3); - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.countRelations(TestReference, 'testReferences', entities, { - referenceName: { - in: [ - TEST_REFERENCES[1].referenceName, - TEST_REFERENCES[2].referenceName, - TEST_REFERENCES[4].referenceName, - TEST_REFERENCES[5].referenceName, - TEST_REFERENCES[7].referenceName, - TEST_REFERENCES[8].referenceName - ] - } - }); - - expect(queryResult).toEqual( - new Map([ - [entities[0], 2], - [entities[1], 2], - [entities[2], 2] - ]) - ); - }); - }); - }); - - describe('#addRelations', () => { - it('call select and return the result', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.addRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()) - ); - expect(queryResult).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: expect.arrayContaining(TEST_REFERENCES.slice(0, 6).map((r) => r._id)) - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations).toHaveLength(6); - }); - - it('should not modify relations if relationIds is empty', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.addRelations('testReferences', entity._id.toHexString(), []); - expect(queryResult).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: expect.arrayContaining(TEST_REFERENCES.slice(0, 3).map((r) => r._id)) - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations).toHaveLength(3); - }); - - describe('with virtual reference', () => { - it('should return a rejected promise', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - return expect( - queryService.addRelations( - 'virtualTestReferences', - entity._id.toHexString(), - TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()) - ) - ).rejects.toThrow('AddRelations not supported for virtual relation virtualTestReferences'); - }); - }); - - 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.addRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()), - { - filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } - } - ) - ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); - }); - - 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.addRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()), - { - relationFilter: { referenceName: { like: '%-one' } } - } - ) - ).rejects.toThrow('Unable to find all testReferences to add to TestEntity'); - }); - }); - }); - - describe('#setRelations', () => { - it('set all relations on the entity', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const relationIds = TEST_REFERENCES.slice(3, 6).map((r) => r._id); - const queryResult = await queryService.setRelations( - 'testReferences', - entity._id.toHexString(), - relationIds.map((id) => id.toHexString()) - ); - expect(queryResult).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: expect.arrayContaining(relationIds) - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations.map((r) => r._id)).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('testReferences', entity._id.toHexString(), []); - expect(queryResult).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: expect.arrayContaining([]) - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations.map((r) => r._id)).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( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()), - { - filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } - } - ) - ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); - }); - - 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( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES.slice(3, 6).map((r) => r._id.toHexString()), - { - relationFilter: { referenceName: { like: '%-one' } } - } - ) - ).rejects.toThrow('Unable to find all testReferences to set on TestEntity'); - }); - }); - }); - - describe('#setRelation', () => { - it('call select and return the result', async () => { - const entity = TEST_REFERENCES[0]; - const queryService = moduleRef.get(TestReferenceService); - const queryResult = await queryService.setRelation( - 'testEntity', - entity._id.toHexString(), - TEST_ENTITIES[1]._id.toHexString() - ); - expect(queryResult).toEqual(expect.objectContaining({ ...entity, testEntity: TEST_ENTITIES[1]._id })); - - const relation = await queryService.findRelation(TestEntity, 'testEntity', entity); - expect(relation).toEqual(TEST_ENTITIES[1]); - }); - - it('should reject with a virtual reference', async () => { - const entity = TEST_REFERENCES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.setRelation('virtualTestEntity', entity._id.toHexString(), TEST_ENTITIES[1]._id.toHexString()) - ).rejects.toThrow('SetRelation not supported for virtual relation virtualTestEntity'); - }); - - 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_REFERENCES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.setRelation('testEntity', entity._id.toHexString(), TEST_ENTITIES[1]._id.toHexString(), { - filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } - }) - ).rejects.toThrow(`Unable to find TestReference with id: ${String(entity._id)}`); - }); - - it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { - const entity = TEST_REFERENCES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.setRelation( - 'testEntity', - entity._id.toHexString(), - TEST_ENTITIES[1]._id.toHexString(), - { - relationFilter: { stringType: { like: '%-one' } } - } - ) - ).rejects.toThrow('Unable to find testEntity to set on TestReference'); - }); - }); - }); - - describe('#removeRelation', () => { - it('call select and return the result', async () => { - const entity = TEST_REFERENCES[0]; - const queryService = moduleRef.get(TestReferenceService); - const queryResult = await queryService.removeRelation( - 'testEntity', - entity._id.toHexString(), - TEST_ENTITIES[1]._id.toHexString() - ); - const { testEntity, ...expected } = entity; - expect(queryResult).toEqual(expect.objectContaining(expected)); - - const relation = await queryService.findRelation(TestEntity, 'testEntity', entity); - expect(relation).toBeUndefined(); - }); - - it('should reject with a virtual reference', async () => { - const entity = TEST_REFERENCES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.removeRelation('virtualTestEntity', entity._id.toHexString(), TEST_ENTITIES[1]._id.toHexString()) - ).rejects.toThrow('RemoveRelation not supported for virtual relation virtualTestEntity'); - }); - - 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_REFERENCES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.removeRelation('testEntity', entity._id.toHexString(), TEST_ENTITIES[1]._id.toHexString(), { - filter: { referenceName: { eq: TEST_REFERENCES[1].referenceName } } - }) - ).rejects.toThrow(`Unable to find TestReference with id: ${String(entity._id)}`); - }); - - it('should throw an error if the relations are not found with the relationIds and provided filter', async () => { - const entity = TEST_REFERENCES[0]; - const queryService = moduleRef.get(TestReferenceService); - return expect( - queryService.removeRelation( - 'testEntity', - entity._id.toHexString(), - TEST_ENTITIES[1]._id.toHexString(), - { - relationFilter: { stringType: { like: '%-one' } } - } - ) - ).rejects.toThrow('Unable to find testEntity to remove from TestReference'); - }); - }); - }); - - describe('#removeRelations', () => { - it('call select and return the result', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.removeRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES.slice(0, 3).map((r) => r._id.toHexString()) - ); - expect(queryResult.toObject()).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: [] - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations).toHaveLength(0); - }); - - it('should not modify relations if relationIds is empty', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - const queryResult = await queryService.removeRelations('testReferences', entity._id.toHexString(), []); - expect(queryResult.toObject()).toEqual( - expect.objectContaining({ - _id: entity._id, - testReferences: expect.arrayContaining(TEST_REFERENCES.slice(0, 3).map((r) => r._id)) - }) - ); - - const relations = await queryService.queryRelations(TestReference, 'testReferences', entity, {}); - expect(relations).toHaveLength(3); - }); - - describe('with virtual reference', () => { - it('should return a rejected promise', async () => { - const entity = TEST_ENTITIES[0]; - const queryService = moduleRef.get(TestEntityService); - return expect( - queryService.removeRelations( - 'virtualTestReferences', - entity._id.toHexString(), - TEST_REFERENCES.slice(0, 3).map((r) => r._id.toHexString()) - ) - ).rejects.toThrow('RemoveRelations not supported for virtual relation virtualTestReferences'); - }); - }); - - 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.removeRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES.slice(0, 3).map((r) => r._id.toHexString()), - { - filter: { stringType: { eq: TEST_ENTITIES[1].stringType } } - } - ) - ).rejects.toThrow(`Unable to find TestEntity with id: ${String(entity._id)}`); - }); - - 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.removeRelations( - 'testReferences', - entity._id.toHexString(), - TEST_REFERENCES.slice(0, 3).map((r) => r._id.toHexString()), - { - relationFilter: { referenceName: { like: '%-one' } } - } - ) - ).rejects.toThrow('Unable to find all testReferences to remove from TestEntity'); - }); - }); - }); -}); From e768900ae33949cb89c7ab4039b7cb008617a0e9 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Fri, 27 May 2022 17:55:53 +0200 Subject: [PATCH 7/8] fix(query-graphql): Fixed `ResolveOneRelation` interface --- packages/query-graphql/package.json | 2 +- .../src/resolvers/relations/relations.interface.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/query-graphql/package.json b/packages/query-graphql/package.json index ae3205b73..e0129928c 100644 --- a/packages/query-graphql/package.json +++ b/packages/query-graphql/package.json @@ -1,6 +1,6 @@ { "name": "@ptc-org/nestjs-query-graphql", - "version": "1.0.0-alpha.3", + "version": "1.0.0-alpha.4", "description": "Nestjs graphql query adapter", "author": "doug-martin ", "homepage": "https://github.com/tripss/nestjs-query#readme", diff --git a/packages/query-graphql/src/resolvers/relations/relations.interface.ts b/packages/query-graphql/src/resolvers/relations/relations.interface.ts index 92ceb480c..67581f68a 100644 --- a/packages/query-graphql/src/resolvers/relations/relations.interface.ts +++ b/packages/query-graphql/src/resolvers/relations/relations.interface.ts @@ -86,7 +86,7 @@ export type ResolverRelation = { export type RelationTypeMap = Record; -export type ResolverOneRelation = Omit, 'disableFilter' | 'disableSorting'>; +export type ResolverOneRelation = Omit, 'disableFilter' | 'disableSort'>; export type ResolverManyRelation = ResolverRelation; export type RelationsOpts = { From 41190a1b16674905a41f6f7841d29f2f5e1edeb8 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Fri, 27 May 2022 17:58:10 +0200 Subject: [PATCH 8/8] refactor: Linting --- .../services/typegoose-query-descriminated.service.spec.ts | 4 ++-- .../__tests__/services/typegoose-query.service.spec.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts b/packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts index 1e0a7c44c..e06f922a5 100644 --- a/packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts +++ b/packages/query-typegoose/__tests__/services/typegoose-query-descriminated.service.spec.ts @@ -352,7 +352,7 @@ describe('TypegooseQueryService', () => { const entity = TEST_DISCRIMINATED_ENTITIES[0]; const queryService = moduleRef.get(TestDiscriminatedEntityService); const found = await queryService.findById(entity._id.toString()); - expect(convertDocument(found!)).toEqual(entity); + expect(convertDocument(found)).toEqual(entity); }); it('return undefined if not found', async () => { @@ -368,7 +368,7 @@ describe('TypegooseQueryService', () => { const found = await queryService.findById(entity._id.toString(), { filter: { stringType: { eq: entity.stringType } } }); - expect(convertDocument(found!)).toEqual(entity); + expect(convertDocument(found)).toEqual(entity); }); it('should return an undefined if an entity with the pk and filter is not found', async () => { diff --git a/packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts b/packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts index ebe444ade..cac31c9ed 100644 --- a/packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts +++ b/packages/query-typegoose/__tests__/services/typegoose-query.service.spec.ts @@ -340,7 +340,7 @@ describe('TypegooseQueryService', () => { const entity = TEST_ENTITIES[0]; const queryService = moduleRef.get(TestEntityService); const found = await queryService.findById(entity._id.toHexString()); - expect(convertDocument(found!)).toEqual(entity); + expect(convertDocument(found)).toEqual(entity); }); it('return undefined if not found', async () => { @@ -356,7 +356,7 @@ describe('TypegooseQueryService', () => { const found = await queryService.findById(entity._id.toHexString(), { filter: { stringType: { eq: entity.stringType } } }); - expect(convertDocument(found!)).toEqual(entity); + expect(convertDocument(found)).toEqual(entity); }); it('should return an undefined if an entity with the pk and filter is not found', async () => {