Skip to content

Commit

Permalink
feat(core): Add app.setup and app.teardown hook support (#2585)
Browse files Browse the repository at this point in the history
  • Loading branch information
daffl authored Apr 1, 2022
1 parent a3c3581 commit ae4ebee
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 15 deletions.
27 changes: 17 additions & 10 deletions packages/feathers/src/application.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -12,10 +12,9 @@ import {
ServiceOptions,
ServiceInterface,
Application,
HookOptions,
FeathersService,
HookMap,
RegularHookMap
ApplicationHookOptions
} from './declarations';
import { enableRegularHooks } from './hooks/regular';

Expand All @@ -36,6 +35,14 @@ export class Feathers<Services, Settings> 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<L extends keyof Settings & string> (name: L): Settings[L] {
Expand Down Expand Up @@ -117,14 +124,14 @@ export class Feathers<Services, Settings> extends EventEmitter implements Feathe
return this;
}

hooks (hookMap: HookOptions<this, any>) {
const regularMap = hookMap as RegularHookMap<this, any>;
hooks (hookMap: ApplicationHookOptions<this>) {
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<Application<Services, Settings>, any>;
Expand Down
18 changes: 14 additions & 4 deletions packages/feathers/src/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ export interface FeathersApplication<Services = any, Settings = any> {
*
* @param map The application hook settings.
*/
hooks (map: HookOptions<this, any>): this;
hooks (map: ApplicationHookOptions<this>): this;
}

// This needs to be an interface instead of a type
Expand Down Expand Up @@ -376,7 +376,17 @@ export type HookMap<A, S> = {
export type HookOptions<A, S> =
HookMap<A, S> | HookFunction<A, S>[] | RegularHookMap<A, S>;

export type AppHookOptions<A> = HookOptions<A, any> & {
setup: any[],
teardown: any[]
export interface ApplicationHookContext<A = Application> extends BaseHookContext {
app: A;
server: any;
}

export type ApplicationHookFunction<A> =
(context: ApplicationHookContext<A>, next: NextFunction) => Promise<void>;

export type ApplicationHookMap<A> = {
setup?: ApplicationHookFunction<A>[],
teardown?: ApplicationHookFunction<A>[]
}

export type ApplicationHookOptions<A> = HookOptions<A, any> | ApplicationHookMap<A>
35 changes: 34 additions & 1 deletion packages/feathers/test/hooks/app.test.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<typeof app> = {
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');
Expand Down

0 comments on commit ae4ebee

Please # to comment.