Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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