@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.
Files changed (33) hide show
  1. package/CHANGELOG.md +393 -377
  2. package/LICENSE.md +321 -321
  3. package/README.md +599 -597
  4. package/bin/localization/labels.json +122 -122
  5. package/bin/settings.json +41 -41
  6. package/bin/start-instance.js +164 -164
  7. package/components/auditing.js +191 -191
  8. package/components/connection-observer.js +72 -72
  9. package/components/definitions.types.js +248 -248
  10. package/components/exchange/default/default-message-exchange.js +136 -136
  11. package/components/exchange/default/default-message-receiver.js +101 -101
  12. package/components/exchange/default/default-message-sender.js +100 -100
  13. package/components/exchange/message-dispatcher.js +168 -168
  14. package/components/exchange/message-exchange.js +449 -449
  15. package/components/exchange/message-handler.js +235 -235
  16. package/components/exchange/message-memory-cache.js +190 -190
  17. package/components/exchange/message-observer.js +126 -126
  18. package/components/exchange/message-receiver.js +181 -181
  19. package/components/exchange/message-sender.js +143 -143
  20. package/components/exchange/message-tracer.js +212 -212
  21. package/components/service-caller.js +370 -370
  22. package/components/service-consumer.js +131 -131
  23. package/components/service-executor.js +278 -278
  24. package/components/service-instance.js +316 -316
  25. package/components/service-provider.js +251 -251
  26. package/integrations/redis-integration.js +591 -591
  27. package/package.json +89 -89
  28. package/utils/cache.js +772 -772
  29. package/utils/config.js +103 -103
  30. package/utils/exceptions.js +368 -368
  31. package/utils/localization.js +298 -298
  32. package/utils/logger.js +82 -82
  33. package/utils/tools.js +632 -632
