From 5545345a7a1a51333d518d5a5cb8580d74ff2492 Mon Sep 17 00:00:00 2001 From: nflaig Date: Thu, 11 Jun 2020 15:04:56 +0200 Subject: [PATCH] feat(core): add services to component artifacts - add services property to Component interface - allow to contribute services from a component to an application - automatically bind service to application Signed-off-by: nflaig --- .../src/__tests__/unit/application.unit.ts | 29 +++++++++++++++++++ packages/core/src/application.ts | 5 +++- packages/core/src/component.ts | 17 ++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/packages/core/src/__tests__/unit/application.unit.ts b/packages/core/src/__tests__/unit/application.unit.ts index 61e76b5285e9..8f710233037c 100644 --- a/packages/core/src/__tests__/unit/application.unit.ts +++ b/packages/core/src/__tests__/unit/application.unit.ts @@ -171,6 +171,35 @@ describe('Application', () => { expect(binding.tagNames).to.containEql('foo'); }); + it('binds services from a component', () => { + class MyService {} + + class MyComponentWithServices implements Component { + services = [MyService]; + } + + app.component(MyComponentWithServices); + + expect( + app.getBinding('services.MyService').valueConstructor, + ).to.be.exactly(MyService); + }); + + it('binds services with @bind from a component', () => { + @bind({scope: BindingScope.TRANSIENT, tags: ['foo']}) + class MyService {} + + class MyComponentWithServices implements Component { + services = [MyService]; + } + + app.component(MyComponentWithServices); + + const binding = app.getBinding('services.MyService'); + expect(binding.scope).to.eql(BindingScope.TRANSIENT); + expect(binding.tagNames).to.containEql('foo'); + }); + it('honors tags when binding providers from a component', () => { @bind({tags: ['foo']}) class MyProvider implements Provider { diff --git a/packages/core/src/application.ts b/packages/core/src/application.ts index a6d525776f8a..acd25c063e1d 100644 --- a/packages/core/src/application.ts +++ b/packages/core/src/application.ts @@ -451,7 +451,7 @@ export class Application extends Context implements LifeCycleObserver { * ``` */ public service( - cls: Constructor>, + cls: ServiceOrProviderClass, nameOrOptions?: string | ServiceOptions, ): Binding { const options = toOptions(nameOrOptions); @@ -602,6 +602,9 @@ export interface ApplicationConfig { // eslint-disable-next-line @typescript-eslint/no-explicit-any export type ControllerClass = Constructor; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ServiceOrProviderClass = Constructor>; + /** * Type description for `package.json` */ diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 77fd8e4906be..7ef70aa3f943 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -10,7 +10,11 @@ import { createBindingFromClass, Provider, } from '@loopback/context'; -import {Application, ControllerClass} from './application'; +import { + Application, + ControllerClass, + ServiceOrProviderClass, +} from './application'; import {LifeCycleObserver} from './lifecycle'; import {Server} from './server'; @@ -71,6 +75,11 @@ export interface Component { lifeCycleObservers?: Constructor[]; + /** + * An array of service or provider classes + */ + services?: ServiceOrProviderClass[]; + /** * An array of bindings to be aded to the application context. * @@ -137,4 +146,10 @@ export function mountComponent(app: Application, component: Component) { app.lifeCycleObserver(observer); } } + + if (component.services) { + for (const service of component.services) { + app.service(service); + } + } }