diff --git a/packages/feathers/src/application.ts b/packages/feathers/src/application.ts index 56b6e9fd5f..1346533ca7 100644 --- a/packages/feathers/src/application.ts +++ b/packages/feathers/src/application.ts @@ -1,6 +1,6 @@ import version from './version'; import { - EventEmitter, stripSlashes, createDebug, HOOKS + EventEmitter, stripSlashes, createDebug, HOOKS, hooks, middleware } from './dependencies'; import { eventHook, eventMixin } from './events'; import { hookMixin } from './hooks/index'; @@ -12,10 +12,9 @@ import { ServiceOptions, ServiceInterface, Application, - HookOptions, FeathersService, HookMap, - RegularHookMap + ApplicationHookOptions } from './declarations'; import { enableRegularHooks } from './hooks/regular'; @@ -36,6 +35,14 @@ export class Feathers extends EventEmitter implements Feathe constructor () { super(); this.regularHooks = enableRegularHooks(this); + hooks(this, { + setup: middleware().params('server').props({ + app: this + }), + teardown: middleware().params('server').props({ + app: this + }) + }); } get (name: L): Settings[L] { @@ -117,14 +124,14 @@ export class Feathers extends EventEmitter implements Feathe return this; } - hooks (hookMap: HookOptions) { - const regularMap = hookMap as RegularHookMap; + hooks (hookMap: ApplicationHookOptions) { + const untypedMap = hookMap as any; - if (regularMap.before || regularMap.after || regularMap.error) { - return this.regularHooks(regularMap); - } - - if (Array.isArray(hookMap)) { + if (untypedMap.before || untypedMap.after || untypedMap.error) { + this.regularHooks(untypedMap); + } else if (untypedMap.setup || untypedMap.teardown) { + hooks(this, untypedMap); + } else if (Array.isArray(hookMap)) { this.appHooks[HOOKS].push(...hookMap as any); } else { const methodHookMap = hookMap as HookMap, any>; diff --git a/packages/feathers/src/declarations.ts b/packages/feathers/src/declarations.ts index f1978ac474..00fafb00c3 100644 --- a/packages/feathers/src/declarations.ts +++ b/packages/feathers/src/declarations.ts @@ -230,7 +230,7 @@ export interface FeathersApplication { * * @param map The application hook settings. */ - hooks (map: HookOptions): this; + hooks (map: ApplicationHookOptions): this; } // This needs to be an interface instead of a type @@ -376,7 +376,17 @@ export type HookMap = { export type HookOptions = HookMap | HookFunction[] | RegularHookMap; -export type AppHookOptions = HookOptions & { - setup: any[], - teardown: any[] +export interface ApplicationHookContext extends BaseHookContext { + app: A; + server: any; } + +export type ApplicationHookFunction = + (context: ApplicationHookContext, next: NextFunction) => Promise; + +export type ApplicationHookMap = { + setup?: ApplicationHookFunction[], + teardown?: ApplicationHookFunction[] +} + +export type ApplicationHookOptions = HookOptions | ApplicationHookMap diff --git a/packages/feathers/test/hooks/app.test.ts b/packages/feathers/test/hooks/app.test.ts index e6b7c6ce33..96c51473bd 100644 --- a/packages/feathers/test/hooks/app.test.ts +++ b/packages/feathers/test/hooks/app.test.ts @@ -1,6 +1,6 @@ import assert from 'assert'; -import { feathers, Application } from '../../src'; +import { feathers, Application, ApplicationHookMap } from '../../src'; describe('app.hooks', () => { let app: Application; @@ -25,6 +25,39 @@ describe('app.hooks', () => { assert.strictEqual(typeof app.hooks, 'function'); }); + it('.setup and .teardown special hooks', async () => { + const app = feathers(); + const order: string[] = []; + const hooks: ApplicationHookMap = { + setup: [async (context, next) => { + assert.strictEqual(context.app, app); + order.push('setup 1'); + await next(); + }, async (_context, next) => { + order.push('setup 2'); + await next(); + order.push('setup after'); + }], + teardown: [async (context, next) => { + assert.strictEqual(context.app, app); + order.push('teardown 1'); + await next(); + }, async (_context, next) => { + order.push('teardown 2'); + await next(); + }] + } + + app.hooks(hooks); + + await app.setup(); + await app.teardown(); + + assert.deepStrictEqual(order, [ + 'setup 1', 'setup 2', 'setup after', 'teardown 1', 'teardown 2' + ]) + }); + describe('app.hooks([ async ])', () => { it('basic app async hook', async () => { const service = app.service('todos');