UNPKG

6.77 kBJavaScriptView Raw
1"use strict";
2// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
3// Node module: @loopback/core
4// This file is licensed under the MIT License.
5// License text available at https://opensource.org/licenses/MIT
6Object.defineProperty(exports, "__esModule", { value: true });
7exports.asService = exports.createServiceBinding = exports.filterByServiceInterface = exports.service = void 0;
8const context_1 = require("@loopback/context");
9const keys_1 = require("./keys");
10/**
11 * `@service` injects a service instance that matches the class or interface.
12 *
13 * @param serviceInterface - Interface for the service. It can be in one of the
14 * following forms:
15 *
16 * - A class, such as MyService
17 * - A string that identifies the interface, such as `'MyService'`
18 * - A symbol that identifies the interface, such as `Symbol('MyService')`
19 *
20 * If not provided, the value is inferred from the design:type of the parameter
21 * or property
22 *
23 * @example
24 * ```ts
25 *
26 * const ctx = new Context();
27 * ctx.bind('my-service').toClass(MyService);
28 * ctx.bind('logger').toClass(Logger);
29 *
30 * export class MyController {
31 * constructor(@service(MyService) private myService: MyService) {}
32 *
33 * @service()
34 * private logger: Logger;
35 * }
36 *
37 * ctx.bind('my-controller').toClass(MyController);
38 * await myController = ctx.get<MyController>('my-controller');
39 * ```
40 */
41function service(serviceInterface, metadata) {
42 return (0, context_1.inject)('', { decorator: '@service', ...metadata }, (ctx, injection, session) => {
43 var _a;
44 let serviceType = serviceInterface;
45 if (!serviceType) {
46 if (typeof injection.methodDescriptorOrParameterIndex === 'number') {
47 serviceType = (_a = context_1.MetadataInspector.getDesignTypeForMethod(injection.target, injection.member)) === null || _a === void 0 ? void 0 : _a.parameterTypes[injection.methodDescriptorOrParameterIndex];
48 }
49 else {
50 serviceType = context_1.MetadataInspector.getDesignTypeForProperty(injection.target, injection.member);
51 }
52 }
53 if (serviceType === undefined) {
54 const targetName = context_1.DecoratorFactory.getTargetName(injection.target, injection.member, injection.methodDescriptorOrParameterIndex);
55 const msg = `No design-time type metadata found while inspecting ${targetName}. ` +
56 'You can either use `@service(ServiceClass)` or ensure `emitDecoratorMetadata` is enabled in your TypeScript configuration. ' +
57 'Run `tsc --showConfig` to print the final TypeScript configuration of your project.';
58 throw new Error(msg);
59 }
60 if (serviceType === Object || serviceType === Array) {
61 throw new Error('Service class cannot be inferred from design type. Use @service(ServiceClass).');
62 }
63 const view = new context_1.ContextView(ctx, filterByServiceInterface(serviceType));
64 const result = view.resolve({
65 optional: metadata === null || metadata === void 0 ? void 0 : metadata.optional,
66 asProxyWithInterceptors: metadata === null || metadata === void 0 ? void 0 : metadata.asProxyWithInterceptors,
67 session,
68 });
69 const serviceTypeName = typeof serviceType === 'string'
70 ? serviceType
71 : typeof serviceType === 'symbol'
72 ? serviceType.toString()
73 : serviceType.name;
74 return (0, context_1.transformValueOrPromise)(result, values => {
75 if (values.length === 1)
76 return values[0];
77 if (values.length >= 1) {
78 throw new Error(`More than one bindings found for ${serviceTypeName}`);
79 }
80 else {
81 if (metadata === null || metadata === void 0 ? void 0 : metadata.optional) {
82 return undefined;
83 }
84 throw new Error(`No binding found for ${serviceTypeName}. Make sure a service ` +
85 `binding is created in context ${ctx.name} with serviceInterface (${serviceTypeName}).`);
86 }
87 });
88 });
89}
90exports.service = service;
91/**
92 * Create a binding filter by service class
93 * @param serviceInterface - Service class matching the one used by `binding.toClass()`
94 * @param options - Options to control if subclasses should be skipped for matching
95 */
96function filterByServiceInterface(serviceInterface) {
97 return binding => binding.valueConstructor === serviceInterface ||
98 binding.tagMap[keys_1.CoreTags.SERVICE_INTERFACE] === serviceInterface;
99}
100exports.filterByServiceInterface = filterByServiceInterface;
101/**
102 * Create a service binding from a class or provider
103 * @param cls - Service class or provider
104 * @param options - Service options
105 */
106function createServiceBinding(cls, options = {}) {
107 var _a;
108 let name = options.name;
109 if (!name && (0, context_1.isProviderClass)(cls)) {
110 // Trim `Provider` from the default service name
111 // This is needed to keep backward compatibility
112 const templateFn = (0, context_1.bindingTemplateFor)(cls);
113 const template = context_1.Binding.bind('template').apply(templateFn);
114 if (template.tagMap[context_1.ContextTags.PROVIDER] &&
115 !template.tagMap[context_1.ContextTags.NAME]) {
116 // The class is a provider and no `name` tag is found
117 name = cls.name.replace(/Provider$/, '');
118 }
119 }
120 if (!name && (0, context_1.isDynamicValueProviderClass)(cls)) {
121 // Trim `Provider` from the default service name
122 const templateFn = (0, context_1.bindingTemplateFor)(cls);
123 const template = context_1.Binding.bind('template').apply(templateFn);
124 if (template.tagMap[context_1.ContextTags.DYNAMIC_VALUE_PROVIDER] &&
125 !template.tagMap[context_1.ContextTags.NAME]) {
126 // The class is a provider and no `name` tag is found
127 name = cls.name.replace(/Provider$/, '');
128 }
129 }
130 const binding = (0, context_1.createBindingFromClass)(cls, {
131 name,
132 type: keys_1.CoreTags.SERVICE,
133 ...options,
134 }).apply(asService((_a = options.interface) !== null && _a !== void 0 ? _a : cls));
135 return binding;
136}
137exports.createServiceBinding = createServiceBinding;
138/**
139 * Create a binding template for a service interface
140 * @param serviceInterface - Service interface
141 */
142function asService(serviceInterface) {
143 return function serviceTemplate(binding) {
144 binding.tag({
145 [context_1.ContextTags.TYPE]: keys_1.CoreTags.SERVICE,
146 [keys_1.CoreTags.SERVICE_INTERFACE]: serviceInterface,
147 });
148 };
149}
150exports.asService = asService;
151//# sourceMappingURL=service.js.map
\No newline at end of file