Skip to content

Commit

Permalink
feat: define sugar decorators for extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed Jan 16, 2019
1 parent 25ad6d9 commit 44742f9
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 11 deletions.
11 changes: 4 additions & 7 deletions examples/greeter-extension/src/greeter-extension-point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {inject, filterByTag, Getter} from '@loopback/context';
import {Greeter} from './types';
import {Getter} from '@loopback/context';
import chalk from 'chalk';
import {configuration, extensions, Greeter} from './types';

/**
* Options for the greeter extension point
Expand All @@ -23,16 +23,13 @@ export class GreeterExtensionPoint {
* Inject a getter function to fetch greeters (bindings tagged with
* 'greeter')
*/
@inject.getter(filterByTag({extensionPoint: 'greeter'}))
@extensions('greeter') // Sugar for @inject.getter(filterByTag({extensionPoint: 'greeter'}))
private greeters: Getter<Greeter[]>,
/**
* An extension point should be able to receive its options via dependency
* injection.
*
* FIXME (rfeng): A shortcut such as `@inject.config` is desired to avoid
* the hard-coded binding key
*/
@inject('greeter-extension-point.options', {optional: true})
@configuration() // Sugar for @inject('greeter-extension-point.options', {optional: true})
private options?: GreeterExtensionPointOptions,
) {}

Expand Down
52 changes: 48 additions & 4 deletions examples/greeter-extension/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {BindingTemplate, BindingScope} from '@loopback/context';
import {
BindingScope,
BindingTemplate,
filterByTag,
inject,
} from '@loopback/context';

/**
* Typically an extension point defines an interface as the contract for
Expand All @@ -14,9 +19,48 @@ export interface Greeter {
greet(name: string): string;
}

/**
* A factory function to create binding template for extensions of the given
* extension point
* @param extensionPoint Name/id of the extension point
*/
export function extensionFor(extensionPoint: string): BindingTemplate {
return binding =>
binding.inScope(BindingScope.SINGLETON).tag({extensionPoint});
}

/**
* A binding template for greeter extensions
* @param binding
*/
export const asGreeter: BindingTemplate = binding =>
binding.inScope(BindingScope.SINGLETON).tag({extensionPoint: 'greeter'});
export const asGreeter: BindingTemplate = extensionFor('greeter');

/**
* Shortcut to inject extensions for the given extension point. To be promoted
* as `@extensions` in `@loopback/core` module.
*
* @param extensionPoint Name/id of the extension point
*/
export function extensions(extensionPoint: string) {
return inject.getter(filterByTag({extensionPoint}));
}

/**
* Shortcut to inject configuration for the target binding. To be promoted
* as `@inject.config` in `@loopback/context` module.
*/
export function configuration() {
return inject(
'',
{decorator: '@inject.config', optional: true},
(ctx, injection, session?) => {
if (!session) return undefined;
// Find the key of the target binding
if (!session.currentBinding) return undefined;
const key = session.currentBinding!.key;
return ctx.get(`${key}.options`, {
session,
optional: injection.metadata && injection.metadata.optional,
});
},
);
}

0 comments on commit 44742f9

Please sign in to comment.