diff --git a/HISTORY.md b/HISTORY.md index af85aaabfe..2a9c45f401 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,7 @@ +# v0.8.0 + +* Updated all `create`, 'update', and `delete` `InputTypes` to be abstract. [#72](https://github.com/doug-martin/nestjs-query/issues/72) + # v0.7.5 * [FIXED] Tables with composite primary keys are not quoted properly. diff --git a/documentation/blog/2020-04-02-v0.7.4.md b/documentation/blog/2020-04-02-v0.7.4.md new file mode 100644 index 0000000000..7f55b9bcbe --- /dev/null +++ b/documentation/blog/2020-04-02-v0.7.4.md @@ -0,0 +1,12 @@ +--- +id: v0.7.4 +title: v0.7.4 +author: Doug Martin +author_title: Creator +author_url: https://github.com/doug-martin +author_image_url: https://avatars1.githubusercontent.com/u/361261?v=4 +tags: [releases, patch] +--- + +* [FIXED] code formatting +* Update root package.json with common dependencies diff --git a/documentation/blog/2020-04-03-v0.7.5.md b/documentation/blog/2020-04-03-v0.7.5.md new file mode 100644 index 0000000000..f10ef57be4 --- /dev/null +++ b/documentation/blog/2020-04-03-v0.7.5.md @@ -0,0 +1,12 @@ +--- +id: v0.7.5 +title: v0.7.5 +author: Doug Martin +author_title: Creator +author_url: https://github.com/doug-martin +author_image_url: https://avatars1.githubusercontent.com/u/361261?v=4 +tags: [releases, patch] +--- + +* [FIXED] Tables with composite primary keys are not quoted properly. +* [DOCS] Added docs for working with multiple connections [#73](https://github.com/doug-martin/nestjs-query/pull/73) - [@johannesschobel](https://github.com/johannesschobel) diff --git a/documentation/blog/2020-04-07-v0.8.0.md b/documentation/blog/2020-04-07-v0.8.0.md new file mode 100644 index 0000000000..0fa2c12849 --- /dev/null +++ b/documentation/blog/2020-04-07-v0.8.0.md @@ -0,0 +1,11 @@ +--- +id: v0.7.5 +title: v0.7.5 +author: Doug Martin +author_title: Creator +author_url: https://github.com/doug-martin +author_image_url: https://avatars1.githubusercontent.com/u/361261?v=4 +tags: [releases, minor] +--- + +* Updated all `create`, 'update', and `delete` `InputTypes` to be abstract. [#72](https://github.com/doug-martin/nestjs-query/issues/72) diff --git a/documentation/docs/graphql/types.mdx b/documentation/docs/graphql/types.mdx index 107c99f0ed..fd0878afc4 100644 --- a/documentation/docs/graphql/types.mdx +++ b/documentation/docs/graphql/types.mdx @@ -508,7 +508,7 @@ export class TodoItemInputDTO { } @InputType() -export class CreateOneTodoItemInput extends CreateOneInputType(TodoItemDTO, TodoItemInputDTO) {} +export class CreateOneTodoItemInput extends CreateOneInputType('todoItem', TodoItemInputDTO) {} ``` --- @@ -540,7 +540,7 @@ export class TodoItemInputDTO { } @InputType() -export class CreateManyTodoItemsInput extends CreateManyInputType(TodoItemDTO, TodoItemInputDTO) {} +export class CreateManyTodoItemsInput extends CreateManyInputType('todoItems', TodoItemInputDTO) {} ``` ### UpdateOneInputType @@ -548,7 +548,7 @@ export class CreateManyTodoItemsInput extends CreateManyInputType(TodoItemDTO, T InputType type for `updateOne` mutations. The `UpdateOneInputType` will generate an `InputType` that will require the user to provide two fields. -* `id` - The id of the recrord to update +* `id` - The id of the record to update * `update` - A record with fields to update. **NOTE** Dont forget to annotate your DTO with `@InputType()` from `@nestjs/graphql`. @@ -563,7 +563,7 @@ import { UpdateOneInputType } from '@nestjs-query/query-graphql'; import { InputType } from '@nestjs/graphql'; import { TodoItemDTO } from './dto/todo-item.dto'; -@InputType('TodoItemUpdate') +@InputType('TodoItemUpdateInput') export class TodoItemUpdateDTO { @IsOptional() @IsString() @@ -577,7 +577,7 @@ export class TodoItemUpdateDTO { } @InputType() -export class UpdateOneTodoItemInput extends UpdateOneInputType(TodoItemDTO, TodoItemUpdateDTO) {} +export class UpdateOneTodoItemInput extends UpdateOneInputType(TodoItemUpdateDTO) {} ``` --- diff --git a/packages/query-graphql/__tests__/resolvers/create.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/create.resolver.spec.ts index a12635f487..da977afa36 100644 --- a/packages/query-graphql/__tests__/resolvers/create.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/create.resolver.spec.ts @@ -55,12 +55,12 @@ describe('CreateResolver', () => { } it('should use the dtoName if provided', () => { - const CreateOneInput = CreateOneInputType(TestResolverDTO, TestResolverDTO); + class CreateOneInput extends CreateOneInputType('createResolverDTO', TestResolverDTO) {} jest.clearAllMocks(); // reset CreateResolver(TestResolverDTO, { dtoName: 'Test', CreateOneInput }); expect(createOneInputTypeSpy).not.toBeCalled(); - expect(createManyInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); + expect(createManyInputTypeSpy).toBeCalledWith('tests', expect.any(Function)); expect(resolverMutationSpy).toBeCalledTimes(2); assertResolverMutationCall(0, TestResolverDTO, { name: 'createOneTest' }, {}, {}); @@ -77,8 +77,8 @@ describe('CreateResolver', () => { } CreateResolver(UnnamedTestResolverDTO); - expect(createOneInputTypeSpy).toBeCalledWith(UnnamedTestResolverDTO, expect.any(Function)); - expect(createManyInputTypeSpy).toBeCalledWith(UnnamedTestResolverDTO, expect.any(Function)); + expect(createOneInputTypeSpy).toBeCalledWith('unnamedTestResolverDTO', expect.any(Function)); + expect(createManyInputTypeSpy).toBeCalledWith('unnamedTestResolverDTOS', expect.any(Function)); expect(resolverMutationSpy).toBeCalledTimes(2); assertResolverMutationCall(0, UnnamedTestResolverDTO, { name: 'createOneUnnamedTestResolverDTO' }, {}, {}); @@ -89,12 +89,12 @@ describe('CreateResolver', () => { describe('#createOne', () => { it('should not create a new type if the CreateOneArgs is supplied', () => { - const CreateOneInput = CreateOneInputType(TestResolverDTO, TestResolverDTO); + class CreateOneInput extends CreateOneInputType('createResolverDTO', TestResolverDTO) {} jest.clearAllMocks(); // reset CreateResolver(TestResolverDTO, { CreateOneInput }); expect(createOneInputTypeSpy).not.toBeCalled(); - expect(createManyInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); + expect(createManyInputTypeSpy).toBeCalledWith('createResolverDTOS', expect.any(Function)); expect(resolverMutationSpy).toBeCalledTimes(2); assertResolverMutationCall(0, TestResolverDTO, { name: 'createOneCreateResolverDTO' }, {}, {}); @@ -112,8 +112,8 @@ describe('CreateResolver', () => { pipes: [], }; CreateResolver(TestResolverDTO, { one: createOneOpts }); - expect(createOneInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); - expect(createManyInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); + expect(createOneInputTypeSpy).toBeCalledWith('createResolverDTO', expect.any(Function)); + expect(createManyInputTypeSpy).toBeCalledWith('createResolverDTOS', expect.any(Function)); expect(resolverMutationSpy).toBeCalledTimes(2); assertResolverMutationCall(0, TestResolverDTO, { name: 'createOneCreateResolverDTO' }, {}, createOneOpts); @@ -124,7 +124,7 @@ describe('CreateResolver', () => { it('should call the service createOne with the provided input', async () => { const mockService = mock>(); - const args: CreateOneInputType> = { + const args: CreateOneInputType> = { input: { stringField: 'foo', }, @@ -142,11 +142,11 @@ describe('CreateResolver', () => { describe('#createMany', () => { it('should not create a new type if the CreateManyArgs is supplied', () => { - const CreateManyInput = CreateManyInputType(TestResolverDTO, TestResolverDTO); + class CreateManyInput extends CreateManyInputType('testResolvers', TestResolverDTO) {} jest.clearAllMocks(); // reset CreateResolver(TestResolverDTO, { CreateManyInput }); - expect(createOneInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); + expect(createOneInputTypeSpy).toBeCalledWith('createResolverDTO', expect.any(Function)); expect(createManyInputTypeSpy).not.toBeCalled(); expect(resolverMutationSpy).toBeCalledTimes(2); @@ -165,8 +165,8 @@ describe('CreateResolver', () => { pipes: [], }; CreateResolver(TestResolverDTO, { many: createManyOpts }); - expect(createOneInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); - expect(createManyInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); + expect(createOneInputTypeSpy).toBeCalledWith('createResolverDTO', expect.any(Function)); + expect(createManyInputTypeSpy).toBeCalledWith('createResolverDTOS', expect.any(Function)); expect(resolverMutationSpy).toBeCalledTimes(2); assertResolverMutationCall(0, TestResolverDTO, { name: 'createOneCreateResolverDTO' }, {}, {}); @@ -177,7 +177,7 @@ describe('CreateResolver', () => { it('should call the service createMany with the provided input', async () => { const mockService = mock>(); - const args: CreateManyInputType> = { + const args: CreateManyInputType> = { input: [ { stringField: 'foo', diff --git a/packages/query-graphql/__tests__/resolvers/update.resolver.spec.ts b/packages/query-graphql/__tests__/resolvers/update.resolver.spec.ts index 4ca9db6590..589f26ec56 100644 --- a/packages/query-graphql/__tests__/resolvers/update.resolver.spec.ts +++ b/packages/query-graphql/__tests__/resolvers/update.resolver.spec.ts @@ -61,7 +61,7 @@ describe('UpdateResolver', () => { it('should create an UpdateResolver with the default DTO', () => { UpdateResolver(TestResolverDTO); - expect(updateOneInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); + expect(updateOneInputTypeSpy).toBeCalledWith(expect.any(Function)); expect(updateManyInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); expect(resolverMutationSpy).toBeCalledTimes(2); @@ -72,7 +72,7 @@ describe('UpdateResolver', () => { }); it('should use the dtoName if provided', () => { - const UpdateOneInput = UpdateOneInputType(TestResolverDTO, TestResolverDTO); + const UpdateOneInput = UpdateOneInputType(TestResolverDTO); jest.clearAllMocks(); // reset UpdateResolver(TestResolverDTO, { dtoName: 'Test', UpdateOneInput }); @@ -88,7 +88,7 @@ describe('UpdateResolver', () => { describe('#updateOne', () => { it('should not update a new type if the UpdateOneArgs is supplied', () => { - const UpdateOneInput = UpdateOneInputType(TestResolverDTO, TestResolverDTO); + const UpdateOneInput = UpdateOneInputType(TestResolverDTO); jest.clearAllMocks(); // reset UpdateResolver(TestResolverDTO, { UpdateOneInput }); @@ -111,7 +111,7 @@ describe('UpdateResolver', () => { pipes: [], }; UpdateResolver(TestResolverDTO, { one: updateOneOpts }); - expect(updateOneInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); + expect(updateOneInputTypeSpy).toBeCalledWith(expect.any(Function)); expect(updateManyInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); expect(resolverMutationSpy).toBeCalledTimes(2); @@ -123,7 +123,7 @@ describe('UpdateResolver', () => { it('should call the service updateOne with the provided input', async () => { const mockService = mock>(); - const input: UpdateOneInputType> = { + const input: UpdateOneInputType> = { id: 'id-1', update: { stringField: 'foo', @@ -146,7 +146,7 @@ describe('UpdateResolver', () => { jest.clearAllMocks(); // reset UpdateResolver(TestResolverDTO, { UpdateManyInput }); - expect(updateOneInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); + expect(updateOneInputTypeSpy).toBeCalledWith(expect.any(Function)); expect(updateManyInputTypeSpy).not.toBeCalled(); expect(resolverMutationSpy).toBeCalledTimes(2); @@ -165,7 +165,7 @@ describe('UpdateResolver', () => { pipes: [], }; UpdateResolver(TestResolverDTO, { many: updateManyOpts }); - expect(updateOneInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); + expect(updateOneInputTypeSpy).toBeCalledWith(expect.any(Function)); expect(updateManyInputTypeSpy).toBeCalledWith(TestResolverDTO, expect.any(Function)); expect(resolverMutationSpy).toBeCalledTimes(2); diff --git a/packages/query-graphql/__tests__/types/create-many-input.type.spec.ts b/packages/query-graphql/__tests__/types/create-many-input.type.spec.ts index be0cb1b429..29a83a4625 100644 --- a/packages/query-graphql/__tests__/types/create-many-input.type.spec.ts +++ b/packages/query-graphql/__tests__/types/create-many-input.type.spec.ts @@ -14,14 +14,14 @@ describe('CreateManyInputType', (): void => { } it('should create an InputType with an array field', () => { - CreateManyInputType(FakeType, FakeType); - expect(inputTypeSpy).toBeCalledWith(`CreateManyFakeTypesInput`); + CreateManyInputType('fakeTypes', FakeType); + expect(inputTypeSpy).toBeCalledWith({ isAbstract: true }); expect(inputTypeSpy).toBeCalledTimes(1); expect(fieldSpy.mock.calls[0]![0]!()).toEqual([FakeType]); }); it('should properly assign the input field', () => { - const Type = CreateManyInputType(FakeType, FakeType); + class Type extends CreateManyInputType('fakeTypes', FakeType) {} const input = [{ field: 'hello' }]; const it = plainToClass(Type, { input }); expect(it.input).toEqual(input); @@ -29,7 +29,7 @@ describe('CreateManyInputType', (): void => { }); it('should assign the typeName to the input field', () => { - const Type = CreateManyInputType(FakeType, FakeType); + class Type extends CreateManyInputType('fakeTypes', FakeType) {} const input = [{ field: 'hello' }]; const it = plainToClass(Type, { fakeTypes: input }); expect(it.input).toEqual(input); @@ -38,7 +38,7 @@ describe('CreateManyInputType', (): void => { describe('validation', () => { it('should validate the input property', () => { - const Type = CreateManyInputType(FakeType, FakeType); + class Type extends CreateManyInputType('fakeTypes', FakeType) {} const input = [{ field: 'hola' }]; const it = plainToClass(Type, { input }); const errors = validateSync(it); @@ -72,7 +72,7 @@ describe('CreateManyInputType', (): void => { }); it('should assign the typeName to the input field', () => { - const Type = CreateManyInputType(FakeType, FakeType); + class Type extends CreateManyInputType('fakeTypes', FakeType) {} const input = [{ field: 'hola' }]; const it = plainToClass(Type, { fakeTypes: input }); const errors = validateSync(it); diff --git a/packages/query-graphql/__tests__/types/create-one-input.type.spec.ts b/packages/query-graphql/__tests__/types/create-one-input.type.spec.ts index 3dcbe77935..768b1d01c4 100644 --- a/packages/query-graphql/__tests__/types/create-one-input.type.spec.ts +++ b/packages/query-graphql/__tests__/types/create-one-input.type.spec.ts @@ -13,15 +13,15 @@ describe('CreateOneInputType', (): void => { field!: string; } it('should create an InputType with the field as the type', () => { - CreateOneInputType(FakeType, FakeType); - expect(inputTypeSpy).toBeCalledWith(`CreateOneFakeTypeInput`); + CreateOneInputType('fakeType', FakeType); + expect(inputTypeSpy).toBeCalledWith({ isAbstract: true }); expect(inputTypeSpy).toBeCalledTimes(1); expect(fieldSpy).toBeCalledTimes(1); expect(fieldSpy.mock.calls[0]![0]!()).toEqual(FakeType); }); it('should properly assign the input field', () => { - const Type = CreateOneInputType(FakeType, FakeType); + class Type extends CreateOneInputType('fakeType', FakeType) {} const input = { field: 'hello' }; const it = plainToClass(Type, { input }); expect(it.input).toEqual(input); @@ -29,7 +29,7 @@ describe('CreateOneInputType', (): void => { }); it('should assign the typeName to the input field', () => { - const Type = CreateOneInputType(FakeType, FakeType); + class Type extends CreateOneInputType('fakeType', FakeType) {} const input = { field: 'hello' }; const it = plainToClass(Type, { fakeType: input }); expect(it.input).toEqual(input); @@ -38,7 +38,7 @@ describe('CreateOneInputType', (): void => { describe('validation', () => { it('should validate the input property', () => { - const Type = CreateOneInputType(FakeType, FakeType); + class Type extends CreateOneInputType('fakeType', FakeType) {} const input = { field: 'hola' }; const it = plainToClass(Type, { input }); const errors = validateSync(it); @@ -63,7 +63,7 @@ describe('CreateOneInputType', (): void => { }); it('should assign the typeName to the input field', () => { - const Type = CreateOneInputType(FakeType, FakeType); + class Type extends CreateOneInputType('fakeType', FakeType) {} const input = { field: 'hola' }; const it = plainToClass(Type, { fakeType: input }); const errors = validateSync(it); diff --git a/packages/query-graphql/__tests__/types/delete-many-input.type.spec.ts b/packages/query-graphql/__tests__/types/delete-many-input.type.spec.ts index 40d1c2d64c..e8e6188c9d 100644 --- a/packages/query-graphql/__tests__/types/delete-many-input.type.spec.ts +++ b/packages/query-graphql/__tests__/types/delete-many-input.type.spec.ts @@ -22,7 +22,7 @@ describe('DeleteManyInputType', (): void => { it('should create an args type with an array field', () => { DeleteManyInputType(DeleteManyDTO); - expect(inputTypeSpy).toBeCalledWith(`DeleteManyDeleteManyDTOSInput`); + expect(inputTypeSpy).toBeCalledWith({ isAbstract: true }); expect(inputTypeSpy).toBeCalledTimes(1); expect(fieldSpy).toHaveBeenCalledWith(expect.any(Function), { description: 'Filter to find records to delete', diff --git a/packages/query-graphql/__tests__/types/update-many-input.type.spec.ts b/packages/query-graphql/__tests__/types/update-many-input.type.spec.ts index c295168ae7..6c17a12921 100644 --- a/packages/query-graphql/__tests__/types/update-many-input.type.spec.ts +++ b/packages/query-graphql/__tests__/types/update-many-input.type.spec.ts @@ -23,7 +23,7 @@ describe('UpdateManyInputType', (): void => { it('should create an args type with an array field', () => { UpdateManyInputType(FakeType, FakeType); expect(inputTypeSpy).toBeCalledTimes(1); - expect(inputTypeSpy).toBeCalledWith('UpdateManyFakeTypesInput'); + expect(inputTypeSpy).toBeCalledWith({ isAbstract: true }); expect(fieldSpy).toBeCalledTimes(2); expect(fieldSpy).toHaveBeenCalledWith(expect.any(Function), { description: 'Filter used to find fields to update', diff --git a/packages/query-graphql/__tests__/types/update-one-input.type.spec.ts b/packages/query-graphql/__tests__/types/update-one-input.type.spec.ts index 9d3582c3ab..5c8b5b5c90 100644 --- a/packages/query-graphql/__tests__/types/update-one-input.type.spec.ts +++ b/packages/query-graphql/__tests__/types/update-one-input.type.spec.ts @@ -15,9 +15,9 @@ describe('UpdateOneInputType', (): void => { name!: string; } it('should create an args type with the field as the type', () => { - UpdateOneInputType(FakeType, FakeType); + UpdateOneInputType(FakeType); expect(inputType).toBeCalledTimes(1); - expect(inputType).toBeCalledWith(`UpdateOneFakeTypeInput`); + expect(inputType).toBeCalledWith({ isAbstract: true }); expect(fieldSpy).toBeCalledTimes(2); expect(fieldSpy.mock.calls[0]![0]!()).toEqual(nestjsGraphql.ID); expect(fieldSpy.mock.calls[1]![0]!()).toEqual(FakeType); @@ -25,7 +25,7 @@ describe('UpdateOneInputType', (): void => { describe('validation', () => { it('should validate id is defined is not empty', () => { - const Type = UpdateOneInputType(FakeType, FakeType); + const Type = UpdateOneInputType(FakeType); const input = { update: { name: 'hello world' } }; const it = plainToClass(Type, input); const errors = validateSync(it); @@ -42,7 +42,7 @@ describe('UpdateOneInputType', (): void => { }); it('should validate id is not empty is defined is not empty', () => { - const Type = UpdateOneInputType(FakeType, FakeType); + const Type = UpdateOneInputType(FakeType); const input = { id: '', update: { name: 'hello world' } }; const it = plainToClass(Type, input); const errors = validateSync(it); @@ -60,7 +60,7 @@ describe('UpdateOneInputType', (): void => { }); it('should validate the update input', () => { - const Type = UpdateOneInputType(FakeType, FakeType); + const Type = UpdateOneInputType(FakeType); const input = { id: 'id-1', update: {} }; const it = plainToClass(Type, input); const errors = validateSync(it); diff --git a/packages/query-graphql/src/metadata/metadata-storage.ts b/packages/query-graphql/src/metadata/metadata-storage.ts index 744c73995e..650d8c8597 100644 --- a/packages/query-graphql/src/metadata/metadata-storage.ts +++ b/packages/query-graphql/src/metadata/metadata-storage.ts @@ -1,14 +1,7 @@ import { TypeMetadataStorage } from '@nestjs/graphql/dist/schema-builder/storages/type-metadata.storage'; -import { Class, DeepPartial, Filter, SortField } from '@nestjs-query/core'; +import { Class, Filter, SortField } from '@nestjs-query/core'; import { ObjectTypeMetadata } from '@nestjs/graphql/dist/schema-builder/metadata/object-type.metadata'; import { ReturnTypeFunc, FieldOptions } from '@nestjs/graphql'; -import { - CreateOneInputType, - CreateManyInputType, - UpdateOneInputType, - UpdateManyInputType, - DeleteManyInputType, -} from '../types'; import { EdgeType, StaticConnectionType } from '../types/connection'; /** @@ -35,39 +28,12 @@ export class GraphQLQueryMetadataStorage { private readonly edgeTypeStorage: Map, Class>>; - private readonly updateManyInputTypeStorage: Map< - Class, - Class>> - >; - - private readonly updateOneInputTypeStorage: Map< - Class, - Class>> - >; - - private readonly deleteManyInputTypeStorage: Map, Class>>; - - private readonly createOneInputTypeStorage: Map< - Class, - Class>> - >; - - private readonly createManyInputTypeStorage: Map< - Class, - Class>> - >; - constructor() { this.filterableObjectStorage = new Map(); this.filterTypeStorage = new Map(); this.sortTypeStorage = new Map(); this.connectionTypeStorage = new Map(); this.edgeTypeStorage = new Map(); - this.updateManyInputTypeStorage = new Map(); - this.updateOneInputTypeStorage = new Map(); - this.deleteManyInputTypeStorage = new Map(); - this.createOneInputTypeStorage = new Map(); - this.createManyInputTypeStorage = new Map(); } addFilterableObjectField(type: Class, field: FilterableFieldDescriptor): void { @@ -115,66 +81,6 @@ export class GraphQLQueryMetadataStorage { return this.getValue(this.edgeTypeStorage, type); } - addCreateOneInputType>( - type: Class, - createOneInputType: Class>, - ): void { - this.createOneInputTypeStorage.set(type, createOneInputType); - } - - getCreateOneInputType>( - type: Class, - ): Class> | undefined { - return this.getValue(this.createOneInputTypeStorage, type); - } - - addCreateManyInputType>( - type: Class, - createManyInputType: Class>, - ): void { - this.createManyInputTypeStorage.set(type, createManyInputType); - } - - getCreateManyInputType>( - type: Class, - ): Class> | undefined { - return this.getValue(this.createManyInputTypeStorage, type); - } - - addUpdateManyInputType>( - type: Class, - updateManyInputType: Class>, - ): void { - this.updateManyInputTypeStorage.set(type, updateManyInputType); - } - - getUpdateManyInputType>( - type: Class, - ): Class> | undefined { - return this.getValue(this.updateManyInputTypeStorage, type); - } - - addUpdateOneInputType>( - type: Class, - updateOneInputType: Class>, - ): void { - this.updateOneInputTypeStorage.set(type, updateOneInputType); - } - - getUpdateOneInputType>( - type: Class, - ): Class> | undefined { - return this.getValue(this.updateOneInputTypeStorage, type); - } - - addDeleteManyInputType(type: Class, deleteManyInputType: Class>): void { - this.deleteManyInputTypeStorage.set(type, deleteManyInputType); - } - - getDeleteManyInputType(type: Class): Class> | undefined { - return this.getValue(this.deleteManyInputTypeStorage, type); - } - getGraphqlObjectMetadata(objType: Class): ObjectTypeMetadata | undefined { return TypeMetadataStorage.getObjectTypesMetadata().find((o) => o.target === objType); } @@ -186,9 +92,6 @@ export class GraphQLQueryMetadataStorage { this.sortTypeStorage.clear(); this.connectionTypeStorage.clear(); this.edgeTypeStorage.clear(); - this.updateManyInputTypeStorage.clear(); - this.updateOneInputTypeStorage.clear(); - this.deleteManyInputTypeStorage.clear(); } private getValue(map: Map, Class>, key: Class): V | undefined { diff --git a/packages/query-graphql/src/resolvers/create.resolver.ts b/packages/query-graphql/src/resolvers/create.resolver.ts index f0bd151f8e..063478e984 100644 --- a/packages/query-graphql/src/resolvers/create.resolver.ts +++ b/packages/query-graphql/src/resolvers/create.resolver.ts @@ -2,10 +2,11 @@ * This is the doc comment for file1.ts * @packageDocumentation */ +// eslint-disable-next-line max-classes-per-file import { Class, DeepPartial } from '@nestjs-query/core'; import omit from 'lodash.omit'; import { InputType, ArgsType, Args, Resolver, PartialType } from '@nestjs/graphql'; -import { getDTONames } from '../common'; +import { DTONames, getDTONames } from '../common'; import { BaseServiceResolver, ResolverClass, ResolverOpts, ServiceResolver } from './resolver.interface'; import { CreateManyInputType, CreateOneInputType, MutationArgsType } from '../types'; import { ResolverMutation } from '../decorators'; @@ -19,22 +20,22 @@ export interface CreateResolverOpts = DeepPartia /** * The class to be used for `createOne` input. */ - CreateOneInput?: Class>; + CreateOneInput?: Class>; /** * The class to be used for `createMany` input. */ - CreateManyInput?: Class>; + CreateManyInput?: Class>; } export interface CreateResolver> extends ServiceResolver { - createOne(input: MutationArgsType>): Promise; + createOne(input: MutationArgsType>): Promise; - createMany(input: MutationArgsType>): Promise; + createMany(input: MutationArgsType>): Promise; } /** @internal */ -const defaultCreateInput = >(DTOClass: Class, baseName: string): Class => { - @InputType(`Create${baseName}`) +const defaultCreateDTO = >(dtoNames: DTONames, DTOClass: Class): Class => { + @InputType(`Create${dtoNames.baseName}`) // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore class PartialInput extends PartialType(DTOClass, InputType) {} @@ -42,6 +43,22 @@ const defaultCreateInput = >(DTOClass: Class; }; +/** @internal */ +const defaultCreateOneInput = (dtoNames: DTONames, InputDTO: Class): Class> => { + const { baseName, baseNameLower } = dtoNames; + @InputType(`CreateOne${baseName}Input`) + class CO extends CreateOneInputType(baseNameLower, InputDTO) {} + return CO; +}; + +/** @internal */ +const defaultCreateManyInput = (dtoNames: DTONames, InputDTO: Class): Class> => { + const { pluralBaseName, pluralBaseNameLower } = dtoNames; + @InputType(`CreateMany${pluralBaseName}Input`) + class CM extends CreateManyInputType(pluralBaseNameLower, InputDTO) {} + return CM; +}; + /** * @internal * Mixin to add `create` graphql endpoints. @@ -51,11 +68,12 @@ export const Creatable = >(DTOClass: Class, >( BaseClass: B, ): Class> & B => { - const { baseName, pluralBaseName } = getDTONames(DTOClass, opts); + const dtoNames = getDTONames(DTOClass, opts); + const { baseName, pluralBaseName } = dtoNames; const { - CreateDTOClass = defaultCreateInput(DTOClass, baseName), - CreateOneInput = CreateOneInputType(DTOClass, CreateDTOClass), - CreateManyInput = CreateManyInputType(DTOClass, CreateDTOClass), + CreateDTOClass = defaultCreateDTO(dtoNames, DTOClass), + CreateOneInput = defaultCreateOneInput(dtoNames, CreateDTOClass), + CreateManyInput = defaultCreateManyInput(dtoNames, CreateDTOClass), } = opts; const commonResolverOpts = omit( diff --git a/packages/query-graphql/src/resolvers/delete.resolver.ts b/packages/query-graphql/src/resolvers/delete.resolver.ts index 4af74c87f8..2a88ca96a4 100644 --- a/packages/query-graphql/src/resolvers/delete.resolver.ts +++ b/packages/query-graphql/src/resolvers/delete.resolver.ts @@ -1,7 +1,7 @@ import { Class, DeleteManyResponse } from '@nestjs-query/core'; import omit from 'lodash.omit'; -import { ObjectType, ArgsType, Resolver, Args, PartialType } from '@nestjs/graphql'; -import { getDTONames } from '../common'; +import { ObjectType, ArgsType, Resolver, Args, PartialType, InputType } from '@nestjs/graphql'; +import { DTONames, getDTONames } from '../common'; import { BaseServiceResolver, ResolverClass, ResolverOpts, ServiceResolver } from './resolver.interface'; import { DeleteManyInputType, DeleteManyResponseType, DeleteOneInputType, MutationArgsType } from '../types'; import { ResolverMutation } from '../decorators'; @@ -24,6 +24,14 @@ export interface DeleteResolver extends ServiceResolver { deleteMany(input: MutationArgsType>): Promise; } +/** @internal */ +const defaultDeleteManyInput = (dtoNames: DTONames, DTOClass: Class): Class> => { + const { pluralBaseName } = dtoNames; + @InputType(`DeleteMany${pluralBaseName}Input`) + class DM extends DeleteManyInputType(DTOClass) {} + return DM; +}; + /** * @internal * Mixin to add `delete` graphql endpoints. @@ -33,8 +41,9 @@ export const Deletable = (DTOClass: Class, opts: DeleteResolverOpts
( BaseClass: B, ): Class> & B => { - const { baseName, pluralBaseName } = getDTONames(DTOClass, opts); - const { DeleteOneInput = DeleteOneInputType(), DeleteManyInput = DeleteManyInputType(DTOClass) } = opts; + const dtoNames = getDTONames(DTOClass, opts); + const { baseName, pluralBaseName } = dtoNames; + const { DeleteOneInput = DeleteOneInputType(), DeleteManyInput = defaultDeleteManyInput(dtoNames, DTOClass) } = opts; const DMR = DeleteManyResponseType(); const commonResolverOpts = omit(opts, 'dtoName', 'one', 'many', 'DeleteOneInput', 'DeleteManyInput'); diff --git a/packages/query-graphql/src/resolvers/update.resolver.ts b/packages/query-graphql/src/resolvers/update.resolver.ts index 116b800ba8..0d8f13c675 100644 --- a/packages/query-graphql/src/resolvers/update.resolver.ts +++ b/packages/query-graphql/src/resolvers/update.resolver.ts @@ -1,7 +1,8 @@ +// eslint-disable-next-line max-classes-per-file import { Class, DeepPartial, UpdateManyResponse } from '@nestjs-query/core'; import { ArgsType, InputType, Resolver, Args, PartialType } from '@nestjs/graphql'; import omit from 'lodash.omit'; -import { getDTONames } from '../common'; +import { DTONames, getDTONames } from '../common'; import { MutationArgsType, UpdateManyInputType, UpdateManyResponseType, UpdateOneInputType } from '../types'; import { BaseServiceResolver, ResolverClass, ResolverOpts, ServiceResolver } from './resolver.interface'; import { ResolverMutation } from '../decorators'; @@ -9,18 +10,18 @@ import { transformAndValidate } from './helpers'; export interface UpdateResolverOpts = DeepPartial> extends ResolverOpts { UpdateDTOClass?: Class; - UpdateOneInput?: Class>; + UpdateOneInput?: Class>; UpdateManyInput?: Class>; } export interface UpdateResolver> extends ServiceResolver { - updateOne(input: MutationArgsType>): Promise; + updateOne(input: MutationArgsType>): Promise; updateMany(input: MutationArgsType>): Promise; } /** @internal */ -const defaultUpdateInput = >(DTOClass: Class, baseName: string): Class => { - @InputType(`Update${baseName}`) +const defaultUpdateInput = >(dtoNames: DTONames, DTOClass: Class): Class => { + @InputType(`Update${dtoNames.baseName}`) // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore class UpdateType extends PartialType(DTOClass, InputType) {} @@ -28,6 +29,26 @@ const defaultUpdateInput = >(DTOClass: Class; }; +/** @internal */ +const defaultUpdateOneInput = (dtoNames: DTONames, UpdateDTO: Class): Class> => { + const { baseName } = dtoNames; + @InputType(`UpdateOnce${baseName}Input`) + class UM extends UpdateOneInputType(UpdateDTO) {} + return UM; +}; + +/** @internal */ +const defaultUpdateManyInput = >( + dtoNames: DTONames, + DTOClass: Class, + UpdateDTO: Class, +): Class> => { + const { pluralBaseName } = dtoNames; + @InputType(`UpdateMany${pluralBaseName}Input`) + class UM extends UpdateManyInputType(DTOClass, UpdateDTO) {} + return UM; +}; + /** * @internal * Mixin to add `update` graphql endpoints. @@ -37,13 +58,14 @@ export const Updateable = >(DTOClass: Class >( BaseClass: B, ): Class> & B => { - const { baseName, pluralBaseName } = getDTONames(DTOClass, opts); + const dtoNames = getDTONames(DTOClass, opts); + const { baseName, pluralBaseName } = dtoNames; const UMR = UpdateManyResponseType(); const { - UpdateDTOClass = defaultUpdateInput(DTOClass, baseName), - UpdateOneInput = UpdateOneInputType(DTOClass, UpdateDTOClass), - UpdateManyInput = UpdateManyInputType(DTOClass, UpdateDTOClass), + UpdateDTOClass = defaultUpdateInput(dtoNames, DTOClass), + UpdateOneInput = defaultUpdateOneInput(dtoNames, UpdateDTOClass), + UpdateManyInput = defaultUpdateManyInput(dtoNames, DTOClass, UpdateDTOClass), } = opts; const commonResolverOpts = omit( diff --git a/packages/query-graphql/src/types/create-many-input.type.ts b/packages/query-graphql/src/types/create-many-input.type.ts index 69235713ac..c61e4a38f5 100644 --- a/packages/query-graphql/src/types/create-many-input.type.ts +++ b/packages/query-graphql/src/types/create-many-input.type.ts @@ -1,42 +1,34 @@ -import { Class, DeepPartial } from '@nestjs-query/core'; +import { Class } from '@nestjs-query/core'; import { Type } from 'class-transformer'; import { ValidateNested, ArrayNotEmpty } from 'class-validator'; import { Field, InputType } from '@nestjs/graphql'; -import { getDTONames } from '../common'; -import { getMetadataStorage } from '../metadata'; -export interface CreateManyInputType> { +export interface CreateManyInputType { input: C[]; } -export function CreateManyInputType>( - DTOClass: Class, - CreateClass: Class, -): Class> { - const metadataStorage = getMetadataStorage(); - const existing = metadataStorage.getCreateManyInputType(DTOClass); - if (existing) { - return existing; - } - - const { pluralBaseNameLower, pluralBaseName } = getDTONames(DTOClass); - @InputType(`CreateMany${pluralBaseName}Input`) - class CreateManyInput implements CreateManyInputType { - @Type(() => CreateClass) +/** + * The abstract input type for create many input types. + * @param fieldName - the name of field to be exposed in the graphql schema + * @param InputClass - the InputType to be used. + */ +export function CreateManyInputType(fieldName: string, InputClass: Class): Class> { + @InputType({ isAbstract: true }) + class CreateManyInput implements CreateManyInputType { + @Type(() => InputClass) @ArrayNotEmpty() @ValidateNested({ each: true }) - @Field(() => [CreateClass], { description: 'Array of records to create', name: pluralBaseNameLower }) + @Field(() => [InputClass], { description: 'Array of records to create', name: fieldName }) input!: C[]; - @Type(() => CreateClass) - get [pluralBaseNameLower](): C[] { + @Type(() => InputClass) + get [fieldName](): C[] { return this.input; } - set [pluralBaseNameLower](input: C[]) { + set [fieldName](input: C[]) { this.input = input; } } - metadataStorage.addCreateManyInputType(DTOClass, CreateManyInput); return CreateManyInput; } diff --git a/packages/query-graphql/src/types/create-one-input.type.ts b/packages/query-graphql/src/types/create-one-input.type.ts index d07650543e..45e86c6970 100644 --- a/packages/query-graphql/src/types/create-one-input.type.ts +++ b/packages/query-graphql/src/types/create-one-input.type.ts @@ -1,41 +1,34 @@ -import { Class, DeepPartial } from '@nestjs-query/core'; +import { Class } from '@nestjs-query/core'; import { Type } from 'class-transformer'; import { ValidateNested } from 'class-validator'; import { Field, InputType } from '@nestjs/graphql'; -import { getDTONames } from '../common'; -import { getMetadataStorage } from '../metadata'; -export interface CreateOneInputType> { +export interface CreateOneInputType { input: C; } -export function CreateOneInputType>( - DTOClass: Class, - CreateClass: Class, -): Class> { - const metadataStorage = getMetadataStorage(); - const existing = metadataStorage.getCreateOneInputType(DTOClass); - if (existing) { - return existing; - } - - const { baseNameLower, baseName } = getDTONames(DTOClass); - @InputType(`CreateOne${baseName}Input`) - class CreateOneInput implements CreateOneInputType { - @Type(() => CreateClass) +/** + * The abstract input type for create one operations. + * + * @param fieldName - The name of the field to be exposed in the graphql schema + * @param InputClass - The InputType to be used. + */ +export function CreateOneInputType(fieldName: string, InputClass: Class): Class> { + @InputType({ isAbstract: true }) + class CreateOneInput implements CreateOneInputType { + @Type(() => InputClass) @ValidateNested() - @Field(() => CreateClass, { description: 'The record to create', name: baseNameLower }) + @Field(() => InputClass, { description: 'The record to create', name: fieldName }) input!: C; - @Type(() => CreateClass) - get [baseNameLower](): C { + @Type(() => InputClass) + get [fieldName](): C { return this.input; } - set [baseNameLower](input: C) { + set [fieldName](input: C) { this.input = input; } } - metadataStorage.addCreateOneInputType(DTOClass, CreateOneInput); return CreateOneInput; } diff --git a/packages/query-graphql/src/types/delete-many-input.type.ts b/packages/query-graphql/src/types/delete-many-input.type.ts index 9099ce0f98..2f24431ea1 100644 --- a/packages/query-graphql/src/types/delete-many-input.type.ts +++ b/packages/query-graphql/src/types/delete-many-input.type.ts @@ -2,23 +2,19 @@ import { Filter, Class } from '@nestjs-query/core'; import { Field, InputType } from '@nestjs/graphql'; import { IsNotEmptyObject, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; -import { getDTONames } from '../common'; -import { getMetadataStorage } from '../metadata'; import { FilterType } from './query'; export interface DeleteManyInputType { filter: Filter; } +/** + * The abstract input type or delete many endpoints. + * @param DTOClass - The class the delete many input type is for. This will be used to create FilterType. + */ export function DeleteManyInputType(DTOClass: Class): Class> { - const metadataStorage = getMetadataStorage(); - const existing = metadataStorage.getDeleteManyInputType(DTOClass); - if (existing) { - return existing; - } - const { pluralBaseName } = getDTONames(DTOClass); const F = FilterType(DTOClass); - @InputType(`DeleteMany${pluralBaseName}Input`) + @InputType({ isAbstract: true }) class DeleteManyInput implements DeleteManyInputType { @IsNotEmptyObject() @Type(() => F) @@ -26,6 +22,5 @@ export function DeleteManyInputType(DTOClass: Class): Class F, { description: 'Filter to find records to delete' }) filter!: Filter; } - metadataStorage.addDeleteManyInputType(DTOClass, DeleteManyInput); return DeleteManyInput; } diff --git a/packages/query-graphql/src/types/delete-one-input.type.ts b/packages/query-graphql/src/types/delete-one-input.type.ts index b0a0ef4a81..c120cfc86b 100644 --- a/packages/query-graphql/src/types/delete-one-input.type.ts +++ b/packages/query-graphql/src/types/delete-one-input.type.ts @@ -8,6 +8,10 @@ export interface DeleteOneInputType { /** @internal */ let deleteOneInputType: Class | null = null; + +/** + * The input type for delete one endpoints. + */ export function DeleteOneInputType(): Class { if (deleteOneInputType) { return deleteOneInputType; 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 9f173efcf0..9622bb1263 100644 --- a/packages/query-graphql/src/types/update-many-input.type.ts +++ b/packages/query-graphql/src/types/update-many-input.type.ts @@ -2,8 +2,6 @@ import { DeepPartial, Filter, Class } from '@nestjs-query/core'; import { Field, InputType } from '@nestjs/graphql'; import { IsNotEmptyObject, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; -import { getDTONames } from '../common'; -import { getMetadataStorage } from '../metadata'; import { FilterType } from './query'; export interface UpdateManyInputType> { @@ -11,19 +9,18 @@ export interface UpdateManyInputType> { update: U; } +/** + * Input abstract type for all update many endpoints. + * @param DTOClass - The DTO used to create a FilterType for the update. + * @param UpdateType - The InputType to use for the update field. + */ export function UpdateManyInputType>( DTOClass: Class, UpdateType: Class, ): Class> { - const metadataStorage = getMetadataStorage(); - const existing = metadataStorage.getUpdateManyInputType(DTOClass); - if (existing) { - return existing; - } - const { pluralBaseName } = getDTONames(DTOClass); const F = FilterType(DTOClass); - @InputType(`UpdateMany${pluralBaseName}Input`) + @InputType({ isAbstract: true }) class UpdateManyInput implements UpdateManyInputType { @IsNotEmptyObject() @ValidateNested() @@ -36,7 +33,5 @@ export function UpdateManyInputType>( @Field(() => UpdateType, { description: 'The update to apply to all records found using the filter' }) update!: U; } - metadataStorage.addUpdateManyInputType(DTOClass, UpdateManyInput); - return UpdateManyInput; } diff --git a/packages/query-graphql/src/types/update-one-input.type.ts b/packages/query-graphql/src/types/update-one-input.type.ts index a3f41db67b..d88d404fdd 100644 --- a/packages/query-graphql/src/types/update-one-input.type.ts +++ b/packages/query-graphql/src/types/update-one-input.type.ts @@ -1,27 +1,20 @@ -import { DeepPartial, Class } from '@nestjs-query/core'; +import { Class } from '@nestjs-query/core'; import { Field, ID, InputType } from '@nestjs/graphql'; import { IsNotEmpty, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; -import { getDTONames } from '../common'; -import { getMetadataStorage } from '../metadata'; -export interface UpdateOneInputType> { +export interface UpdateOneInputType { id: string | number; update: U; } -export function UpdateOneInputType>( - DTOClass: Class, - UpdateType: Class, -): Class> { - const metadataStorage = getMetadataStorage(); - const existing = metadataStorage.getUpdateOneInputType(DTOClass); - if (existing) { - return existing; - } - const { baseName } = getDTONames(DTOClass); - @InputType(`UpdateOne${baseName}Input`) - class UpdateOneInput implements UpdateOneInputType { +/** + * The abstract input type for update one endpoints. + * @param UpdateType - The InputType to use for the update field. + */ +export function UpdateOneInputType(UpdateType: Class): Class> { + @InputType({ isAbstract: true }) + class UpdateOneInput implements UpdateOneInputType { @IsNotEmpty() @Field(() => ID, { description: 'The id of the record to update' }) id!: string | number; @@ -31,6 +24,5 @@ export function UpdateOneInputType>( @Field(() => UpdateType, { description: 'The update to apply.' }) update!: U; } - metadataStorage.addUpdateOneInputType(DTOClass, UpdateOneInput); return UpdateOneInput; } 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 58ae04edac..bdd81ecc17 100644 --- a/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts +++ b/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts @@ -79,7 +79,7 @@ describe('TypeOrmQueryService', (): void => { when(mockQueryBuilder.select(query)).thenReturn(instance(selectQueryBuilder)); when(selectQueryBuilder.getMany()).thenResolve(entities); const queryResult = await queryService.query(query); - expect(queryResult).toEqual(entities); + return expect(queryResult).toEqual(entities); }); }); @@ -97,7 +97,7 @@ describe('TypeOrmQueryService', (): void => { when(mockRelationQueryBuilder.select(objectContaining(entity), query)).thenReturn(instance(selectQueryBuilder)); when(selectQueryBuilder.getMany()).thenResolve(relations); const queryResult = await queryService.queryRelations(TestRelation, relationName, entity, query); - expect(queryResult).toEqual(relations); + return expect(queryResult).toEqual(relations); }); }); describe('with multiple entities', () => { @@ -133,7 +133,7 @@ describe('TypeOrmQueryService', (): void => { const queryResult = await queryService.queryRelations(TestRelation, relationName, entities, { paging: { limit: 2 }, }); - expect(queryResult).toEqual( + return expect(queryResult).toEqual( new Map([ [entities[0], entityOneRelations], [entities[1], entityTwoRelations], @@ -171,7 +171,7 @@ describe('TypeOrmQueryService', (): void => { const queryResult = await queryService.queryRelations(TestRelation, relationName, entities, { paging: { limit: 2 }, }); - expect(queryResult).toEqual(new Map([[entities[0], entityOneRelations]])); + return expect(queryResult).toEqual(new Map([[entities[0], entityOneRelations]])); }); }); }); @@ -192,7 +192,7 @@ describe('TypeOrmQueryService', (): void => { // @ts-ignore when(mockRepo.metadata).thenReturn({ relations: [{ propertyName: relationName, type: TestRelation }] }); const queryResult = await queryService.findRelation(TestRelation, relationName, entity); - expect(queryResult).toEqual(relation); + return expect(queryResult).toEqual(relation); }); it('should return undefined select if no results are found.', async () => { @@ -209,7 +209,7 @@ describe('TypeOrmQueryService', (): void => { // @ts-ignore when(mockRepo.metadata).thenReturn({ relations: [{ propertyName: relationName, type: TestRelation }] }); const queryResult = await queryService.findRelation(TestRelation, relationName, entity); - expect(queryResult).toBeUndefined(); + return expect(queryResult).toBeUndefined(); }); it('throw an error if a relation with that name is not found.', async () => { @@ -224,7 +224,7 @@ describe('TypeOrmQueryService', (): void => { when(relationQueryBuilder.loadOne()).thenResolve(undefined); // @ts-ignore when(mockRepo.metadata).thenReturn({ relations: [] }); - expect(queryService.findRelation(TestRelation, relationName, entity)).rejects.toThrowError( + return expect(queryService.findRelation(TestRelation, relationName, entity)).rejects.toThrowError( 'Unable to find relation testRelations on TestEntity', ); }); @@ -268,7 +268,7 @@ describe('TypeOrmQueryService', (): void => { ]); // @ts-ignore const queryResult = await queryService.findRelation(TestRelation, relationName, entities); - expect(queryResult).toEqual( + return expect(queryResult).toEqual( new Map([ [entities[0], entityOneRelation], [entities[1], entityTwoRelation], @@ -306,7 +306,7 @@ describe('TypeOrmQueryService', (): void => { ]); // @ts-ignore const queryResult = await queryService.findRelation(TestRelation, relationName, entities); - expect(queryResult).toEqual(new Map([[entities[0], entityOneRelation]])); + return expect(queryResult).toEqual(new Map([[entities[0], entityOneRelation]])); }); }); }); @@ -326,7 +326,7 @@ describe('TypeOrmQueryService', (): void => { when(relationQueryBuilder.of(objectContaining(entity))).thenReturn(instance(relationQueryBuilder)); when(relationQueryBuilder.add(relationIds)).thenResolve(); const queryResult = await queryService.addRelations(relationName, entity.testEntityPk, relationIds); - expect(queryResult).toEqual(entity); + return expect(queryResult).toEqual(entity); }); }); @@ -344,7 +344,7 @@ describe('TypeOrmQueryService', (): void => { when(relationQueryBuilder.of(objectContaining(entity))).thenReturn(instance(relationQueryBuilder)); when(relationQueryBuilder.set(relation.testRelationPk)).thenResolve(); const queryResult = await queryService.setRelation(relationName, entity.testEntityPk, relation.testRelationPk); - expect(queryResult).toEqual(entity); + return expect(queryResult).toEqual(entity); }); }); @@ -363,7 +363,7 @@ describe('TypeOrmQueryService', (): void => { when(relationQueryBuilder.of(objectContaining(entity))).thenReturn(instance(relationQueryBuilder)); when(relationQueryBuilder.remove(relationIds)).thenResolve(); const queryResult = await queryService.removeRelations(relationName, entity.testEntityPk, relationIds); - expect(queryResult).toEqual(entity); + return expect(queryResult).toEqual(entity); }); }); @@ -381,7 +381,7 @@ describe('TypeOrmQueryService', (): void => { when(relationQueryBuilder.of(objectContaining(entity))).thenReturn(instance(relationQueryBuilder)); when(relationQueryBuilder.remove(relation.testRelationPk)).thenResolve(); const queryResult = await queryService.removeRelation(relationName, entity.testEntityPk, relation.testRelationPk); - expect(queryResult).toEqual(entity); + return expect(queryResult).toEqual(entity); }); }); @@ -392,14 +392,14 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.target).thenReturn(TestEntity); when(mockRepo.findOne(entity.testEntityPk)).thenResolve(entity); const queryResult = await queryService.findById(entity.testEntityPk); - expect(queryResult).toEqual(entity); + return expect(queryResult).toEqual(entity); }); it('return undefined if not found', async () => { const { queryService, mockRepo } = createQueryService(); when(mockRepo.findOne(1)).thenResolve(undefined); const queryResult = await queryService.findById(1); - expect(queryResult).toBeUndefined(); + return expect(queryResult).toBeUndefined(); }); }); @@ -410,7 +410,7 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.target).thenReturn(TestEntity); when(mockRepo.findOneOrFail(entity.testEntityPk)).thenResolve(entity); const queryResult = await queryService.getById(entity.testEntityPk); - expect(queryResult).toEqual(entity); + return expect(queryResult).toEqual(entity); }); }); @@ -420,9 +420,12 @@ describe('TypeOrmQueryService', (): void => { const entityInstances = entities.map((e) => plainToClass(TestEntity, e)); const { queryService, mockRepo } = createQueryService(); when(mockRepo.target).thenReturn(TestEntity); + entityInstances.forEach((e) => { + when(mockRepo.hasId(e)).thenReturn(false); + }); when(mockRepo.save(entities)).thenResolve(entityInstances); const queryResult = await queryService.createMany(entities); - expect(queryResult).toEqual(entityInstances); + return expect(queryResult).toEqual(entityInstances); }); it('call save on the repo with instances of entities when passed instances', async () => { @@ -435,7 +438,7 @@ describe('TypeOrmQueryService', (): void => { }); when(mockRepo.save(deepEqual(entityInstances))).thenResolve(entityInstances); const queryResult = await queryService.createMany(entityInstances); - expect(queryResult).toEqual(entityInstances); + return expect(queryResult).toEqual(entityInstances); }); it('should reject if the entity contains an id', async () => { @@ -445,10 +448,10 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.target).thenReturn(TestEntity); entityInstances.forEach((e) => { when(mockRepo.hasId(e)).thenReturn(true); + when(mockRepo.getId(e)).thenReturn(e.testEntityPk); + when(mockRepo.findOne(e.testEntityPk)).thenResolve(e); }); - expect(queryService.createMany(entityInstances)).rejects.toThrowError( - 'Id cannot be specified when creating or updating', - ); + return expect(queryService.createMany(entityInstances)).rejects.toThrowError('Entity already exists'); }); }); @@ -460,7 +463,7 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.target).thenReturn(TestEntity); when(mockRepo.save(deepEqual(entityInstance))).thenResolve(entityInstance); const queryResult = await queryService.createOne(entityInstance); - expect(queryResult).toEqual(entityInstance); + return expect(queryResult).toEqual(entityInstance); }); it('call save on the repo with an instance of the entity when passed an instance', async () => { @@ -471,7 +474,7 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.hasId(entityInstance)).thenReturn(false); when(mockRepo.save(entity)).thenResolve(entityInstance); const queryResult = await queryService.createOne(entity); - expect(queryResult).toEqual(entityInstance); + return expect(queryResult).toEqual(entityInstance); }); it('should reject if the entity contains an id', async () => { @@ -480,9 +483,9 @@ describe('TypeOrmQueryService', (): void => { const { queryService, mockRepo } = createQueryService(); when(mockRepo.target).thenReturn(TestEntity); when(mockRepo.hasId(entityInstance)).thenReturn(true); - expect(queryService.createOne(entityInstance)).rejects.toThrowError( - 'Id cannot be specified when creating or updating', - ); + when(mockRepo.getId(entityInstance)).thenReturn(entityInstance.testEntityPk); + when(mockRepo.findOne(entityInstance.testEntityPk)).thenResolve(entityInstance); + return expect(queryService.createOne(entityInstance)).rejects.toThrowError('Entity already exists'); }); }); @@ -496,7 +499,7 @@ describe('TypeOrmQueryService', (): void => { when(mockQueryBuilder.delete(objectContaining({ filter: deleteMany }))).thenReturn(instance(deleteQueryBuilder)); when(deleteQueryBuilder.execute()).thenResolve({ raw: undefined, affected }); const queryResult = await queryService.deleteMany(deleteMany); - expect(queryResult).toEqual({ deletedCount: affected }); + return expect(queryResult).toEqual({ deletedCount: affected }); }); it('should return 0 if affected is not returned', async () => { @@ -507,7 +510,7 @@ describe('TypeOrmQueryService', (): void => { when(mockQueryBuilder.delete(objectContaining({ filter: deleteMany }))).thenReturn(instance(deleteQueryBuilder)); when(deleteQueryBuilder.execute()).thenResolve({ raw: undefined }); const queryResult = await queryService.deleteMany(deleteMany); - expect(queryResult).toEqual({ deletedCount: 0 }); + return expect(queryResult).toEqual({ deletedCount: 0 }); }); }); @@ -520,7 +523,7 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.findOneOrFail(testEntityPk)).thenResolve(entity); when(mockRepo.remove(entity)).thenResolve(entity); const queryResult = await queryService.deleteOne(testEntityPk); - expect(queryResult).toEqual(entity); + return expect(queryResult).toEqual(entity); }); it('call fail if the entity is not found', async () => { @@ -548,7 +551,7 @@ describe('TypeOrmQueryService', (): void => { when(mockUpdateQueryBuilder.execute()).thenResolve({ generatedMaps: [], raw: undefined, affected }); when(mockRepo.remove(entity)).thenResolve(entity); const queryResult = await queryService.updateMany(update, filter); - expect(queryResult).toEqual({ updatedCount: affected }); + return expect(queryResult).toEqual({ updatedCount: affected }); }); it('should reject if the update contains a primary key', async () => { @@ -559,8 +562,8 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.target).thenReturn(TestEntity); when(mockRepo.hasId(update as TestEntity)).thenReturn(true); when(mockRepo.remove(entity)).thenResolve(entity); - expect(queryService.updateMany(update, filter)).rejects.toThrowError( - 'Id cannot be specified when creating or updating', + return expect(queryService.updateMany(update, filter)).rejects.toThrowError( + 'Id cannot be specified when updating', ); }); @@ -577,7 +580,7 @@ describe('TypeOrmQueryService', (): void => { when(mockUpdateQueryBuilder.execute()).thenResolve({ generatedMaps: [], raw: undefined }); when(mockRepo.remove(entity)).thenResolve(entity); const queryResult = await queryService.updateMany(update, filter); - expect(queryResult).toEqual({ updatedCount: 0 }); + return expect(queryResult).toEqual({ updatedCount: 0 }); }); }); @@ -593,7 +596,7 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.merge(entity, update)).thenReturn(savedEntity); when(mockRepo.save(deepEqual(savedEntity))).thenResolve(savedEntity); const queryResult = await queryService.updateOne(updateId, update); - expect(queryResult).toEqual(savedEntity); + return expect(queryResult).toEqual(savedEntity); }); it('should reject if the update contains a primary key', async () => { @@ -604,8 +607,8 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.target).thenReturn(TestEntity); when(mockRepo.hasId(update as TestEntity)).thenReturn(true); when(mockRepo.remove(entity)).thenResolve(entity); - expect(queryService.updateOne(updateId, update)).rejects.toThrowError( - 'Id cannot be specified when creating or updating', + return expect(queryService.updateOne(updateId, update)).rejects.toThrowError( + 'Id cannot be specified when updating', ); });