-
Notifications
You must be signed in to change notification settings - Fork 139
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(graphql,hooks): Add before hooks to graphql mutations
- Loading branch information
1 parent
bb72af0
commit 3448955
Showing
11 changed files
with
223 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const BEFORE_CREATE_ONE = 'nestjs-query:before-create-one'; | ||
export const BEFORE_CREATE_MANY = 'nestjs-query:before-create-many'; | ||
|
||
export const BEFORE_UPDATE_ONE = 'nestjs-query:before-update-one'; | ||
export const BEFORE_UPDATE_MANY = 'nestjs-query:before-update-many'; | ||
|
||
export const BEFORE_DELETE_ONE = 'nestjs-query:before-delete-one'; | ||
export const BEFORE_DELETE_MANY = 'nestjs-query:before-delete-many'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Class } from '@nestjs-query/core'; | ||
|
||
export type ComposableDecorator = MethodDecorator | PropertyDecorator | ClassDecorator | ParameterDecorator; | ||
export type ComposedDecorator = MethodDecorator & PropertyDecorator & ClassDecorator & ParameterDecorator; | ||
|
||
export function composeDecorators(...decorators: ComposableDecorator[]): ComposedDecorator { | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
return <TFunction extends Function, Y>( | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
target: TFunction | object, | ||
propertyKey?: string | symbol, | ||
descriptorOrIndex?: TypedPropertyDescriptor<Y> | number, | ||
) => { | ||
decorators.forEach((decorator) => { | ||
if (target instanceof Function && !descriptorOrIndex) { | ||
return (decorator as ClassDecorator)(target); | ||
} | ||
if (typeof descriptorOrIndex === 'number') { | ||
return (decorator as ParameterDecorator)(target, propertyKey as string | symbol, descriptorOrIndex); | ||
} | ||
return (decorator as MethodDecorator | PropertyDecorator)( | ||
target, | ||
propertyKey as string | symbol, | ||
descriptorOrIndex as TypedPropertyDescriptor<Y>, | ||
); | ||
}); | ||
}; | ||
} | ||
|
||
type ClassDecoratorDataFunc<Data> = (data: Data) => ClassDecorator; | ||
export const classMetadataDecorator = <Data>(key: string): ClassDecoratorDataFunc<Data> => { | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
return (data: Data) => (target: Function): void => { | ||
Reflect.defineMetadata(key, data, target); | ||
}; | ||
}; | ||
|
||
export type MetaValue<MetaType> = MetaType | undefined; | ||
export function getClassMetadata<DTO, MetaType>(DTOClass: Class<DTO>, key: string): MetaValue<MetaType> { | ||
return Reflect.getMetadata(key, DTOClass) as MetaValue<MetaType>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { Class, DeepPartial } from '@nestjs-query/core'; | ||
import { | ||
CreateManyInputType, | ||
CreateOneInputType, | ||
DeleteManyInputType, | ||
DeleteOneInputType, | ||
UpdateManyInputType, | ||
UpdateOneInputType, | ||
} from '../types'; | ||
import { | ||
BEFORE_CREATE_MANY, | ||
BEFORE_CREATE_ONE, | ||
BEFORE_DELETE_MANY, | ||
BEFORE_DELETE_ONE, | ||
BEFORE_UPDATE_MANY, | ||
BEFORE_UPDATE_ONE, | ||
} from './constants'; | ||
import { getClassMetadata, classMetadataDecorator, MetaValue } from './decorator.utils'; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export type HookFunc<T, Context = any> = (instance: T, context: Context) => T | Promise<T>; | ||
export type CreateOneHook<DTO> = HookFunc<CreateOneInputType<DTO>>; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export const BeforeCreateOne = classMetadataDecorator<CreateOneHook<any>>(BEFORE_CREATE_ONE); | ||
export function getCreateOneHook<DTO>(DTOClass: Class<DTO>): MetaValue<CreateOneHook<DTO>> { | ||
return getClassMetadata(DTOClass, BEFORE_CREATE_ONE); | ||
} | ||
|
||
export type CreateManyHook<DTO> = HookFunc<CreateManyInputType<DTO>>; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export const BeforeCreateMany = classMetadataDecorator<CreateManyHook<any>>(BEFORE_CREATE_MANY); | ||
export function getCreateManyHook<DTO>(DTOClass: Class<DTO>): MetaValue<CreateManyHook<DTO>> { | ||
return getClassMetadata(DTOClass, BEFORE_CREATE_MANY); | ||
} | ||
|
||
export type UpdateOneHook<DTO> = HookFunc<UpdateOneInputType<DTO>>; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export const BeforeUpdateOne = classMetadataDecorator<UpdateOneHook<any>>(BEFORE_UPDATE_ONE); | ||
export function getUpdateOneHook<DTO, U extends DeepPartial<DTO>>(DTOClass: Class<DTO>): MetaValue<UpdateOneHook<U>> { | ||
return getClassMetadata(DTOClass, BEFORE_UPDATE_ONE); | ||
} | ||
|
||
export type UpdateManyHook<DTO, U extends DeepPartial<DTO>> = HookFunc<UpdateManyInputType<DTO, U>>; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export const BeforeUpdateMany = classMetadataDecorator<UpdateManyHook<any, any>>(BEFORE_UPDATE_MANY); | ||
export function getUpdateManyHook<DTO, U extends DeepPartial<DTO>>( | ||
DTOClass: Class<DTO>, | ||
): MetaValue<UpdateManyHook<DTO, U>> { | ||
return getClassMetadata(DTOClass, BEFORE_UPDATE_MANY); | ||
} | ||
|
||
export type DeleteOneHook = HookFunc<DeleteOneInputType>; | ||
export const BeforeDeleteOne = classMetadataDecorator<DeleteOneHook>(BEFORE_DELETE_ONE); | ||
export function getDeleteOneHook<DTO>(DTOClass: Class<DTO>): MetaValue<DeleteOneHook> { | ||
return getClassMetadata(DTOClass, BEFORE_DELETE_ONE); | ||
} | ||
|
||
export type DeleteManyHook<DTO> = HookFunc<DeleteManyInputType<DTO>>; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export const BeforeDeleteMany = classMetadataDecorator<DeleteManyHook<any>>(BEFORE_DELETE_MANY); | ||
export function getDeleteManyHook<DTO>(DTOClass: Class<DTO>): MetaValue<DeleteManyHook<DTO>> { | ||
return getClassMetadata(DTOClass, BEFORE_DELETE_MANY); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
packages/query-graphql/src/decorators/mutation-args.decorator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { Class } from '@nestjs-query/core'; | ||
import { Args, GqlExecutionContext } from '@nestjs/graphql'; | ||
import { createParamDecorator, ExecutionContext } from '@nestjs/common'; | ||
import { composeDecorators } from './decorator.utils'; | ||
import { transformAndValidate } from '../resolvers/helpers'; | ||
import { MutationArgsType } from '../types'; | ||
import { HookFunc } from './hook.decorator'; | ||
|
||
export const MutationArgs = <HookType>( | ||
HookArgsClass: Class<MutationArgsType<HookType>>, | ||
hook?: HookFunc<HookType>, | ||
): ParameterDecorator => { | ||
return composeDecorators( | ||
Args(), | ||
createParamDecorator(async (data: unknown, ctx: ExecutionContext) => { | ||
const gqlContext = GqlExecutionContext.create(ctx); | ||
const args = await transformAndValidate(HookArgsClass, gqlContext.getArgs()); | ||
if (hook) { | ||
return Object.assign(args, { input: hook(args.input, gqlContext.getContext()) }); | ||
} | ||
return args; | ||
})(), | ||
); | ||
}; |
28 changes: 3 additions & 25 deletions
28
packages/query-graphql/src/decorators/skip-if.decorator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,13 @@ | ||
import { composeDecorators, ComposableDecorator, ComposedDecorator } from './decorator.utils'; | ||
/** | ||
* @internal | ||
* Wraps Args to allow skipping decorating | ||
* @param check - checker to run. | ||
* @param decorators - The decorators to apply | ||
*/ | ||
export function SkipIf( | ||
check: () => boolean, | ||
...decorators: (MethodDecorator | PropertyDecorator | ClassDecorator | ParameterDecorator)[] | ||
): MethodDecorator & PropertyDecorator & ClassDecorator & ParameterDecorator { | ||
export function SkipIf(check: () => boolean, ...decorators: ComposableDecorator[]): ComposedDecorator { | ||
if (check()) { | ||
return (): void => {}; | ||
} | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
return <TFunction extends Function, Y>( | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
target: TFunction | object, | ||
propertyKey?: string | symbol, | ||
descriptorOrIndex?: TypedPropertyDescriptor<Y> | number, | ||
) => { | ||
decorators.forEach((decorator) => { | ||
if (target instanceof Function && !descriptorOrIndex) { | ||
return (decorator as ClassDecorator)(target); | ||
} | ||
if (typeof descriptorOrIndex === 'number') { | ||
return (decorator as ParameterDecorator)(target, propertyKey as string | symbol, descriptorOrIndex); | ||
} | ||
return (decorator as MethodDecorator | PropertyDecorator)( | ||
target, | ||
propertyKey as string | symbol, | ||
descriptorOrIndex as TypedPropertyDescriptor<Y>, | ||
); | ||
}); | ||
}; | ||
return composeDecorators(...decorators); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.