Skip to content

Commit

Permalink
Merge branch 'soft-delete-pr' into custom-version
Browse files Browse the repository at this point in the history
  • Loading branch information
TriPSs committed Oct 28, 2021
2 parents 0fe9580 + 4c59cd8 commit 48cbd63
Show file tree
Hide file tree
Showing 13 changed files with 84 additions and 51 deletions.
4 changes: 2 additions & 2 deletions examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"apollo-server-plugin-base": "3.3.0",
"apollo-server-types": "3.3.0",
"class-validator": "0.13.1",
"graphql": "15.6.1",
"graphql": "15.7.1",
"graphql-query-complexity": "0.9.0",
"graphql-tools": "8.2.0",
"mongoose": "5.12.0",
Expand All @@ -56,7 +56,7 @@
"@nestjs/schematics": "8.0.4",
"@nestjs/testing": "8.1.2",
"@types/express": "4.17.13",
"@types/node": "14.17.29",
"@types/node": "14.17.32",
"@types/passport-jwt": "3.0.6",
"@types/passport-local": "1.0.34",
"@types/supertest": "2.0.11",
Expand Down
64 changes: 32 additions & 32 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/core/src/interfaces/delete-many-options.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { DeleteOneOptions } from './delete-one-options.interface';

export type DeleteManyOptions<DTO> = DeleteOneOptions<DTO>;
7 changes: 6 additions & 1 deletion packages/core/src/interfaces/delete-one-options.interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { Filterable } from './filterable.interface';

export type DeleteOneOptions<DTO> = Filterable<DTO>;
export interface DeleteOneOptions<DTO> extends Filterable<DTO> {
/**
* Use soft delete when doing delete mutation
*/
useSoftDelete?: boolean;
}
7 changes: 6 additions & 1 deletion packages/core/src/interfaces/find-by-id-options.interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { Filterable } from './filterable.interface';

export type FindByIdOptions<DTO> = Filterable<DTO>;
export interface FindByIdOptions<DTO> extends Filterable<DTO> {
/**
* Allow also deleted records to be get
*/
withDeleted?: boolean;
}
1 change: 1 addition & 0 deletions packages/core/src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export * from './find-by-id-options.interface';
export * from './get-by-id-options.interface';
export * from './update-one-options.interface';
export * from './delete-one-options.interface';
export * from './delete-many-options.interface';
export * from './filterable.interface';
3 changes: 2 additions & 1 deletion packages/core/src/services/query.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
GetByIdOptions,
UpdateOneOptions,
DeleteOneOptions,
DeleteManyOptions,
} from '../interfaces';

/**
Expand Down Expand Up @@ -271,7 +272,7 @@ export interface QueryService<DTO, C = DeepPartial<DTO>, U = DeepPartial<DTO>> {
*
* @param filter - the filter to find records to delete.
*/
deleteMany(filter: Filter<DTO>): Promise<DeleteManyResponse>;
deleteMany(filter: Filter<DTO>, opts?: DeleteManyOptions<DTO>): Promise<DeleteManyResponse>;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/query-graphql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"class-transformer": "0.4.0",
"class-validator": "0.13.1",
"dataloader": "2.0.0",
"graphql": "15.6.1",
"graphql": "15.7.1",
"graphql-subscriptions": "1.2.1",
"ts-mockito": "2.6.1",
"ts-morph": "12.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { Query, QueryOptions, ReturnTypeFunc } from '@nestjs/graphql';
import { applyDecorators } from '@nestjs/common';
import { isDisabled, ResolverMethod, ResolverMethodOpts } from './resolver-method.decorator';

export interface QueryResolverMethodOpts extends ResolverMethodOpts {
withDeleted?: boolean;
}

/**
* @internal
* Decorator for a graphql `query` endpoint.
Expand All @@ -12,7 +16,7 @@ import { isDisabled, ResolverMethod, ResolverMethodOpts } from './resolver-metho
export function ResolverQuery(
typeFunc: ReturnTypeFunc,
options?: QueryOptions,
...opts: ResolverMethodOpts[]
...opts: QueryResolverMethodOpts[]
): MethodDecorator {
if (isDisabled(opts)) {
return (): void => {};
Expand Down
12 changes: 10 additions & 2 deletions packages/query-graphql/src/resolvers/delete.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ export interface DeleteResolverOpts<DTO> extends SubscriptionResolverOpts {
* ArgsType for deleteMany mutation.
*/
DeleteManyInput?: Class<DeleteManyInputType<DTO>>;
/**
* Use soft delete when doing delete mutation
*/
useSoftDelete?: boolean;
}

