Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add model hook decorators (#15333)
- Loading branch information
Showing
9 changed files
with
461 additions
and
2 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
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,3 @@ | ||
# decorators-legacy | ||
|
||
This directory regroups the decorators that are built using the legacy decorator proposal. |
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,75 @@ | ||
import upperFirst from 'lodash/upperFirst'; | ||
import type { ModelHooks } from '../../model-typescript.js'; | ||
import { Model } from '../../model.js'; | ||
import { isModelStatic } from '../../utils/model-utils.js'; | ||
|
||
export interface HookOptions { | ||
name?: string; | ||
} | ||
|
||
export type HookDecoratorArgs = [targetOrOptions: Object | HookOptions, propertyName?: string | symbol]; | ||
|
||
/** | ||
* Implementation for hook decorator functions. These are polymorphic. When | ||
* called with a single argument (IHookOptions) they return a decorator | ||
* factory function. When called with multiple arguments, they add the hook | ||
* to the model’s metadata. | ||
* | ||
* @param hookType | ||
* @param args | ||
*/ | ||
export function implementHookDecorator( | ||
hookType: keyof ModelHooks, | ||
args: HookDecoratorArgs, | ||
): MethodDecorator | undefined { | ||
if (args.length === 1) { | ||
const options: HookOptions = args[0]; | ||
|
||
return (target: Object, propertyName: string | symbol) => { | ||
addHook(target, propertyName, hookType, options); | ||
}; | ||
} | ||
|
||
const target = args[0]; | ||
const propertyName = args[1]!; | ||
|
||
addHook(target, propertyName, hookType); | ||
|
||
// eslint-disable-next-line consistent-return | ||
return undefined; | ||
} | ||
|
||
function addHook( | ||
targetModel: Object, | ||
methodName: string | symbol, | ||
hookType: keyof ModelHooks, | ||
options?: HookOptions, | ||
): void { | ||
if (typeof targetModel !== 'function') { | ||
throw new TypeError( | ||
`Decorator @${upperFirst(hookType)} has been used on method "${targetModel.constructor.name}.${String(methodName)}" which is not static. Only static methods can be used for hooks.`, | ||
); | ||
} | ||
|
||
if (!isModelStatic(targetModel)) { | ||
throw new TypeError( | ||
`Decorator @${upperFirst(hookType)} has been used on "${targetModel.name}.${String(methodName)}", but class "${targetModel.name}" does not extend Model. Hook decorators can only be used on models.`, | ||
); | ||
} | ||
|
||
// @ts-expect-error | ||
const targetMethod: unknown = targetModel[methodName]; | ||
if (typeof targetMethod !== 'function') { | ||
throw new TypeError( | ||
`Decorator @${upperFirst(hookType)} has been used on "${targetModel.name}.${String(methodName)}", which is not a method.`, | ||
); | ||
} | ||
|
||
if (methodName in Model) { | ||
throw new Error( | ||
`Decorator @${upperFirst(hookType)} has been used on "${targetModel.name}.${String(methodName)}", but method ${JSON.stringify(methodName)} already exists on the base Model class and replacing it can lead to issues.`, | ||
); | ||
} | ||
|
||
targetModel.hooks.addListener(hookType, targetMethod.bind(targetModel), options?.name); | ||
} |
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,34 @@ | ||
import Pkg from './index.js'; | ||
|
||
export const AfterAssociate = Pkg.AfterAssociate; | ||
export const AfterBulkCreate = Pkg.AfterBulkCreate; | ||
export const AfterBulkDestroy = Pkg.AfterBulkDestroy; | ||
export const AfterBulkRestore = Pkg.AfterBulkRestore; | ||
export const AfterBulkUpdate = Pkg.AfterBulkUpdate; | ||
export const AfterCreate = Pkg.AfterCreate; | ||
export const AfterDestroy = Pkg.AfterDestroy; | ||
export const AfterFind = Pkg.AfterFind; | ||
export const AfterRestore = Pkg.AfterRestore; | ||
export const AfterSave = Pkg.AfterSave; | ||
export const AfterSync = Pkg.AfterSync; | ||
export const AfterUpdate = Pkg.AfterUpdate; | ||
export const AfterUpsert = Pkg.AfterUpsert; | ||
export const AfterValidate = Pkg.AfterValidate; | ||
export const BeforeAssociate = Pkg.BeforeAssociate; | ||
export const BeforeBulkCreate = Pkg.BeforeBulkCreate; | ||
export const BeforeBulkDestroy = Pkg.BeforeBulkDestroy; | ||
export const BeforeBulkRestore = Pkg.BeforeBulkRestore; | ||
export const BeforeBulkUpdate = Pkg.BeforeBulkUpdate; | ||
export const BeforeCount = Pkg.BeforeCount; | ||
export const BeforeCreate = Pkg.BeforeCreate; | ||
export const BeforeDestroy = Pkg.BeforeDestroy; | ||
export const BeforeFind = Pkg.BeforeFind; | ||
export const BeforeFindAfterExpandIncludeAll = Pkg.BeforeFindAfterExpandIncludeAll; | ||
export const BeforeFindAfterOptions = Pkg.BeforeFindAfterOptions; | ||
export const BeforeRestore = Pkg.BeforeRestore; | ||
export const BeforeSave = Pkg.BeforeSave; | ||
export const BeforeSync = Pkg.BeforeSync; | ||
export const BeforeUpdate = Pkg.BeforeUpdate; | ||
export const BeforeUpsert = Pkg.BeforeUpsert; | ||
export const BeforeValidate = Pkg.BeforeValidate; | ||
export const ValidationFailed = Pkg.ValidationFailed; |
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,2 @@ | ||
export * from './model-hooks-bulk.js'; | ||
export * from './model-hooks-single.js'; |
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,50 @@ | ||
import type { HookDecoratorArgs, HookOptions } from './hook-decorators.js'; | ||
import { implementHookDecorator } from './hook-decorators.js'; | ||
|
||
export function BeforeBulkCreate(target: Object, propertyName: string): void; | ||
export function BeforeBulkCreate(options: HookOptions): MethodDecorator; | ||
export function BeforeBulkCreate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeBulkCreate', args); | ||
} | ||
|
||
export function AfterBulkCreate(target: Object, propertyName: string): void; | ||
export function AfterBulkCreate(options: HookOptions): MethodDecorator; | ||
export function AfterBulkCreate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterBulkCreate', args); | ||
} | ||
|
||
export function BeforeBulkDestroy(target: Object, propertyName: string): void; | ||
export function BeforeBulkDestroy(options: HookOptions): MethodDecorator; | ||
export function BeforeBulkDestroy(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeBulkDestroy', args); | ||
} | ||
|
||
export function AfterBulkDestroy(target: Object, propertyName: string): void; | ||
export function AfterBulkDestroy(options: HookOptions): MethodDecorator; | ||
export function AfterBulkDestroy(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterBulkDestroy', args); | ||
} | ||
|
||
export function BeforeBulkRestore(target: Object, propertyName: string): void; | ||
export function BeforeBulkRestore(options: HookOptions): MethodDecorator; | ||
export function BeforeBulkRestore(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeBulkRestore' as any, args); | ||
} | ||
|
||
export function AfterBulkRestore(target: Object, propertyName: string): void; | ||
export function AfterBulkRestore(options: HookOptions): MethodDecorator; | ||
export function AfterBulkRestore(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterBulkRestore' as any, args); | ||
} | ||
|
||
export function BeforeBulkUpdate(target: Object, propertyName: string): void; | ||
export function BeforeBulkUpdate(options: HookOptions): MethodDecorator; | ||
export function BeforeBulkUpdate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeBulkUpdate', args); | ||
} | ||
|
||
export function AfterBulkUpdate(target: Object, propertyName: string): void; | ||
export function AfterBulkUpdate(options: HookOptions): MethodDecorator; | ||
export function AfterBulkUpdate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterBulkUpdate', args); | ||
} |
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,146 @@ | ||
import type { HookOptions, HookDecoratorArgs } from './hook-decorators.js'; | ||
import { implementHookDecorator } from './hook-decorators.js'; | ||
|
||
export function BeforeAssociate(target: Object, propertyName: string): void; | ||
export function BeforeAssociate(options: HookOptions): MethodDecorator; | ||
export function BeforeAssociate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeAssociate', args); | ||
} | ||
|
||
export function AfterAssociate(target: Object, propertyName: string): void; | ||
export function AfterAssociate(options: HookOptions): MethodDecorator; | ||
export function AfterAssociate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterAssociate', args); | ||
} | ||
|
||
export function BeforeCount(target: Object, propertyName: string): void; | ||
export function BeforeCount(options: HookOptions): MethodDecorator; | ||
export function BeforeCount(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeCount', args); | ||
} | ||
|
||
export function BeforeCreate(target: Object, propertyName: string): void; | ||
export function BeforeCreate(options: HookOptions): MethodDecorator; | ||
export function BeforeCreate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeCreate', args); | ||
} | ||
|
||
export function AfterCreate(target: Object, propertyName: string): void; | ||
export function AfterCreate(options: HookOptions): MethodDecorator; | ||
export function AfterCreate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterCreate', args); | ||
} | ||
|
||
export function BeforeDestroy(target: Object, propertyName: string): void; | ||
export function BeforeDestroy(options: HookOptions): MethodDecorator; | ||
export function BeforeDestroy(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeDestroy', args); | ||
} | ||
|
||
export function AfterDestroy(target: Object, propertyName: string): void; | ||
export function AfterDestroy(options: HookOptions): MethodDecorator; | ||
export function AfterDestroy(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterDestroy', args); | ||
} | ||
|
||
export function BeforeFind(target: Object, propertyName: string): void; | ||
export function BeforeFind(options: HookOptions): MethodDecorator; | ||
export function BeforeFind(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeFind', args); | ||
} | ||
|
||
export function BeforeFindAfterExpandIncludeAll(target: Object, propertyName: string): void; | ||
export function BeforeFindAfterExpandIncludeAll(options: HookOptions): MethodDecorator; | ||
export function BeforeFindAfterExpandIncludeAll(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeFindAfterExpandIncludeAll', args); | ||
} | ||
|
||
export function BeforeFindAfterOptions(target: Object, propertyName: string): void; | ||
export function BeforeFindAfterOptions(options: HookOptions): MethodDecorator; | ||
export function BeforeFindAfterOptions(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeFindAfterOptions', args); | ||
} | ||
|
||
export function AfterFind(target: Object, propertyName: string): void; | ||
export function AfterFind(options: HookOptions): MethodDecorator; | ||
export function AfterFind(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterFind', args); | ||
} | ||
|
||
export function BeforeRestore(target: Object, propertyName: string): void; | ||
export function BeforeRestore(options: HookOptions): MethodDecorator; | ||
export function BeforeRestore(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeRestore' as any, args); | ||
} | ||
|
||
export function AfterRestore(target: Object, propertyName: string): void; | ||
export function AfterRestore(options: HookOptions): MethodDecorator; | ||
export function AfterRestore(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterRestore' as any, args); | ||
} | ||
|
||
export function BeforeSave(target: Object, propertyName: string): void; | ||
export function BeforeSave(options: HookOptions): MethodDecorator; | ||
export function BeforeSave(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeSave' as any, args); | ||
} | ||
|
||
export function AfterSave(target: Object, propertyName: string): void; | ||
export function AfterSave(options: HookOptions): MethodDecorator; | ||
export function AfterSave(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterSave' as any, args); | ||
} | ||
|
||
export function BeforeSync(target: Object, propertyName: string): void; | ||
export function BeforeSync(options: HookOptions): MethodDecorator; | ||
export function BeforeSync(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeSync', args); | ||
} | ||
|
||
export function AfterSync(target: Object, propertyName: string): void; | ||
export function AfterSync(options: HookOptions): MethodDecorator; | ||
export function AfterSync(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterSync', args); | ||
} | ||
|
||
export function BeforeUpdate(target: Object, propertyName: string): void; | ||
export function BeforeUpdate(options: HookOptions): MethodDecorator; | ||
export function BeforeUpdate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeUpdate', args); | ||
} | ||
|
||
export function AfterUpdate(target: Object, propertyName: string): void; | ||
export function AfterUpdate(options: HookOptions): MethodDecorator; | ||
export function AfterUpdate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterUpdate', args); | ||
} | ||
|
||
export function BeforeUpsert(target: Object, propertyName: string): void; | ||
export function BeforeUpsert(options: HookOptions): MethodDecorator; | ||
export function BeforeUpsert(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeUpsert' as any, args); | ||
} | ||
|
||
export function AfterUpsert(target: Object, propertyName: string): void; | ||
export function AfterUpsert(options: HookOptions): MethodDecorator; | ||
export function AfterUpsert(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterUpsert' as any, args); | ||
} | ||
|
||
export function BeforeValidate(target: Object, propertyName: string): void; | ||
export function BeforeValidate(options: HookOptions): MethodDecorator; | ||
export function BeforeValidate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('beforeValidate', args); | ||
} | ||
|
||
export function ValidationFailed(target: Object, propertyName: string): void; | ||
export function ValidationFailed(options: HookOptions): MethodDecorator; | ||
export function ValidationFailed(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('validationFailed' as any, args); | ||
} | ||
|
||
export function AfterValidate(target: Object, propertyName: string): void; | ||
export function AfterValidate(options: HookOptions): MethodDecorator; | ||
export function AfterValidate(...args: HookDecoratorArgs): undefined | MethodDecorator { | ||
return implementHookDecorator('afterValidate', args); | ||
} |
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.