@@ -1,371 +1,371 @@
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 MessageObserver = require( "#message-observer" );
10
- const tools = require( "#tools" );
11
- const exceptions = require( "#exceptions" );
12
- const logger = require( "#logger" );
13
- const config = require( "#config" );
14
- const cache = require( "#cache" );
15
- const messageDispatcher = require( "#message-dispatcher" );
16
-
17
- /**
18
- * Used to assemble and prepare a new {@link ServiceCall} object.
19
- *
20
- * @method
21
- * @param {string} messageID The message ID of the service call. This has to be unique across the whole service call tree, including the current service call, and will be used to identify the service call in the service call tree.
22
- * @param {ServiceAddress} serviceAddress The service address has to define a valid service domain name, service alias, and optionally a service version.
23
- * @param {Object} serviceParams Set of named parameters to provide to the called service.
24
- * @param {ServiceExecContext} serviceExecContext The context in which the service call is being executed.
25
- * @returns {Promise<ServiceCall>}
26
- * @private
27
- */
28
- let prepareServiceCall = ( messageID, serviceAddress, serviceParams, serviceExecContext ) => {
29
- return new Promise( ( resolve ) => {
30
- const ServiceInstance = require( "#service-instance" );
31
-
32
- // assemble the new service call:
33
- let chainID = ( serviceExecContext.previousServiceCall ) ? serviceExecContext.previousServiceCall.chainID : tools.getUUID();
34
- let chainLevel = ( serviceExecContext.previousServiceCall ) ? serviceExecContext.previousServiceCall.chainLevel + 1 : 0;
35
- let source = {
36
- instanceID: ServiceInstance.instanceID,
37
- route: ServiceInstance.serviceDomainName
38
- };
39
- let destination = {
40
- instanceID: undefined,
41
- route: serviceAddress.serviceDomainName
42
- };
43
- /** @type ServiceCall */
44
- let serviceCall = {
45
- authToken: serviceExecContext.authToken,
46
- chainID: chainID,
47
- chainLevel: chainLevel,
48
- createdOn: Date.now(),
49
- destination: destination,
50
- executionTime: 0,
51
- exception: undefined,
52
- finishedOn: undefined,
53
- isCompleted: false,
54
- isSuccessful: undefined,
55
- messageID: messageID,
56
- payload: undefined,
57
- predecessor: ( serviceExecContext.previousServiceCall ) ? serviceExecContext.previousServiceCall.messageID : undefined,
58
- serviceAddress: serviceAddress,
59
- serviceParams: serviceParams,
60
- source: source,
61
- successors: undefined
62
- };
63
-
64
- resolve( serviceCall );
65
- } );
66
- }
67
-
68
- /**
69
- * Used to verify if the service is registered in the service registry.
70
- *
71
- * @method
72
- * @param {ServiceAddress} serviceAddress
73
- * @returns {Promise}
74
- * @private
75
- */
76
- let findServiceInRegistry = ( serviceAddress ) => {
77
- return new Promise( ( resolve, reject ) => {
78
- let serviceCatalog = config.getSetting( config.setting.SERVICE_REGISTRY_ADDRESS ) + serviceAddress.serviceDomainName;
79
- cache.instance.isSetMember( serviceCatalog, serviceAddress.serviceAlias ).then( ( result ) => {
80
- if ( result === true ) {
81
- resolve();
82
- } else {
83
- reject( exceptions.raise( exceptions.exceptionCode.E_COM_SERVICE_NOT_REGISTERED ) );
84
- }
85
- } ).catch( ( error ) => {
86
- reject( exceptions.raise( error ) );
87
- } );
88
- } );
89
- }
90
-
91
- /**
92
- * A class defining a service call processor.
93
- *
94
- * @class ServiceCallProcessor
95
- * @private
96
- */
97
- class ServiceCallProcessor {
98
-
99
- #messageID;
100
- #serviceAddress;
101
- #serviceParams;
102
- #serviceExecContext;
103
- #timeoutHandle;
104
- #taskCompletionHandler;
105
- #isProcessed = false;
106
-
107
- /**
108
- * @constructor
109
- * @param {ServiceAddress} serviceAddress The service address has to define a valid service domain name, service alias, and optionally a service version.
110
- * @param {Object} serviceParams Set of named parameters to provide to the called service.
111
- * @param {ServiceExecContext} serviceExecContext The context in which the service call is being executed.
112
- * @returns {ServiceCallProcessor}
113
- */
114
- constructor( serviceAddress, serviceParams, serviceExecContext ) {
115
- this.#messageID = tools.getUUID();
116
- this.#serviceAddress = serviceAddress;
117
- this.#serviceParams = serviceParams;
118
- this.#serviceExecContext = serviceExecContext;
119
- }
120
-
121
- /* Public interface */
122
-
123
- /**
124
- * The unique identifier of the service call.
125
- *
126
- * @property
127
- * @returns {string}
128
- * @public
129
- */
130
- get messageID() {
131
- return this.#messageID;
132
- }
133
-
134
- /**
135
- * Used to start the execution of the service call.
136
- * <br/>
137
- * NOTE: This method will time out after specific preconfigured time, in which case it will resolve with {@link E_COM_SERVICE_EXEC_TIMEOUT} error.
138
- *
139
- * @method
140
- * @returns {Promise<ServiceCallResult>}
141
- * @public
142
- */
143
- process() {
144
- return Promise.race( [ this.#execute(), this.#timeout() ] ).then( ( result ) => {
145
- clearTimeout( this.#timeoutHandle );
146
- this.#isProcessed = true;
147
- return result;
148
- } ).catch( ( error ) => {
149
- clearTimeout( this.#timeoutHandle );
150
- this.#isProcessed = true;
151
- logger.log( `Error during service call execution!`, logger.logSeverity.DEBUG, error );
152
- return {
153
- isSuccessful: false,
154
- exception: exceptions.raise( error ),
155
- payload: undefined
156
- };
157
- } );
158
- }
159
-
160
- /**
161
- * Used to complete the execution of the service call that was started within the {@link process} method.
162
- *
163
- * @method
164
- * @param {ServiceCall} serviceCall
165
- * @public
166
- */
167
- complete( serviceCall ) {
168
- if ( this.#isProcessed !== true ) {
169
- let serviceCallResult = {
170
- exception: serviceCall.exception,
171
- isSuccessful: ( serviceCall.isSuccessful !== undefined ) ? tools.toBool( serviceCall.isSuccessful ) : true,
172
- payload: serviceCall.payload
173
- };
174
- this.#taskCompletionHandler( serviceCallResult );
175
- }
176
- }
177
-
178
- /* Private interface */
179
-
180
- /**
181
- * Used to execute the service call.
182
- *
183
- * @method
184
- * @returns {Promise<ServiceCallResult>}
185
- * @private
186
- */
187
- #execute() {
188
- return new Promise( ( resolve, reject ) => {
189
- findServiceInRegistry( this.#serviceAddress ).then( () => {
190
- return prepareServiceCall( this.#messageID, this.#serviceAddress, this.#serviceParams, this.#serviceExecContext );
191
- } ).then( ( serviceCall ) => {
192
- return messageDispatcher.instance.sendRequest( serviceCall );
193
- } ).then( () => {
194
- this.#taskCompletionHandler = ( serviceCallResult ) => {
195
- resolve( serviceCallResult );
196
- };
197
- } ).catch( ( error ) => {
198
- if ( this.#isProcessed !== true ) {
199
- reject( exceptions.raise( error ) );
200
- }
201
- } );
202
- } );
203
- }
204
-
205
- /**
206
- * Used to time out the service call execution.
207
- *
208
- * @method
209
- * @returns {Promise<ServiceCallResult>}
210
- * @private
211
- */
212
- #timeout() {
213
- return new Promise( ( resolve, reject ) => {
214
- this.#timeoutHandle = setTimeout( () => {
215
- if ( this.#isProcessed !== true ) {
216
- reject( exceptions.raise( exceptions.exceptionCode.E_COM_SERVICE_EXEC_TIMEOUT ) );
217
- }
218
- }, config.getSetting( config.setting.SERVICE_EXECUTION_TIMEOUT ) );
219
- } );
220
- }
221
-
222
- }
223
-
224
- /**
225
- * A class defining a service caller behavior.
226
- *
227
- * @class ServiceCaller
228
- * @extends MessageObserver
229
- * @public
230
- */
231
- class ServiceCaller extends MessageObserver {
232
-
233
- #serviceCallProcessors = {};
234
-
235
- /**
236
- * @constructor
237
- */
238
- constructor() {
239
- super( 10 );
240
- }
241
-
242
- /* Public interface */
243
-
244
- /**
245
- * Used to call a service in the service ecosystem asynchronously.
246
- * <br/>
247
- * NOTE: This method will time out after specific preconfigured time, in which case it will resolve with {@link E_COM_SERVICE_EXEC_TIMEOUT} error.
248
- *
249
- * @method
250
- * @param {ServiceAddress} serviceAddress The service address has to define a valid service domain name, service alias, and optionally a service version.
251
- * @param {Object} serviceParams Set of named parameters to provide to the called service.
252
- * @param {ServiceExecContext} serviceExecContext The context in which the service call is being executed.
253
- * @returns {Promise<ServiceCallResult>} Will always return a service call result that can be either successful or not.
254
- * @public
255
- */
256
- executeServiceCall( serviceAddress, serviceParams, serviceExecContext ) {
257
- return new Promise( ( resolve ) => {
258
- let processor = new ServiceCallProcessor( serviceAddress, serviceParams, serviceExecContext );
259
- this.#addProcessor( processor.messageID, processor );
260
- processor.process().then( ( serviceCallResult ) => {
261
- this.#removeProcessor( processor.messageID );
262
- resolve( serviceCallResult );
263
- } );
264
- } );
265
- }
266
-
267
- /**
268
- * Once the proper message is received this method will trigger the completion of the pending {@link ServiceCall} execution started in {@link #executeServiceCall}.
269
- *
270
- * @method
271
- * @param {string} identifier The identifier of the observed connection.
272
- * @param {ServiceCall} serviceCall The service call message for processing.
273
- * @returns {ServiceCall} The service call message that was received.
274
- * @override
275
- * @public
276
- */
277
- onMessage( identifier, serviceCall ) {
278
- // Complete the service call:
279
- serviceCall.finishedOn = Date.now();
280
- serviceCall.executionTime = serviceCall.finishedOn - serviceCall.createdOn;
281
- serviceCall.isCompleted = true;
282
-
283
- let processor = this.#getProcessor( serviceCall.messageID );
284
- if ( processor ) {
285
- this.#removeProcessor( serviceCall.messageID );
286
- processor.complete( serviceCall );
287
- } else {
288
- logger.log( `Received service call message with ID '${ serviceCall.messageID }' without registered processor! This may be caused by a service call timeout.`, logger.logSeverity.DEBUG, serviceCall.exception || undefined );
289
- }
290
-
291
- return serviceCall;
292
- }
293
-
294
- /**
295
- * Needs to be invoked by the connection handler when the connection is disrupted.
296
- *
297
- * @method
298
- * @param {string} identifier The identifier of the observed connection.
299
- * @override
300
- * @public
301
- */
302
- onConnectionDisrupted( identifier ) {
303
- super.onConnectionDisrupted( identifier );
304
- }
305
-
306
- /**
307
- * Needs to be invoked by the connection handler when the connection is recovered.
308
- *
309
- * @method
310
- * @param {string} identifier The identifier of the observed connection.
311
- * @override
312
- * @public
313
- */
314
- onConnectionRecovered( identifier ) {
315
- super.onConnectionRecovered( identifier );
316
- }
317
-
318
- /**
319
- * Needs to be invoked by the connection handler when the connection is irrevocably lost.
320
- *
321
- * @method
322
- * @param {string} identifier The identifier of the observed connection.
323
- * @override
324
- * @public
325
- */
326
- onConnectionLost( identifier ) {
327
- super.onConnectionLost( identifier );
328
- }
329
-
330
- /* Private interface */
331
-
332
- /**
333
- * Used to add a new task handler to the list of current tasks.
334
- *
335
- * @method
336
- * @param {string} messageID
337
- * @param {ServiceCallProcessor} processor
338
- * @private
339
- */
340
- #addProcessor( messageID, processor ) {
341
- this.#serviceCallProcessors[ messageID ] = processor;
342
- }
343
-
344
- /**
345
- * Used to fetch a task handler from the list of current tasks.
346
- *
347
- * @method
348
- * @param {string} messageID
349
- * @returns {ServiceCallProcessor}
350
- * @private
351
- */
352
- #getProcessor( messageID ) {
353
- return this.#serviceCallProcessors[ messageID ];
354
- }
355
-
356
- /**
357
- * Used to remove a task handler from the list of current tasks.
358
- *
359
- * @method
360
- * @param {string} messageID
361
- * @private
362
- */
363
- #removeProcessor( messageID ) {
364
- if ( this.#serviceCallProcessors[ messageID ] ) {
365
- delete this.#serviceCallProcessors[ messageID ];
366
- }
367
- }
368
-
369
- }
370
-
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 MessageObserver = require( "#message-observer" );
10
+ const tools = require( "#tools" );
11
+ const exceptions = require( "#exceptions" );
12
+ const logger = require( "#logger" );
13
+ const config = require( "#config" );
14
+ const cache = require( "#cache" );
15
+ const messageDispatcher = require( "#message-dispatcher" );
16
+
17
+ /**
18
+ * Used to assemble and prepare a new {@link ServiceCall} object.
19
+ *
20
+ * @method
21
+ * @param {string} messageID The message ID of the service call. This has to be unique across the whole service call tree, including the current service call, and will be used to identify the service call in the service call tree.
22
+ * @param {ServiceAddress} serviceAddress The service address has to define a valid service domain name, service alias, and optionally a service version.
23
+ * @param {Object} serviceParams Set of named parameters to provide to the called service.
24
+ * @param {ServiceExecContext} serviceExecContext The context in which the service call is being executed.
25
+ * @returns {Promise<ServiceCall>}
26
+ * @private
27
+ */
28
+ let prepareServiceCall = ( messageID, serviceAddress, serviceParams, serviceExecContext ) => {
29
+ return new Promise( ( resolve ) => {
30
+ const ServiceInstance = require( "#service-instance" );
31
+
32
+ // assemble the new service call:
33
+ let chainID = ( serviceExecContext.previousServiceCall ) ? serviceExecContext.previousServiceCall.chainID : tools.getUUID();
34
+ let chainLevel = ( serviceExecContext.previousServiceCall ) ? serviceExecContext.previousServiceCall.chainLevel + 1 : 0;
35
+ let source = {
36
+ instanceID: ServiceInstance.instanceID,
37
+ route: ServiceInstance.serviceDomainName
38
+ };
39
+ let destination = {
40
+ instanceID: undefined,
41
+ route: serviceAddress.serviceDomainName
42
+ };
43
+ /** @type ServiceCall */
44
+ let serviceCall = {
45
+ authToken: serviceExecContext.authToken,
46
+ chainID: chainID,
47
+ chainLevel: chainLevel,
48
+ createdOn: Date.now(),
49
+ destination: destination,
50
+ executionTime: 0,
51
+ exception: undefined,
52
+ finishedOn: undefined,
53
+ isCompleted: false,
54
+ isSuccessful: undefined,
55
+ messageID: messageID,
56
+ payload: undefined,
57
+ predecessor: ( serviceExecContext.previousServiceCall ) ? serviceExecContext.previousServiceCall.messageID : undefined,
58
+ serviceAddress: serviceAddress,
59
+ serviceParams: serviceParams,
60
+ source: source,
61
+ successors: undefined
62
+ };
63
+
64
+ resolve( serviceCall );
65
+ } );
66
+ }
67
+
68
+ /**
69
+ * Used to verify if the service is registered in the service registry.
70
+ *
71
+ * @method
72
+ * @param {ServiceAddress} serviceAddress
73
+ * @returns {Promise}
74
+ * @private
75
+ */
76
+ let findServiceInRegistry = ( serviceAddress ) => {
77
+ return new Promise( ( resolve, reject ) => {
78
+ let serviceCatalog = config.getSetting( config.setting.SERVICE_REGISTRY_ADDRESS ) + serviceAddress.serviceDomainName;
79
+ cache.instance.isSetMember( serviceCatalog, serviceAddress.serviceAlias ).then( ( result ) => {
80
+ if ( result === true ) {
81
+ resolve();
82
+ } else {
83
+ reject( exceptions.raise( exceptions.exceptionCode.E_COM_SERVICE_NOT_REGISTERED ) );
84
+ }
85
+ } ).catch( ( error ) => {
86
+ reject( exceptions.raise( error ) );
87
+ } );
88
+ } );
89
+ }
90
+
91
+ /**
92
+ * A class defining a service call processor.
93
+ *
94
+ * @class ServiceCallProcessor
95
+ * @private
96
+ */
97
+ class ServiceCallProcessor {
98
+
99
+ #messageID;
100
+ #serviceAddress;
101
+ #serviceParams;
102
+ #serviceExecContext;
103
+ #timeoutHandle;
104
+ #taskCompletionHandler;
105
+ #isProcessed = false;
106
+
107
+ /**
108
+ * @constructor
109
+ * @param {ServiceAddress} serviceAddress The service address has to define a valid service domain name, service alias, and optionally a service version.
110
+ * @param {Object} serviceParams Set of named parameters to provide to the called service.
111
+ * @param {ServiceExecContext} serviceExecContext The context in which the service call is being executed.
112
+ * @returns {ServiceCallProcessor}
113
+ */
114
+ constructor( serviceAddress, serviceParams, serviceExecContext ) {
115
+ this.#messageID = tools.getUUID();
116
+ this.#serviceAddress = serviceAddress;
117
+ this.#serviceParams = serviceParams;
118
+ this.#serviceExecContext = serviceExecContext;
119
+ }
120
+
121
+ /* Public interface */
122
+
123
+ /**
124
+ * The unique identifier of the service call.
125
+ *
126
+ * @property
127
+ * @returns {string}
128
+ * @public
129
+ */
130
+ get messageID() {
131
+ return this.#messageID;
132
+ }
133
+
134
+ /**
135
+ * Used to start the execution of the service call.
136
+ * <br/>
137
+ * NOTE: This method will time out after specific preconfigured time, in which case it will resolve with {@link E_COM_SERVICE_EXEC_TIMEOUT} error.
138
+ *
139
+ * @method
140
+ * @returns {Promise<ServiceCallResult>}
141
+ * @public
142
+ */
143
+ process() {
144
+ return Promise.race( [ this.#execute(), this.#timeout() ] ).then( ( result ) => {
145
+ clearTimeout( this.#timeoutHandle );
146
+ this.#isProcessed = true;
147
+ return result;
148
+ } ).catch( ( error ) => {
149
+ clearTimeout( this.#timeoutHandle );
150
+ this.#isProcessed = true;
151
+ logger.log( `Error during service call execution!`, logger.logSeverity.DEBUG, error );
152
+ return {
153
+ isSuccessful: false,
154
+ exception: exceptions.raise( error ),
155
+ payload: undefined
156
+ };
157
+ } );
158
+ }
159
+
160
+ /**
161
+ * Used to complete the execution of the service call that was started within the {@link process} method.
162
+ *
163
+ * @method
164
+ * @param {ServiceCall} serviceCall
165
+ * @public
166
+ */
167
+ complete( serviceCall ) {
168
+ if ( this.#isProcessed !== true ) {
169
+ let serviceCallResult = {
170
+ exception: serviceCall.exception,
171
+ isSuccessful: ( serviceCall.isSuccessful !== undefined ) ? tools.toBool( serviceCall.isSuccessful ) : true,
172
+ payload: serviceCall.payload
173
+ };
174
+ this.#taskCompletionHandler( serviceCallResult );
175
+ }
176
+ }
177
+
178
+ /* Private interface */
179
+
180
+ /**
181
+ * Used to execute the service call.
182
+ *
183
+ * @method
184
+ * @returns {Promise<ServiceCallResult>}
185
+ * @private
186
+ */
187
+ #execute() {
188
+ return new Promise( ( resolve, reject ) => {
189
+ findServiceInRegistry( this.#serviceAddress ).then( () => {
190
+ return prepareServiceCall( this.#messageID, this.#serviceAddress, this.#serviceParams, this.#serviceExecContext );
191
+ } ).then( ( serviceCall ) => {
192
+ return messageDispatcher.instance.sendRequest( serviceCall );
193
+ } ).then( () => {
194
+ this.#taskCompletionHandler = ( serviceCallResult ) => {
195
+ resolve( serviceCallResult );
196
+ };
197
+ } ).catch( ( error ) => {
198
+ if ( this.#isProcessed !== true ) {
199
+ reject( exceptions.raise( error ) );
200
+ }
201
+ } );
202
+ } );
203
+ }
204
+
205
+ /**
206
+ * Used to time out the service call execution.
207
+ *
208
+ * @method
209
+ * @returns {Promise<ServiceCallResult>}
210
+ * @private
211
+ */
212
+ #timeout() {
213
+ return new Promise( ( resolve, reject ) => {
214
+ this.#timeoutHandle = setTimeout( () => {
215
+ if ( this.#isProcessed !== true ) {
216
+ reject( exceptions.raise( exceptions.exceptionCode.E_COM_SERVICE_EXEC_TIMEOUT ) );
217
+ }
218
+ }, config.getSetting( config.setting.SERVICE_EXECUTION_TIMEOUT ) );
219
+ } );
220
+ }
221
+
222
+ }
223
+
224
+ /**
225
+ * A class defining a service caller behavior.
226
+ *
227
+ * @class ServiceCaller
228
+ * @extends MessageObserver
229
+ * @public
230
+ */
231
+ class ServiceCaller extends MessageObserver {
232
+
233
+ #serviceCallProcessors = {};
234
+
235
+ /**
236
+ * @constructor
237
+ */
238
+ constructor() {
239
+ super( 10 );
240
+ }
241
+
242
+ /* Public interface */
243
+
244
+ /**
245
+ * Used to call a service in the service ecosystem asynchronously.
246
+ * <br/>
247
+ * NOTE: This method will time out after specific preconfigured time, in which case it will resolve with {@link E_COM_SERVICE_EXEC_TIMEOUT} error.
248
+ *
249
+ * @method
250
+ * @param {ServiceAddress} serviceAddress The service address has to define a valid service domain name, service alias, and optionally a service version.
251
+ * @param {Object} serviceParams Set of named parameters to provide to the called service.
252
+ * @param {ServiceExecContext} serviceExecContext The context in which the service call is being executed.
253
+ * @returns {Promise<ServiceCallResult>} Will always return a service call result that can be either successful or not.
254
+ * @public
255
+ */
256
+ executeServiceCall( serviceAddress, serviceParams, serviceExecContext ) {
257
+ return new Promise( ( resolve ) => {
258
+ let processor = new ServiceCallProcessor( serviceAddress, serviceParams, serviceExecContext );
259
+ this.#addProcessor( processor.messageID, processor );
260
+ processor.process().then( ( serviceCallResult ) => {
261
+ this.#removeProcessor( processor.messageID );
262
+ resolve( serviceCallResult );
263
+ } );
264
+ } );
265
+ }
266
+
267
+ /**
268
+ * Once the proper message is received this method will trigger the completion of the pending {@link ServiceCall} execution started in {@link #executeServiceCall}.
269
+ *
270
+ * @method
271
+ * @param {string} identifier The identifier of the observed connection.
272
+ * @param {ServiceCall} serviceCall The service call message for processing.
273
+ * @returns {ServiceCall} The service call message that was received.
274
+ * @override
275
+ * @public
276
+ */
277
+ onMessage( identifier, serviceCall ) {
278
+ // Complete the service call:
279
+ serviceCall.finishedOn = Date.now();
280
+ serviceCall.executionTime = serviceCall.finishedOn - serviceCall.createdOn;
281
+ serviceCall.isCompleted = true;
282
+
283
+ let processor = this.#getProcessor( serviceCall.messageID );
284
+ if ( processor ) {
285
+ this.#removeProcessor( serviceCall.messageID );
286
+ processor.complete( serviceCall );
287
+ } else {
288
+ logger.log( `Received service call message with ID '${ serviceCall.messageID }' without registered processor! This may be caused by a service call timeout.`, logger.logSeverity.DEBUG, serviceCall.exception || undefined );
289
+ }
290
+
291
+ return serviceCall;
292
+ }
293
+
294
+ /**
295
+ * Needs to be invoked by the connection handler when the connection is disrupted.
296
+ *
297
+ * @method
298
+ * @param {string} identifier The identifier of the observed connection.
299
+ * @override
300
+ * @public
301
+ */
302
+ onConnectionDisrupted( identifier ) {
303
+ super.onConnectionDisrupted( identifier );
304
+ }
305
+
306
+ /**
307
+ * Needs to be invoked by the connection handler when the connection is recovered.
308
+ *
309
+ * @method
310
+ * @param {string} identifier The identifier of the observed connection.
311
+ * @override
312
+ * @public
313
+ */
314
+ onConnectionRecovered( identifier ) {
315
+ super.onConnectionRecovered( identifier );
316
+ }
317
+
318
+ /**
319
+ * Needs to be invoked by the connection handler when the connection is irrevocably lost.
320
+ *
321
+ * @method
322
+ * @param {string} identifier The identifier of the observed connection.
323
+ * @override
324
+ * @public
325
+ */
326
+ onConnectionLost( identifier ) {
327
+ super.onConnectionLost( identifier );
328
+ }
329
+
330
+ /* Private interface */
331
+
332
+ /**
333
+ * Used to add a new task handler to the list of current tasks.
334
+ *
335
+ * @method
336
+ * @param {string} messageID
337
+ * @param {ServiceCallProcessor} processor
338
+ * @private
339
+ */
340
+ #addProcessor( messageID, processor ) {
341
+ this.#serviceCallProcessors[ messageID ] = processor;
342
+ }
343
+
344
+ /**
345
+ * Used to fetch a task handler from the list of current tasks.
346
+ *
347
+ * @method
348
+ * @param {string} messageID
349
+ * @returns {ServiceCallProcessor}
350
+ * @private
351
+ */
352
+ #getProcessor( messageID ) {
353
+ return this.#serviceCallProcessors[ messageID ];
354
+ }
355
+
356
+ /**
357
+ * Used to remove a task handler from the list of current tasks.
358
+ *
359
+ * @method
360
+ * @param {string} messageID
361
+ * @private
362
+ */
363
+ #removeProcessor( messageID ) {
364
+ if ( this.#serviceCallProcessors[ messageID ] ) {
365
+ delete this.#serviceCallProcessors[ messageID ];
366
+ }
367
+ }
368
+
369
+ }
370
+
371
371
  module.exports = ServiceCaller;