@ti-engine/core 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +393 -377
- package/LICENSE.md +321 -321
- package/README.md +599 -597
- package/bin/localization/labels.json +122 -122
- package/bin/settings.json +41 -41
- package/bin/start-instance.js +164 -164
- package/components/auditing.js +191 -191
- package/components/connection-observer.js +72 -72
- package/components/definitions.types.js +248 -248
- package/components/exchange/default/default-message-exchange.js +136 -136
- package/components/exchange/default/default-message-receiver.js +101 -101
- package/components/exchange/default/default-message-sender.js +100 -100
- package/components/exchange/message-dispatcher.js +168 -168
- package/components/exchange/message-exchange.js +449 -449
- package/components/exchange/message-handler.js +235 -235
- package/components/exchange/message-memory-cache.js +190 -190
- package/components/exchange/message-observer.js +126 -126
- package/components/exchange/message-receiver.js +181 -181
- package/components/exchange/message-sender.js +143 -143
- package/components/exchange/message-tracer.js +212 -212
- package/components/service-caller.js +370 -370
- package/components/service-consumer.js +131 -131
- package/components/service-executor.js +278 -278
- package/components/service-instance.js +316 -316
- package/components/service-provider.js +251 -251
- package/integrations/redis-integration.js +591 -591
- package/package.json +89 -89
- package/utils/cache.js +772 -772
- package/utils/config.js +103 -103
- package/utils/exceptions.js +368 -368
- package/utils/localization.js +298 -298
- package/utils/logger.js +82 -82
- package/utils/tools.js +632 -632
|
@@ -1,252 +1,252 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* The ti-engine is an open source, free to use—both for personal and commercial projects—framework for the creation of microservice-based solutions using node.js.
|
|
3
|
-
* Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
4
|
-
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
5
|
-
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
6
|
-
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const ServiceConsumer = require( "#service-consumer" );
|
|
10
|
-
const _ = require( "lodash" );
|
|
11
|
-
const path = require( "path" );
|
|
12
|
-
const exceptions = require( "#exceptions" );
|
|
13
|
-
const logger = require( "#logger" );
|
|
14
|
-
const messageDispatcher = require( "#message-dispatcher" );
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Abstract class used to define a Service Provider behavior.
|
|
18
|
-
* <br/>
|
|
19
|
-
* NOTE: Inherit this to create a module that can be started as a microservice provider instance.
|
|
20
|
-
* <br/>
|
|
21
|
-
* NOTE: A service provider is a microservice that offers an API of named business services that can be invoked by other
|
|
22
|
-
* microservices using {@link ServiceCall} objects. The provider will take care of the actual execution of that service and
|
|
23
|
-
* therefore acts as a "black box". The only necessary items are the service address and optional inbound parameters to be
|
|
24
|
-
* used in that service's logic. The result of the service's execution will be bundled in an {@link ServiceCallResult}
|
|
25
|
-
* object and returned to the caller.
|
|
26
|
-
*
|
|
27
|
-
* @class ServiceProvider
|
|
28
|
-
* @extends ServiceConsumer
|
|
29
|
-
* @abstract
|
|
30
|
-
* @public
|
|
31
|
-
*/
|
|
32
|
-
class ServiceProvider extends ServiceConsumer {
|
|
33
|
-
|
|
34
|
-
/** @type ServiceExecutor */
|
|
35
|
-
#serviceExecutor;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @constructor
|
|
39
|
-
* @param {string} serviceDomainName The service domain name for this service instance.
|
|
40
|
-
* @param {ServiceConfiguration} [serviceConfig] The JSON configuration for this service.
|
|
41
|
-
* @throws {TiException.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
|
|
42
|
-
*/
|
|
43
|
-
constructor( serviceDomainName, serviceConfig ) {
|
|
44
|
-
super( serviceDomainName, serviceConfig );
|
|
45
|
-
|
|
46
|
-
// make sure this abstract class cannot be instantiated:
|
|
47
|
-
if ( new.target === ServiceProvider ) {
|
|
48
|
-
throw exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_CLASS_INIT, { name: this.constructor.name } );
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/* Public interface */
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Perform initialization tasks when the service provider starts.
|
|
56
|
-
* <br/>
|
|
57
|
-
* NOTE: This method will be invoked automatically.
|
|
58
|
-
* <br/>
|
|
59
|
-
* NOTE: If you need to add more onStart logic, you can override this method but make sure to call it in the
|
|
60
|
-
* overriding method using: super.onStart()
|
|
61
|
-
*
|
|
62
|
-
* @method
|
|
63
|
-
* @returns {Promise}
|
|
64
|
-
* @override
|
|
65
|
-
* @public
|
|
66
|
-
*/
|
|
67
|
-
onStart() {
|
|
68
|
-
return new Promise( ( resolve, reject ) => {
|
|
69
|
-
const ServiceExecutor = require( "#service-executor" );
|
|
70
|
-
|
|
71
|
-
this.#serviceExecutor = new ServiceExecutor();
|
|
72
|
-
this.#serviceExecutor.configureVerifyAccess( this.verifyAccess );
|
|
73
|
-
|
|
74
|
-
super.onStart().then( () => {
|
|
75
|
-
let serviceDefinitions = this.serviceConfig.services;
|
|
76
|
-
return this.registerServices( serviceDefinitions );
|
|
77
|
-
} ).then( () => {
|
|
78
|
-
messageDispatcher.instance.addMessageObserverRequestsIn( this.#serviceExecutor );
|
|
79
|
-
resolve();
|
|
80
|
-
} ).catch( ( error ) => {
|
|
81
|
-
reject( exceptions.raise( error ) );
|
|
82
|
-
} );
|
|
83
|
-
} );
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Perform shut down and cleanup tasks when the service provider stops.
|
|
88
|
-
* <br/>
|
|
89
|
-
* NOTE: This method will be invoked automatically.
|
|
90
|
-
* <br/>
|
|
91
|
-
* NOTE: If you need to add more onStop logic, you can override this method but make sure to call it in the
|
|
92
|
-
* overriding method using: super.onStop()
|
|
93
|
-
*
|
|
94
|
-
* @method
|
|
95
|
-
* @returns {Promise}
|
|
96
|
-
* @override
|
|
97
|
-
* @public
|
|
98
|
-
*/
|
|
99
|
-
onStop() {
|
|
100
|
-
return new Promise( ( resolve, reject ) => {
|
|
101
|
-
super.onStop().then( () => {
|
|
102
|
-
resolve();
|
|
103
|
-
} ).catch( ( error ) => {
|
|
104
|
-
reject( exceptions.raise( error ) );
|
|
105
|
-
} );
|
|
106
|
-
} );
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Used to report health status of the service instance for external monitoring.
|
|
111
|
-
* This is a scheduled job that will be executed at SERVICE_HEALTH_CHECK_INTERVAL time.
|
|
112
|
-
* <br/>
|
|
113
|
-
* NOTE: By default, this method will update a Redis key with an expiration timer. You can override this
|
|
114
|
-
* functionality with something custom like calling an HTTP endpoint.
|
|
115
|
-
*
|
|
116
|
-
* @method
|
|
117
|
-
* @override
|
|
118
|
-
* @virtual
|
|
119
|
-
* @public
|
|
120
|
-
*/
|
|
121
|
-
reportHealthy() {
|
|
122
|
-
super.reportHealthy();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Used to verify whether the service caller has authorization to access the service.
|
|
127
|
-
* <br/>
|
|
128
|
-
* NOTE: Override this to implement authorization check. By default, this method simply returns.
|
|
129
|
-
*
|
|
130
|
-
* @method
|
|
131
|
-
* @param {string} authToken
|
|
132
|
-
* @param {ServiceAddress} serviceAddress
|
|
133
|
-
* @return {Promise}
|
|
134
|
-
* @virtual
|
|
135
|
-
* @public
|
|
136
|
-
*/
|
|
137
|
-
verifyAccess( authToken, serviceAddress ) {
|
|
138
|
-
return Promise.resolve();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Used to register a single service to the service provider's API. One service can have multiple versions accessible at the same time.
|
|
143
|
-
* <br/>
|
|
144
|
-
* NOTE: This will actually bind the serviceDefinition as the first parameter of the service handler function. When creating default service handlers,
|
|
145
|
-
* keep in mind that your first param must always be the 'serviceDefinition' and the second one will be the general 'serviceParams' object.
|
|
146
|
-
* <br/>
|
|
147
|
-
* NOTE: Additionally, if you intend to call another service inside the service handler, then you have to use a normal function for the handler and not
|
|
148
|
-
* an arrow function! Arrow functions cannot bind the scope of the parent class to themselves, and you won't have access to it and its methods.
|
|
149
|
-
*
|
|
150
|
-
* @method
|
|
151
|
-
* @param {ServiceDefinition} serviceDefinition Full service definition object.
|
|
152
|
-
* @param {ServiceHandlerMethod} [defaultServiceHandler=undefined] A default service handler in case there is one.
|
|
153
|
-
* @return {Promise}
|
|
154
|
-
* @public
|
|
155
|
-
*/
|
|
156
|
-
registerService( serviceDefinition, defaultServiceHandler = undefined ) {
|
|
157
|
-
return new Promise( ( resolve, reject ) => {
|
|
158
|
-
/** @type {ServiceHandlerMethod} */
|
|
159
|
-
let serviceHandler = null;
|
|
160
|
-
if ( serviceDefinition.serviceFile ) {
|
|
161
|
-
let serviceFilePath = path.normalize( path.join( process.cwd(), serviceDefinition.serviceFile ) );
|
|
162
|
-
try {
|
|
163
|
-
serviceHandler = require( serviceFilePath ).service;
|
|
164
|
-
} catch ( error ) {
|
|
165
|
-
logger.log( `Specified service handler file '${ serviceFilePath }' could not be loaded!`, logger.logSeverity.ERROR, error );
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
if ( typeof ( defaultServiceHandler ) === "function" ) {
|
|
169
|
-
serviceHandler = defaultServiceHandler;
|
|
170
|
-
} else {
|
|
171
|
-
logger.log( "A service cannot be registered without provided default service handler at the very least!", logger.logSeverity.WARNING, serviceDefinition );
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// if we have a valid service handler, proceed with the registration:
|
|
176
|
-
if ( typeof ( serviceHandler ) === "function" ) {
|
|
177
|
-
// make sure we have a version and parent service provider specified:
|
|
178
|
-
serviceDefinition.serviceVersion = serviceDefinition.serviceVersion || 1;
|
|
179
|
-
this.#serviceExecutor.addServiceHandler( serviceHandler, serviceDefinition, this ).then( () => {
|
|
180
|
-
resolve();
|
|
181
|
-
} ).catch( ( error ) => {
|
|
182
|
-
reject( exceptions.raise( error ) );
|
|
183
|
-
} );
|
|
184
|
-
} else {
|
|
185
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_BAD_SERVICE_HANDLER ) );
|
|
186
|
-
}
|
|
187
|
-
} );
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Used to register multiple services from the provided service definitions.
|
|
192
|
-
*
|
|
193
|
-
* @method
|
|
194
|
-
* @param {ServiceDefinition[]} serviceDefinitions
|
|
195
|
-
* @param {ServiceHandlerMethod} [defaultServiceHandler=undefined]
|
|
196
|
-
* @return {Promise}
|
|
197
|
-
* @public
|
|
198
|
-
*/
|
|
199
|
-
registerServices( serviceDefinitions, defaultServiceHandler = undefined ) {
|
|
200
|
-
return new Promise( ( resolve, reject ) => {
|
|
201
|
-
if ( serviceDefinitions ) {
|
|
202
|
-
logger.log( `Starting service registration process. There is ${ ( ( defaultServiceHandler ) ? "" : "NO" ) } default service handler provided.`, logger.logSeverity.INFO );
|
|
203
|
-
|
|
204
|
-
let promises = [];
|
|
205
|
-
_.forEach( serviceDefinitions, ( serviceDefinition ) => {
|
|
206
|
-
// NOTE: we are not going to interrupt the service interface loading if one of the services fails to load or is not found!
|
|
207
|
-
// If this happens, a corresponding log entry will be created, but the loading process will continue. Therefore, the following
|
|
208
|
-
// promise will always be resolved (unless a programming error occurs in it, of course).
|
|
209
|
-
let registrationPromise = ( serviceDefinition, defaultServiceHandler ) => {
|
|
210
|
-
return new Promise( ( resolve ) => {
|
|
211
|
-
this.registerService( serviceDefinition, defaultServiceHandler ).then( () => {
|
|
212
|
-
resolve( true );
|
|
213
|
-
} ).catch( ( error ) => {
|
|
214
|
-
logger.log( `Service registration failed for '${ serviceDefinition?.serviceAlias || "unknown-service" }'.`, logger.logSeverity.ERROR, error );
|
|
215
|
-
resolve( false );
|
|
216
|
-
} );
|
|
217
|
-
} );
|
|
218
|
-
};
|
|
219
|
-
promises.push( registrationPromise( serviceDefinition, defaultServiceHandler ) );
|
|
220
|
-
} );
|
|
221
|
-
|
|
222
|
-
Promise.all( promises ).then( ( result ) => {
|
|
223
|
-
let registrationResults = _.countBy( result, ( value ) => {
|
|
224
|
-
return value === true;
|
|
225
|
-
} );
|
|
226
|
-
logger.log( `Registration of defined services completed with ${ registrationResults[ "true" ] || 0 } successful out of ${ serviceDefinitions.length } total.`, logger.logSeverity.INFO );
|
|
227
|
-
|
|
228
|
-
resolve();
|
|
229
|
-
} ).catch( ( error ) => {
|
|
230
|
-
reject( exceptions.raise( error ) );
|
|
231
|
-
} );
|
|
232
|
-
} else {
|
|
233
|
-
logger.log( `Service registration process skipped as there are no service definitions provided.`, logger.logSeverity.NOTICE );
|
|
234
|
-
resolve();
|
|
235
|
-
}
|
|
236
|
-
} );
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Used to get an ordered list of all currently registered services. This does not include the service versions.
|
|
241
|
-
*
|
|
242
|
-
* @method
|
|
243
|
-
* @returns {string[]}
|
|
244
|
-
* @public
|
|
245
|
-
*/
|
|
246
|
-
getRegisteredServices() {
|
|
247
|
-
return _.sortBy( _.keys( this.#serviceExecutor.serviceInterface ) );
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
1
|
+
/*
|
|
2
|
+
* The ti-engine is an open source, free to use—both for personal and commercial projects—framework for the creation of microservice-based solutions using node.js.
|
|
3
|
+
* Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
5
|
+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
6
|
+
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const ServiceConsumer = require( "#service-consumer" );
|
|
10
|
+
const _ = require( "lodash" );
|
|
11
|
+
const path = require( "path" );
|
|
12
|
+
const exceptions = require( "#exceptions" );
|
|
13
|
+
const logger = require( "#logger" );
|
|
14
|
+
const messageDispatcher = require( "#message-dispatcher" );
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Abstract class used to define a Service Provider behavior.
|
|
18
|
+
* <br/>
|
|
19
|
+
* NOTE: Inherit this to create a module that can be started as a microservice provider instance.
|
|
20
|
+
* <br/>
|
|
21
|
+
* NOTE: A service provider is a microservice that offers an API of named business services that can be invoked by other
|
|
22
|
+
* microservices using {@link ServiceCall} objects. The provider will take care of the actual execution of that service and
|
|
23
|
+
* therefore acts as a "black box". The only necessary items are the service address and optional inbound parameters to be
|
|
24
|
+
* used in that service's logic. The result of the service's execution will be bundled in an {@link ServiceCallResult}
|
|
25
|
+
* object and returned to the caller.
|
|
26
|
+
*
|
|
27
|
+
* @class ServiceProvider
|
|
28
|
+
* @extends ServiceConsumer
|
|
29
|
+
* @abstract
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
class ServiceProvider extends ServiceConsumer {
|
|
33
|
+
|
|
34
|
+
/** @type ServiceExecutor */
|
|
35
|
+
#serviceExecutor;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @constructor
|
|
39
|
+
* @param {string} serviceDomainName The service domain name for this service instance.
|
|
40
|
+
* @param {ServiceConfiguration} [serviceConfig] The JSON configuration for this service.
|
|
41
|
+
* @throws {TiException.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
|
|
42
|
+
*/
|
|
43
|
+
constructor( serviceDomainName, serviceConfig ) {
|
|
44
|
+
super( serviceDomainName, serviceConfig );
|
|
45
|
+
|
|
46
|
+
// make sure this abstract class cannot be instantiated:
|
|
47
|
+
if ( new.target === ServiceProvider ) {
|
|
48
|
+
throw exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_CLASS_INIT, { name: this.constructor.name } );
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Public interface */
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Perform initialization tasks when the service provider starts.
|
|
56
|
+
* <br/>
|
|
57
|
+
* NOTE: This method will be invoked automatically.
|
|
58
|
+
* <br/>
|
|
59
|
+
* NOTE: If you need to add more onStart logic, you can override this method but make sure to call it in the
|
|
60
|
+
* overriding method using: super.onStart()
|
|
61
|
+
*
|
|
62
|
+
* @method
|
|
63
|
+
* @returns {Promise}
|
|
64
|
+
* @override
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
onStart() {
|
|
68
|
+
return new Promise( ( resolve, reject ) => {
|
|
69
|
+
const ServiceExecutor = require( "#service-executor" );
|
|
70
|
+
|
|
71
|
+
this.#serviceExecutor = new ServiceExecutor();
|
|
72
|
+
this.#serviceExecutor.configureVerifyAccess( this.verifyAccess );
|
|
73
|
+
|
|
74
|
+
super.onStart().then( () => {
|
|
75
|
+
let serviceDefinitions = this.serviceConfig.services;
|
|
76
|
+
return this.registerServices( serviceDefinitions );
|
|
77
|
+
} ).then( () => {
|
|
78
|
+
messageDispatcher.instance.addMessageObserverRequestsIn( this.#serviceExecutor );
|
|
79
|
+
resolve();
|
|
80
|
+
} ).catch( ( error ) => {
|
|
81
|
+
reject( exceptions.raise( error ) );
|
|
82
|
+
} );
|
|
83
|
+
} );
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Perform shut down and cleanup tasks when the service provider stops.
|
|
88
|
+
* <br/>
|
|
89
|
+
* NOTE: This method will be invoked automatically.
|
|
90
|
+
* <br/>
|
|
91
|
+
* NOTE: If you need to add more onStop logic, you can override this method but make sure to call it in the
|
|
92
|
+
* overriding method using: super.onStop()
|
|
93
|
+
*
|
|
94
|
+
* @method
|
|
95
|
+
* @returns {Promise}
|
|
96
|
+
* @override
|
|
97
|
+
* @public
|
|
98
|
+
*/
|
|
99
|
+
onStop() {
|
|
100
|
+
return new Promise( ( resolve, reject ) => {
|
|
101
|
+
super.onStop().then( () => {
|
|
102
|
+
resolve();
|
|
103
|
+
} ).catch( ( error ) => {
|
|
104
|
+
reject( exceptions.raise( error ) );
|
|
105
|
+
} );
|
|
106
|
+
} );
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Used to report health status of the service instance for external monitoring.
|
|
111
|
+
* This is a scheduled job that will be executed at SERVICE_HEALTH_CHECK_INTERVAL time.
|
|
112
|
+
* <br/>
|
|
113
|
+
* NOTE: By default, this method will update a Redis key with an expiration timer. You can override this
|
|
114
|
+
* functionality with something custom like calling an HTTP endpoint.
|
|
115
|
+
*
|
|
116
|
+
* @method
|
|
117
|
+
* @override
|
|
118
|
+
* @virtual
|
|
119
|
+
* @public
|
|
120
|
+
*/
|
|
121
|
+
reportHealthy() {
|
|
122
|
+
super.reportHealthy();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Used to verify whether the service caller has authorization to access the service.
|
|
127
|
+
* <br/>
|
|
128
|
+
* NOTE: Override this to implement authorization check. By default, this method simply returns.
|
|
129
|
+
*
|
|
130
|
+
* @method
|
|
131
|
+
* @param {string} authToken
|
|
132
|
+
* @param {ServiceAddress} serviceAddress
|
|
133
|
+
* @return {Promise}
|
|
134
|
+
* @virtual
|
|
135
|
+
* @public
|
|
136
|
+
*/
|
|
137
|
+
verifyAccess( authToken, serviceAddress ) {
|
|
138
|
+
return Promise.resolve();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Used to register a single service to the service provider's API. One service can have multiple versions accessible at the same time.
|
|
143
|
+
* <br/>
|
|
144
|
+
* NOTE: This will actually bind the serviceDefinition as the first parameter of the service handler function. When creating default service handlers,
|
|
145
|
+
* keep in mind that your first param must always be the 'serviceDefinition' and the second one will be the general 'serviceParams' object.
|
|
146
|
+
* <br/>
|
|
147
|
+
* NOTE: Additionally, if you intend to call another service inside the service handler, then you have to use a normal function for the handler and not
|
|
148
|
+
* an arrow function! Arrow functions cannot bind the scope of the parent class to themselves, and you won't have access to it and its methods.
|
|
149
|
+
*
|
|
150
|
+
* @method
|
|
151
|
+
* @param {ServiceDefinition} serviceDefinition Full service definition object.
|
|
152
|
+
* @param {ServiceHandlerMethod} [defaultServiceHandler=undefined] A default service handler in case there is one.
|
|
153
|
+
* @return {Promise}
|
|
154
|
+
* @public
|
|
155
|
+
*/
|
|
156
|
+
registerService( serviceDefinition, defaultServiceHandler = undefined ) {
|
|
157
|
+
return new Promise( ( resolve, reject ) => {
|
|
158
|
+
/** @type {ServiceHandlerMethod} */
|
|
159
|
+
let serviceHandler = null;
|
|
160
|
+
if ( serviceDefinition.serviceFile ) {
|
|
161
|
+
let serviceFilePath = path.normalize( path.join( process.cwd(), serviceDefinition.serviceFile ) );
|
|
162
|
+
try {
|
|
163
|
+
serviceHandler = require( serviceFilePath ).service;
|
|
164
|
+
} catch ( error ) {
|
|
165
|
+
logger.log( `Specified service handler file '${ serviceFilePath }' could not be loaded!`, logger.logSeverity.ERROR, error );
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
if ( typeof ( defaultServiceHandler ) === "function" ) {
|
|
169
|
+
serviceHandler = defaultServiceHandler;
|
|
170
|
+
} else {
|
|
171
|
+
logger.log( "A service cannot be registered without provided default service handler at the very least!", logger.logSeverity.WARNING, serviceDefinition );
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// if we have a valid service handler, proceed with the registration:
|
|
176
|
+
if ( typeof ( serviceHandler ) === "function" ) {
|
|
177
|
+
// make sure we have a version and parent service provider specified:
|
|
178
|
+
serviceDefinition.serviceVersion = serviceDefinition.serviceVersion || 1;
|
|
179
|
+
this.#serviceExecutor.addServiceHandler( serviceHandler, serviceDefinition, this ).then( () => {
|
|
180
|
+
resolve();
|
|
181
|
+
} ).catch( ( error ) => {
|
|
182
|
+
reject( exceptions.raise( error ) );
|
|
183
|
+
} );
|
|
184
|
+
} else {
|
|
185
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_BAD_SERVICE_HANDLER ) );
|
|
186
|
+
}
|
|
187
|
+
} );
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Used to register multiple services from the provided service definitions.
|
|
192
|
+
*
|
|
193
|
+
* @method
|
|
194
|
+
* @param {ServiceDefinition[]} serviceDefinitions
|
|
195
|
+
* @param {ServiceHandlerMethod} [defaultServiceHandler=undefined]
|
|
196
|
+
* @return {Promise}
|
|
197
|
+
* @public
|
|
198
|
+
*/
|
|
199
|
+
registerServices( serviceDefinitions, defaultServiceHandler = undefined ) {
|
|
200
|
+
return new Promise( ( resolve, reject ) => {
|
|
201
|
+
if ( serviceDefinitions ) {
|
|
202
|
+
logger.log( `Starting service registration process. There is ${ ( ( defaultServiceHandler ) ? "" : "NO" ) } default service handler provided.`, logger.logSeverity.INFO );
|
|
203
|
+
|
|
204
|
+
let promises = [];
|
|
205
|
+
_.forEach( serviceDefinitions, ( serviceDefinition ) => {
|
|
206
|
+
// NOTE: we are not going to interrupt the service interface loading if one of the services fails to load or is not found!
|
|
207
|
+
// If this happens, a corresponding log entry will be created, but the loading process will continue. Therefore, the following
|
|
208
|
+
// promise will always be resolved (unless a programming error occurs in it, of course).
|
|
209
|
+
let registrationPromise = ( serviceDefinition, defaultServiceHandler ) => {
|
|
210
|
+
return new Promise( ( resolve ) => {
|
|
211
|
+
this.registerService( serviceDefinition, defaultServiceHandler ).then( () => {
|
|
212
|
+
resolve( true );
|
|
213
|
+
} ).catch( ( error ) => {
|
|
214
|
+
logger.log( `Service registration failed for '${ serviceDefinition?.serviceAlias || "unknown-service" }'.`, logger.logSeverity.ERROR, error );
|
|
215
|
+
resolve( false );
|
|
216
|
+
} );
|
|
217
|
+
} );
|
|
218
|
+
};
|
|
219
|
+
promises.push( registrationPromise( serviceDefinition, defaultServiceHandler ) );
|
|
220
|
+
} );
|
|
221
|
+
|
|
222
|
+
Promise.all( promises ).then( ( result ) => {
|
|
223
|
+
let registrationResults = _.countBy( result, ( value ) => {
|
|
224
|
+
return value === true;
|
|
225
|
+
} );
|
|
226
|
+
logger.log( `Registration of defined services completed with ${ registrationResults[ "true" ] || 0 } successful out of ${ serviceDefinitions.length } total.`, logger.logSeverity.INFO );
|
|
227
|
+
|
|
228
|
+
resolve();
|
|
229
|
+
} ).catch( ( error ) => {
|
|
230
|
+
reject( exceptions.raise( error ) );
|
|
231
|
+
} );
|
|
232
|
+
} else {
|
|
233
|
+
logger.log( `Service registration process skipped as there are no service definitions provided.`, logger.logSeverity.NOTICE );
|
|
234
|
+
resolve();
|
|
235
|
+
}
|
|
236
|
+
} );
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Used to get an ordered list of all currently registered services. This does not include the service versions.
|
|
241
|
+
*
|
|
242
|
+
* @method
|
|
243
|
+
* @returns {string[]}
|
|
244
|
+
* @public
|
|
245
|
+
*/
|
|
246
|
+
getRegisteredServices() {
|
|
247
|
+
return _.sortBy( _.keys( this.#serviceExecutor.serviceInterface ) );
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
}
|
|
251
|
+
|
|
252
252
|
module.exports = ServiceProvider;
|