From accacafc158e215006c7cd01418b544cbe676a77 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 15 Jan 2019 16:22:08 -0800 Subject: [PATCH] feat: define sugar decorators for extensions --- .../src/greeter-extension-point.ts | 11 ++-- examples/greeter-extension/src/types.ts | 52 +++++++++++++++++-- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/examples/greeter-extension/src/greeter-extension-point.ts b/examples/greeter-extension/src/greeter-extension-point.ts index dfbbbfa59723..0428f015b8e1 100644 --- a/examples/greeter-extension/src/greeter-extension-point.ts +++ b/examples/greeter-extension/src/greeter-extension-point.ts @@ -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 @@ -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, /** * 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, ) {} diff --git a/examples/greeter-extension/src/types.ts b/examples/greeter-extension/src/types.ts index 2b8aa31da1f7..06b8a0af2e9e 100644 --- a/examples/greeter-extension/src/types.ts +++ b/examples/greeter-extension/src/types.ts @@ -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 @@ -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, + }); + }, + ); +}