// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved. // Node module: @loopback/boot // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT import { BINDING_METADATA_KEY, config, Constructor, CoreBindings, hasInjections, inject, isDynamicValueProviderClass, MetadataInspector, } from '@loopback/core'; import {ApplicationWithServices} from '@loopback/service-proxy'; import debugFactory from 'debug'; import {BootBindings} from '../keys'; import {ArtifactOptions, booter} from '../types'; import {BaseArtifactBooter} from './base-artifact.booter'; const debug = debugFactory('loopback:boot:service-booter'); /** * A class that extends BaseArtifactBooter to boot the 'Service' artifact type. * Discovered services are bound using `app.service()`. * * Supported phases: configure, discover, load * * @param app - Application instance * @param projectRoot - Root of User Project relative to which all paths are resolved * @param bootConfig - Service Artifact Options Object */ @booter('services') export class ServiceBooter extends BaseArtifactBooter { constructor( @inject(CoreBindings.APPLICATION_INSTANCE) public app: ApplicationWithServices, @inject(BootBindings.PROJECT_ROOT) projectRoot: string, @config() public serviceConfig: ArtifactOptions = {}, ) { super( projectRoot, // Set Service Booter Options if passed in via bootConfig Object.assign({}, ServiceDefaults, serviceConfig), ); } /** * Uses super method to get a list of Artifact classes. Boot each file by * creating a DataSourceConstructor and binding it to the application class. */ async load() { await super.load(); for (const cls of this.classes) { if (!isBindableClass(cls)) continue; debug('Bind class: %s', cls.name); const binding = this.app.service(cls); debug('Binding created for class: %j', binding); } } } /** * Default ArtifactOptions for DataSourceBooter. */ export const ServiceDefaults: ArtifactOptions = { dirs: ['services'], extensions: ['.service.js'], nested: true, }; function isServiceProvider(cls: Constructor) { const hasSupportedName = cls.name.endsWith('Provider'); const hasValueMethod = typeof cls.prototype.value === 'function'; return hasSupportedName && hasValueMethod; } function isBindableClass(cls: Constructor) { if (MetadataInspector.getClassMetadata(BINDING_METADATA_KEY, cls)) { return true; } if (hasInjections(cls)) { return true; } if (isServiceProvider(cls)) { debug('Provider class found: %s', cls.name); return true; } if (isDynamicValueProviderClass(cls)) { debug('Dynamic value provider class found: %s', cls.name); return true; } debug('Skip class not decorated with @injectable: %s', cls.name); return false; }