Skip to content

Commit

Permalink
feat(context): allow global interceptors to be sorted by group
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed May 6, 2019
1 parent 9dfe950 commit cf2ef94
Show file tree
Hide file tree
Showing 6 changed files with 290 additions and 82 deletions.
31 changes: 31 additions & 0 deletions docs/site/Interceptors.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,37 @@ Let's examine the list of interceptors invoked for each method on

Interceptors to apply: [`convertName`, `log`]

Global interceptors are invoked before class/method level ones unless they are
explicitly overridden by `@intercept`.

Global interceptors can be sorted as follows:

1. Tag global interceptor binding with `ContextTags.GLOBAL_INTERCEPTOR_GROUP`.
The tag value will be treated as the `group` name of the interceptor. For
example:

```ts
app
.bind('globalInterceptors.authInterceptor')
.to(authInterceptor)
.apply(asGlobalInterceptor('auth'));
```

If the group tag does not exist, the value is default to `''`.

2. Control the ordered groups for global interceptors

```ts
app
.bind(ContextBindings.GLOBAL_INTERCEPTOR_ORDERED_GROUPS)
.to(['log', 'auth']);
```

If ordered groups is not bound to
`ContextBindings.GLOBAL_INTERCEPTOR_ORDERED_GROUPS`, global interceptors will be
sorted by their group names alphabetically. Interceptors with unknown groups are
invoked before those listed in ordered groups.

## Create your own interceptors

Interceptors can be made available by LoopBack itself, extension modules, or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ describe('Interceptor', () => {
ctx
.bind('globalLog')
.to(globalLog)
.apply(asGlobalInterceptor);
.apply(asGlobalInterceptor());
}
});

Expand Down
125 changes: 124 additions & 1 deletion packages/context/src/__tests__/unit/interceptor.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@
// License text available at https://opensource.org/licenses/MIT

import {expect} from '@loopback/testlab';
import {InterceptorOrKey, mergeInterceptors} from '../..';
import {
asGlobalInterceptor,
Context,
ContextBindings,
Interceptor,
InterceptorOrKey,
InvocationContext,
mergeInterceptors,
} from '../..';

describe('mergeInterceptors', () => {
it('removes duplicate entries from the spec', () => {
Expand All @@ -31,3 +39,118 @@ describe('mergeInterceptors', () => {
).to.eql(expectedResult);
}
});

describe('globalInterceptors', () => {
let ctx: Context;

const logInterceptor: Interceptor = async (context, next) => {
await next();
};
const authInterceptor: Interceptor = async (context, next) => {
await next();
};

beforeEach(givenContext);

it('sorts by group', () => {
ctx
.bind(ContextBindings.GLOBAL_INTERCEPTOR_ORDERED_GROUPS)
.to(['log', 'auth']);

ctx
.bind('globalInterceptors.authInterceptor')
.to(authInterceptor)
.apply(asGlobalInterceptor('auth'));

ctx
.bind('globalInterceptors.logInterceptor')
.to(logInterceptor)
.apply(asGlobalInterceptor('log'));

const invocationCtx = givenInvocationContext();

const keys = invocationCtx.getGlobalInterceptorBindingKeys();
expect(keys).to.eql([
'globalInterceptors.logInterceptor',
'globalInterceptors.authInterceptor',
]);
});

it('sorts by group - unknown group comes before known ones', () => {
ctx
.bind(ContextBindings.GLOBAL_INTERCEPTOR_ORDERED_GROUPS)
.to(['log', 'auth']);

ctx
.bind('globalInterceptors.authInterceptor')
.to(authInterceptor)
.apply(asGlobalInterceptor('auth'));

ctx
.bind('globalInterceptors.logInterceptor')
.to(logInterceptor)
.apply(asGlobalInterceptor('unknown'));

const invocationCtx = givenInvocationContext();

const keys = invocationCtx.getGlobalInterceptorBindingKeys();
expect(keys).to.eql([
'globalInterceptors.logInterceptor',
'globalInterceptors.authInterceptor',
]);
});

it('sorts by group alphabetically without ordered group', () => {
ctx
.bind('globalInterceptors.authInterceptor')
.to(authInterceptor)
.apply(asGlobalInterceptor('auth'));

ctx
.bind('globalInterceptors.logInterceptor')
.to(logInterceptor)
.apply(asGlobalInterceptor('log'));

const invocationCtx = givenInvocationContext();

const keys = invocationCtx.getGlobalInterceptorBindingKeys();
expect(keys).to.eql([
'globalInterceptors.authInterceptor',
'globalInterceptors.logInterceptor',
]);
});

it('sorts by binding order without group tags', () => {
ctx
.bind('globalInterceptors.authInterceptor')
.to(authInterceptor)
.apply(asGlobalInterceptor());

ctx
.bind('globalInterceptors.logInterceptor')
.to(logInterceptor)
.apply(asGlobalInterceptor());

const invocationCtx = givenInvocationContext();

const keys = invocationCtx.getGlobalInterceptorBindingKeys();
expect(keys).to.eql([
'globalInterceptors.authInterceptor',
'globalInterceptors.logInterceptor',
]);
});

class MyController {
greet(name: string) {
return `Hello, ${name}`;
}
}

function givenContext() {
ctx = new Context();
}

function givenInvocationContext() {
return new InvocationContext(ctx, new MyController(), 'greet', ['John']);
}
});
Loading

0 comments on commit cf2ef94

Please sign in to comment.