export interface DeleteResolver<DTO, QS extends QueryService<DTO, unknown, unknown>> extends ServiceResolver<DTO, QS> {
Expand Down Expand Up @@ -85,7 +89,7 @@ export const Deletable =
const deleteManyMutationName = opts.many?.name ?? `deleteMany${pluralBaseName}`;
const DMR = DeleteManyResponseType();

const commonResolverOpts = omit(opts, 'dtoName', 'one', 'many', 'DeleteOneInput', 'DeleteManyInput');
const commonResolverOpts = omit(opts, 'dtoName', 'one', 'many', 'DeleteOneInput', 'DeleteManyInput', 'useSoftDelete');

@ObjectType(`${baseName}DeleteResponse`)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand Down Expand Up @@ -124,7 +128,10 @@ export const Deletable =
})
authorizeFilter?: Filter<DTO>,
): Promise<Partial<DTO>> {
const deletedResponse = await this.service.deleteOne(input.input.id, { filter: authorizeFilter ?? {} });
const deletedResponse = await this.service.deleteOne(input.input.id, {
filter: authorizeFilter ?? {},
useSoftDelete: opts?.useSoftDelete,
});
if (enableOneSubscriptions) {
await this.publishDeletedOneEvent(deletedResponse, authorizeFilter);
}
Expand All @@ -148,6 +155,7 @@ export const Deletable =
): Promise<DeleteManyResponse> {
const deleteManyResponse = await this.service.deleteMany(
mergeFilter(input.input.filter, authorizeFilter ?? {}),
opts ?? {},
);
if (enableManySubscriptions) {
await this.publishDeletedManyEvent(deleteManyResponse, authorizeFilter);
Expand Down
4 changes: 2 additions & 2 deletions packages/query-graphql/src/resolvers/read.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const Readable =
const { QueryArgs = QueryArgsType(DTOClass, { ...opts, connectionName: `${baseName}Connection` }) } = opts;
const { ConnectionType } = QueryArgs;

const commonResolverOpts = omit(opts, 'dtoName', 'one', 'many', 'QueryArgs', 'Connection');
const commonResolverOpts = omit(opts, 'dtoName', 'one', 'many', 'QueryArgs', 'Connection', 'withDeleted');
@ArgsType()
class QA extends QueryArgs {}

Expand All @@ -84,7 +84,7 @@ export const Readable =
})
authorizeFilter?: Filter<DTO>,
): Promise<DTO | undefined> {
return this.service.findById(input.id, { filter: authorizeFilter });
return this.service.findById(input.id, { filter: authorizeFilter, withDeleted: opts?.one?.withDeleted });
}

@ResolverQuery(
Expand Down
8 changes: 4 additions & 4 deletions packages/query-graphql/src/resolvers/resolver.interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { QueryService } from '@nestjs-query/core';
import { DTONamesOpts } from '../common';
import { ResolverMethodOpts, SubscriptionResolverMethodOpts } from '../decorators';
import { QueryResolverMethodOpts, SubscriptionResolverMethodOpts } from '../decorators';
import { GraphQLPubSub } from '../subscription';
import { PagingStrategies, QueryArgsTypeOpts } from '../types';

Expand All @@ -11,15 +11,15 @@ type NamedEndpoint = {
description?: string;
};

export interface ResolverOpts extends ResolverMethodOpts, DTONamesOpts {
export interface ResolverOpts extends QueryResolverMethodOpts, DTONamesOpts {
/**
* Options for single record graphql endpoints
*/
one?: ResolverMethodOpts & NamedEndpoint;
one?: QueryResolverMethodOpts & NamedEndpoint;
/**
* Options for multiple record graphql endpoints
*/
many?: ResolverMethodOpts & NamedEndpoint;
many?: QueryResolverMethodOpts & NamedEndpoint;
}

export interface SubscriptionResolverOpts extends SubscriptionResolverMethodOpts, DTONamesOpts {
Expand Down
14 changes: 10 additions & 4 deletions packages/query-typeorm/src/services/typeorm-query.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
UpdateOneOptions,
DeleteOneOptions,
Filterable,
DeleteManyOptions,
} from '@nestjs-query/core';
import { Repository, DeleteResult } from 'typeorm';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
Expand Down Expand Up @@ -97,7 +98,11 @@ export class TypeOrmQueryService<Entity>
* @param id - The id of the record to find.
*/
async findById(id: string | number, opts?: FindByIdOptions<Entity>): Promise<Entity | undefined> {
return this.filterQueryBuilder.selectById(id, opts ?? {}).getOne();
const qb = this.filterQueryBuilder.selectById(id, opts ?? {});
if (opts?.withDeleted) {
qb.withDeleted();
}
return qb.getOne();
}

/**
Expand Down Expand Up @@ -202,10 +207,11 @@ export class TypeOrmQueryService<Entity>
*
* @param id - The `id` of the entity to delete.
* @param filter Additional filter to use when finding the entity to delete.
* @param opts - Additional options.
*/
async deleteOne(id: string | number, opts?: DeleteOneOptions<Entity>): Promise<Entity> {
const entity = await this.getById(id, opts);
if (this.useSoftDelete) {
if (this.useSoftDelete || opts?.useSoftDelete) {
return this.repo.softRemove(entity);
}
return this.repo.remove(entity);
Expand All @@ -224,9 +230,9 @@ export class TypeOrmQueryService<Entity>
*
* @param filter - A `Filter` to find records to delete.
*/
async deleteMany(filter: Filter<Entity>): Promise<DeleteManyResponse> {
async deleteMany(filter: Filter<Entity>, opts?: DeleteManyOptions<Entity>): Promise<DeleteManyResponse> {
let deleteResult: DeleteResult;
if (this.useSoftDelete) {
if (this.useSoftDelete || opts?.useSoftDelete) {
deleteResult = await this.filterQueryBuilder.softDelete({ filter }).execute();
} else {
deleteResult = await this.filterQueryBuilder.delete({ filter }).execute();
Expand Down

0 comments on commit 48cbd63

Please sign in to comment.