@ti-engine/core 1.0.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/components/auditing.js +163 -0
- package/components/connection-observer.js +53 -0
- package/components/exchange/default/default-message-exchange.js +133 -0
- package/components/exchange/default/default-message-receiver.js +89 -0
- package/components/exchange/default/default-message-sender.js +90 -0
- package/components/exchange/message-dispatcher.js +162 -0
- package/components/exchange/message-exchange.js +418 -0
- package/components/exchange/message-handler.js +173 -0
- package/components/exchange/message-memory-cache.js +150 -0
- package/components/exchange/message-observer.js +76 -0
- package/components/exchange/message-receiver.js +113 -0
- package/components/exchange/message-sender.js +133 -0
- package/components/exchange/message-tracer.js +146 -0
- package/components/service-caller.js +306 -0
- package/components/service-consumer.js +112 -0
- package/components/service-executor.js +231 -0
- package/components/service-instance.js +280 -0
- package/components/service-provider.js +221 -0
- package/integrations/gcloud-integration.js +61 -0
- package/integrations/redis-integration.js +261 -0
- package/package.json +54 -0
- package/settings.json +33 -0
- package/utils/cache.js +507 -0
- package/utils/config.js +146 -0
- package/utils/exceptions.js +241 -0
- package/utils/logger.js +69 -0
- package/utils/tools.js +537 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
3
|
+
* SPDX-License-Identifier: ICU
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const _ = require( "lodash" );
|
|
7
|
+
const schedule = require( "node-schedule" );
|
|
8
|
+
const tools = require( "#tools" );
|
|
9
|
+
const config = require( "#config" );
|
|
10
|
+
const logger = require( "#logger" );
|
|
11
|
+
const exceptions = require( "#exceptions" );
|
|
12
|
+
const cache = require( "#cache" );
|
|
13
|
+
const messageDispatcher = require( "#message-dispatcher" );
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Abstract class used to define a Service Instance behavior.
|
|
17
|
+
* <br/>
|
|
18
|
+
* NOTE: Inherit this to create an a module that can be started as a microservice instance.
|
|
19
|
+
* <br/>
|
|
20
|
+
* NOTE: This class does not
|
|
21
|
+
*
|
|
22
|
+
* @class ServiceInstance
|
|
23
|
+
* @abstract
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
class ServiceInstance {
|
|
27
|
+
|
|
28
|
+
static #instanceID;
|
|
29
|
+
static #serviceDomainName;
|
|
30
|
+
#serviceConfig;
|
|
31
|
+
#serviceHealthCheck;
|
|
32
|
+
#reportHealthyJob;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @constructor
|
|
36
|
+
* @param {string} serviceDomainName The service domain name for this service instance.
|
|
37
|
+
* @param {Object} [serviceConfig={}] The JSON configuration for this service.
|
|
38
|
+
*/
|
|
39
|
+
constructor( serviceDomainName, serviceConfig = {} ) {
|
|
40
|
+
// make sure this abstract class cannot be instantiated:
|
|
41
|
+
if ( new.target === ServiceInstance ) {
|
|
42
|
+
throw exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_CLASS_INIT, { name: this.constructor.name } );
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
ServiceInstance.#instanceID = process.env.TI_INSTANCE_ID || tools.getUUID();
|
|
46
|
+
ServiceInstance.#serviceDomainName = serviceDomainName;
|
|
47
|
+
this.#serviceConfig = ( _.isObjectLike( serviceConfig ) ) ? serviceConfig : {};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Public interface */
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Property returning the current service instance ID.
|
|
54
|
+
*
|
|
55
|
+
* @property
|
|
56
|
+
* @returns {string}
|
|
57
|
+
* @public
|
|
58
|
+
*/
|
|
59
|
+
static get instanceID() { return ServiceInstance.#instanceID; }
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Property returning the current service domain name.
|
|
63
|
+
*
|
|
64
|
+
* @property
|
|
65
|
+
* @returns {string}
|
|
66
|
+
* @public
|
|
67
|
+
*/
|
|
68
|
+
static get serviceDomainName() { return ServiceInstance.#serviceDomainName; }
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Property to indicate that this and every child class is a {@link ServiceInstance}.
|
|
72
|
+
*
|
|
73
|
+
* @property
|
|
74
|
+
* @returns {boolean}
|
|
75
|
+
* @public
|
|
76
|
+
*/
|
|
77
|
+
get isServiceInstance() { return true; }
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Property returning the service configuration JSON.
|
|
81
|
+
*
|
|
82
|
+
* @property
|
|
83
|
+
* @returns {Object}
|
|
84
|
+
* @public
|
|
85
|
+
*/
|
|
86
|
+
get serviceConfig() { return this.#serviceConfig; }
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Initializes the instance.
|
|
90
|
+
*
|
|
91
|
+
* @method
|
|
92
|
+
* @returns {Promise}
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
95
|
+
start() {
|
|
96
|
+
return new Promise( ( resolve, reject ) => {
|
|
97
|
+
if ( !ServiceInstance.#serviceDomainName ) {
|
|
98
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_INVALID_SERVICE_DOMAIN_NAME ) );
|
|
99
|
+
} else {
|
|
100
|
+
this.#preStart().then( () => {
|
|
101
|
+
return this.onStart();
|
|
102
|
+
} ).then( () => {
|
|
103
|
+
return this.#postStart();
|
|
104
|
+
} ).then( () => {
|
|
105
|
+
resolve();
|
|
106
|
+
} ).catch( ( error ) => {
|
|
107
|
+
reject( exceptions.raise( error ) );
|
|
108
|
+
} );
|
|
109
|
+
}
|
|
110
|
+
} );
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Executes custom logic on instance start.
|
|
115
|
+
* <br/>
|
|
116
|
+
* NOTE: This method will be invoked automatically.
|
|
117
|
+
* <br/>
|
|
118
|
+
* NOTE: If you need to add more onStart logic you can override this method but make sure to call it in the
|
|
119
|
+
* overriding method using: super.onStart()
|
|
120
|
+
*
|
|
121
|
+
* @method
|
|
122
|
+
* @returns {Promise}
|
|
123
|
+
* @virtual
|
|
124
|
+
* @public
|
|
125
|
+
*/
|
|
126
|
+
onStart() {
|
|
127
|
+
return new Promise( ( resolve, reject ) => {
|
|
128
|
+
const DefaultMessageExchange = require( "#default-message-exchange" );
|
|
129
|
+
const ServiceProvider = require( "#service-provider" );
|
|
130
|
+
const ServiceConsumer = require( "#service-consumer" );
|
|
131
|
+
|
|
132
|
+
let configureInbound = ( this instanceof ServiceProvider );
|
|
133
|
+
let configureOutbound = ( this instanceof ServiceConsumer );
|
|
134
|
+
|
|
135
|
+
messageDispatcher.initialize( new DefaultMessageExchange( ServiceInstance.instanceID, ServiceInstance.serviceDomainName ), configureInbound, configureOutbound ).then( () => {
|
|
136
|
+
resolve();
|
|
137
|
+
} ).catch( ( error ) => {
|
|
138
|
+
reject( exceptions.raise( error ) );
|
|
139
|
+
} );
|
|
140
|
+
} );
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Shuts down the instance.
|
|
145
|
+
*
|
|
146
|
+
* @method
|
|
147
|
+
* @returns {Promise}
|
|
148
|
+
* @public
|
|
149
|
+
*/
|
|
150
|
+
stop() {
|
|
151
|
+
return new Promise( ( resolve, reject ) => {
|
|
152
|
+
this.#preStop().then( () => {
|
|
153
|
+
return this.onStop();
|
|
154
|
+
} ).then( () => {
|
|
155
|
+
return this.#postStop();
|
|
156
|
+
} ).then( () => {
|
|
157
|
+
resolve();
|
|
158
|
+
} ).catch( ( error ) => {
|
|
159
|
+
reject( exceptions.raise( error ) );
|
|
160
|
+
} );
|
|
161
|
+
} );
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Executes custom logic on instance stop.
|
|
166
|
+
* <br/>
|
|
167
|
+
* NOTE: This method will be invoked automatically.
|
|
168
|
+
* <br/>
|
|
169
|
+
* NOTE: If you need to add more onStop logic you can override this method but make sure to call it in the
|
|
170
|
+
* overriding method using: super.onStop()
|
|
171
|
+
*
|
|
172
|
+
* @method
|
|
173
|
+
* @returns {Promise}
|
|
174
|
+
* @virtual
|
|
175
|
+
* @public
|
|
176
|
+
*/
|
|
177
|
+
onStop() {
|
|
178
|
+
return new Promise( ( resolve, reject ) => {
|
|
179
|
+
messageDispatcher.shutDown().then( () => {
|
|
180
|
+
resolve();
|
|
181
|
+
} ).catch( ( error ) => {
|
|
182
|
+
reject( exceptions.raise( error ) );
|
|
183
|
+
} );
|
|
184
|
+
} );
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* Private interface */
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Used to run internal pre-start logic.
|
|
191
|
+
* <br/>
|
|
192
|
+
* NOTE: This will be executed before any user's custom logic in {@link ServiceInstance.onStart}.
|
|
193
|
+
*
|
|
194
|
+
* @method
|
|
195
|
+
* @returns {Promise}
|
|
196
|
+
* @private
|
|
197
|
+
*/
|
|
198
|
+
#preStart() {
|
|
199
|
+
return new Promise( ( resolve, reject ) => {
|
|
200
|
+
this.#serviceHealthCheck = config.getSetting( config.setting.SERVICE_HEALTH_CHECK_ADDRESS ) + process.env.TI_INSTANCE_NAME + ":" + ServiceInstance.instanceID;
|
|
201
|
+
resolve();
|
|
202
|
+
} );
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Used to run internal post-start logic.
|
|
207
|
+
* <br/>
|
|
208
|
+
* NOTE: This will be executed only after the user's custom logic in {@link ServiceInstance.onStart} has been successfully executed.
|
|
209
|
+
*
|
|
210
|
+
* @method
|
|
211
|
+
* @returns {Promise}
|
|
212
|
+
* @private
|
|
213
|
+
*/
|
|
214
|
+
#postStart() {
|
|
215
|
+
return new Promise( ( resolve, reject ) => {
|
|
216
|
+
// schedule regular health check:
|
|
217
|
+
this.#reportHealthyJob = schedule.scheduleJob( config.getSetting( config.setting.SERVICE_HEALTH_CHECK_INTERVAL ), () => {
|
|
218
|
+
this.#reportHealthy();
|
|
219
|
+
} );
|
|
220
|
+
|
|
221
|
+
logger.log( `Instance '${ ServiceInstance.instanceID }' started successfully.`, logger.logSeverity.NOTICE, {
|
|
222
|
+
nodeVersion: process.version
|
|
223
|
+
} );
|
|
224
|
+
|
|
225
|
+
resolve();
|
|
226
|
+
} );
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Used to run internal pre-start logic.
|
|
231
|
+
* <br/>
|
|
232
|
+
* NOTE: This will be executed before any user's custom logic in {@link ServiceInstance.onStop}.
|
|
233
|
+
*
|
|
234
|
+
* @method
|
|
235
|
+
* @returns {Promise}
|
|
236
|
+
* @private
|
|
237
|
+
*/
|
|
238
|
+
#preStop() {
|
|
239
|
+
return new Promise( ( resolve, reject ) => {
|
|
240
|
+
if ( this.#reportHealthyJob ) {
|
|
241
|
+
this.#reportHealthyJob.cancel();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
resolve();
|
|
245
|
+
} );
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Used to run internal post-stop logic.
|
|
250
|
+
* <br/>
|
|
251
|
+
* NOTE: This will be executed only after the user's custom logic in {@link ServiceInstance.onStop} has been successfully executed.
|
|
252
|
+
*
|
|
253
|
+
* @method
|
|
254
|
+
* @returns {Promise}
|
|
255
|
+
* @private
|
|
256
|
+
*/
|
|
257
|
+
#postStop() {
|
|
258
|
+
return new Promise( ( resolve, reject ) => {
|
|
259
|
+
logger.log( `Instance '${ ServiceInstance.instanceID }' shut down successfully.`, logger.logSeverity.NOTICE );
|
|
260
|
+
|
|
261
|
+
resolve();
|
|
262
|
+
} );
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Scheduled job used to report for service instance health checks.
|
|
267
|
+
*
|
|
268
|
+
* @method
|
|
269
|
+
* @private
|
|
270
|
+
*/
|
|
271
|
+
#reportHealthy() {
|
|
272
|
+
let timestamp = new Date();
|
|
273
|
+
cache.setValue( this.#serviceHealthCheck, timestamp.toISOString(), config.getSetting( config.setting.SERVICE_HEALTH_CHECK_TIMEOUT ) ).catch( ( error ) => {
|
|
274
|
+
logger.log( `Error while trying to report for health check from '${ ServiceInstance.instanceID }'!`, logger.logSeverity.WARNING, error );
|
|
275
|
+
} );
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
module.exports = ServiceInstance;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
3
|
+
* SPDX-License-Identifier: ICU
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const ServiceConsumer = require( "#service-consumer" );
|
|
7
|
+
const _ = require( "lodash" );
|
|
8
|
+
const fs = require( "fs-extra" );
|
|
9
|
+
const exceptions = require( "#exceptions" );
|
|
10
|
+
const logger = require( "#logger" );
|
|
11
|
+
const messageDispatcher = require( "#message-dispatcher" );
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Abstract class used to define a Service Provider behavior.
|
|
15
|
+
* <br/>
|
|
16
|
+
* NOTE: Inherit this to create an a module that can be started as a microservice provider instance.
|
|
17
|
+
* <br/>
|
|
18
|
+
* NOTE: A service provider is a microservice that offers an API of named business services that can be invoked by other
|
|
19
|
+
* microservices using {@link ServiceCall} objects. The provider will take care of the actual execution of that service and
|
|
20
|
+
* therefore acts as a "black box". The only necessary items are the service address and optional inbound parameters to be
|
|
21
|
+
* used in that service's logic. The result of the service's execution will be bundled in an {@link ServiceCallResult}
|
|
22
|
+
* object and returned to the caller.
|
|
23
|
+
*
|
|
24
|
+
* @class ServiceProvider
|
|
25
|
+
* @extends ServiceConsumer
|
|
26
|
+
* @abstract
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
class ServiceProvider extends ServiceConsumer {
|
|
30
|
+
|
|
31
|
+
#serviceExecutor;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @constructor
|
|
35
|
+
* @param {string} serviceDomainName The service domain name for this service instance.
|
|
36
|
+
* @param {Object} [serviceConfig={}] The JSON configuration for this service.
|
|
37
|
+
*/
|
|
38
|
+
constructor( serviceDomainName, serviceConfig = {} ) {
|
|
39
|
+
super( serviceDomainName, serviceConfig );
|
|
40
|
+
|
|
41
|
+
// make sure this abstract class cannot be instantiated:
|
|
42
|
+
if ( new.target === ServiceProvider ) {
|
|
43
|
+
throw exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_CLASS_INIT, { name: this.constructor.name } );
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Public interface */
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Perform initialization tasks when the service provider starts.
|
|
51
|
+
* <br/>
|
|
52
|
+
* NOTE: This method will be invoked automatically.
|
|
53
|
+
* <br/>
|
|
54
|
+
* NOTE: If you need to add more onStart logic you can override this method but make sure to call it in the
|
|
55
|
+
* overriding method using: super.onStart()
|
|
56
|
+
*
|
|
57
|
+
* @method
|
|
58
|
+
* @returns {Promise}
|
|
59
|
+
* @override
|
|
60
|
+
* @public
|
|
61
|
+
*/
|
|
62
|
+
onStart() {
|
|
63
|
+
return new Promise( ( resolve, reject ) => {
|
|
64
|
+
const ServiceExecutor = require( "#service-executor" );
|
|
65
|
+
|
|
66
|
+
this.#serviceExecutor = new ServiceExecutor();
|
|
67
|
+
this.#serviceExecutor.configureVerifyAccess( this.verifyAccess );
|
|
68
|
+
|
|
69
|
+
super.onStart().then( () => {
|
|
70
|
+
let serviceDefinitions = this.serviceConfig.services;
|
|
71
|
+
return this.registerServices( serviceDefinitions );
|
|
72
|
+
} ).then( () => {
|
|
73
|
+
messageDispatcher.addMessageObserverRequestsIn( this.#serviceExecutor );
|
|
74
|
+
resolve();
|
|
75
|
+
} ).catch( ( error ) => {
|
|
76
|
+
reject( exceptions.raise( error ) );
|
|
77
|
+
} );
|
|
78
|
+
} );
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Perform shut down and cleanup tasks when the service provider stops.
|
|
83
|
+
* <br/>
|
|
84
|
+
* NOTE: This method will be invoked automatically.
|
|
85
|
+
* <br/>
|
|
86
|
+
* NOTE: If you need to add more onStop logic you can override this method but make sure to call it in the
|
|
87
|
+
* overriding method using: super.onStop()
|
|
88
|
+
*
|
|
89
|
+
* @method
|
|
90
|
+
* @returns {Promise}
|
|
91
|
+
* @override
|
|
92
|
+
* @public
|
|
93
|
+
*/
|
|
94
|
+
onStop() {
|
|
95
|
+
return new Promise( ( resolve, reject ) => {
|
|
96
|
+
super.onStop().then( () => {
|
|
97
|
+
resolve();
|
|
98
|
+
} ).catch( ( error ) => {
|
|
99
|
+
reject( exceptions.raise( error ) );
|
|
100
|
+
} );
|
|
101
|
+
} );
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Used to verify whether the service caller has authorization to access the service.
|
|
106
|
+
* <br/>
|
|
107
|
+
* NOTE: Override this to implement authorization check. By default this method simply returns.
|
|
108
|
+
*
|
|
109
|
+
* @method
|
|
110
|
+
* @param {string} authToken
|
|
111
|
+
* @param {ServiceAddress} serviceAddress
|
|
112
|
+
* @return {Promise}
|
|
113
|
+
* @virtual
|
|
114
|
+
* @public
|
|
115
|
+
*/
|
|
116
|
+
verifyAccess( authToken, serviceAddress ) {
|
|
117
|
+
return Promise.resolve();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Used to register a single service to the service provider's API. One service can have multiple versions accessible at the same time.
|
|
122
|
+
* <br/>
|
|
123
|
+
* NOTE: This will actually bind the serviceDefinition as first parameter of the service handler function. When creating default service handlers,
|
|
124
|
+
* keep in mind that your first param must always be the 'serviceDefinition' and the second one will be the general 'serviceParams' object.
|
|
125
|
+
* <br/>
|
|
126
|
+
* NOTE: Additionally, if you intend to call another service inside the service handler, then you have to use normal function for the handler and not
|
|
127
|
+
* 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.
|
|
128
|
+
*
|
|
129
|
+
* @method
|
|
130
|
+
* @param {ServiceDefinition} serviceDefinition Full service definition object.
|
|
131
|
+
* @param {ServiceHandler} [defaultServiceHandler=undefined] A default service handler in case there is one.
|
|
132
|
+
* @return {Promise}
|
|
133
|
+
* @public
|
|
134
|
+
*/
|
|
135
|
+
registerService( serviceDefinition, defaultServiceHandler = undefined ) {
|
|
136
|
+
return new Promise( ( resolve, reject ) => {
|
|
137
|
+
/** @type {ServiceHandler} */
|
|
138
|
+
let serviceHandler = null;
|
|
139
|
+
if ( serviceDefinition.serviceFile ) {
|
|
140
|
+
let filePath = process.cwd() + serviceDefinition.serviceFile + ( ( _.endsWith( serviceDefinition.serviceFile, ".js" ) ) ? "" : ".js" );
|
|
141
|
+
if ( fs.existsSync( filePath ) ) {
|
|
142
|
+
// try to load the service handler dynamically from the file:
|
|
143
|
+
try {
|
|
144
|
+
serviceHandler = require( filePath ).service;
|
|
145
|
+
} catch ( error ) {
|
|
146
|
+
logger.log( `Specified service handler file '${ serviceDefinition.serviceFile }' could not be loaded!`, logger.logSeverity.ERROR, error );
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
logger.log( `Specified service handler file '${ serviceDefinition.serviceFile }' was not found in the specified location!`, logger.logSeverity.WARNING );
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
if ( typeof ( defaultServiceHandler ) === "function" ) {
|
|
153
|
+
serviceHandler = defaultServiceHandler;
|
|
154
|
+
} else {
|
|
155
|
+
logger.log( "A service cannot be registered without provided default service handler at the very least!", logger.logSeverity.WARNING, serviceDefinition );
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// if we have a valid service handler proceed with the registration:
|
|
160
|
+
if ( typeof ( serviceHandler ) === "function" ) {
|
|
161
|
+
// make sure we have a version and parent service provider specified:
|
|
162
|
+
serviceDefinition.serviceVersion = serviceDefinition.serviceVersion || 1;
|
|
163
|
+
this.#serviceExecutor.addServiceHandler( serviceHandler, serviceDefinition );
|
|
164
|
+
resolve();
|
|
165
|
+
} else {
|
|
166
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_BAD_SERVICE_HANDLER ) );
|
|
167
|
+
}
|
|
168
|
+
} );
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Used to register multiple services from the provided service definitions.
|
|
173
|
+
*
|
|
174
|
+
* @method
|
|
175
|
+
* @param {ServiceDefinition[]} serviceDefinitions
|
|
176
|
+
* @param {ServiceHandler} [defaultServiceHandler=undefined]
|
|
177
|
+
* @return {Promise}
|
|
178
|
+
* @public
|
|
179
|
+
*/
|
|
180
|
+
registerServices( serviceDefinitions, defaultServiceHandler = undefined ) {
|
|
181
|
+
return new Promise( ( resolve, reject ) => {
|
|
182
|
+
if ( serviceDefinitions ) {
|
|
183
|
+
logger.log( `Starting service registration process. There is ${ ( ( defaultServiceHandler ) ? "" : "NO" ) } default service handler provided.`, logger.logSeverity.INFO );
|
|
184
|
+
|
|
185
|
+
let promises = [];
|
|
186
|
+
_.forEach( serviceDefinitions, ( serviceDefinition ) => {
|
|
187
|
+
// NOTE: we are not going to interrupt the service interface loading if one of the services fails to load or is not found!
|
|
188
|
+
// If this happens, a corresponding log entry will be created but the loading process will continue. Therefore the following
|
|
189
|
+
// promise will always resolve (unless a programming error occurs in it of course).
|
|
190
|
+
let registrationPromise = ( serviceDefinition, defaultServiceHandler ) => {
|
|
191
|
+
return new Promise( ( resolve, reject ) => {
|
|
192
|
+
this.registerService( serviceDefinition, defaultServiceHandler ).then( () => {
|
|
193
|
+
resolve( true );
|
|
194
|
+
} ).catch( ( error ) => {
|
|
195
|
+
resolve( false );
|
|
196
|
+
} );
|
|
197
|
+
} );
|
|
198
|
+
};
|
|
199
|
+
promises.push( registrationPromise( serviceDefinition, defaultServiceHandler ) );
|
|
200
|
+
} );
|
|
201
|
+
|
|
202
|
+
Promise.all( promises ).then( ( result ) => {
|
|
203
|
+
let registrationResults = _.countBy( result, ( value ) => {
|
|
204
|
+
return value === true;
|
|
205
|
+
} );
|
|
206
|
+
logger.log( `Registration of defined services completed with ${ registrationResults[ "true" ] || 0 } successful out of ${ serviceDefinitions.length } total.`, logger.logSeverity.INFO );
|
|
207
|
+
|
|
208
|
+
resolve();
|
|
209
|
+
} ).catch( ( error ) => {
|
|
210
|
+
reject( exceptions.raise( error ) );
|
|
211
|
+
} );
|
|
212
|
+
} else {
|
|
213
|
+
logger.log( `Service registration process skipped as there are no service definitions provided.`, logger.logSeverity.NOTICE );
|
|
214
|
+
resolve();
|
|
215
|
+
}
|
|
216
|
+
} );
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
module.exports = ServiceProvider;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
3
|
+
* SPDX-License-Identifier: ICU
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const config = require( "#config" );
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Used to verify if the GCloud integration is enabled.
|
|
10
|
+
*
|
|
11
|
+
* @method
|
|
12
|
+
* @returns {boolean}
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
module.exports.isEnabled = () => {
|
|
16
|
+
return process.env.TI_GCLOUD_ENABLED === true;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Will be used to gracefully shut down the instance.
|
|
21
|
+
* <br/>
|
|
22
|
+
* NOTE: Will be overridden below after the instance creation.
|
|
23
|
+
*
|
|
24
|
+
* @method
|
|
25
|
+
* @param {Error} error
|
|
26
|
+
* @abstract
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
module.exports.reportError = ( error ) => {
|
|
30
|
+
console.error( "Attempting to report error to GCloud while integration to it is disabled!" );
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if ( process.env.TI_GCLOUD_ENABLED === true ) {
|
|
34
|
+
const { ErrorReporting } = require( "@google-cloud/error-reporting" );
|
|
35
|
+
|
|
36
|
+
const errorReporting = new ErrorReporting( {
|
|
37
|
+
projectId: config.getSetting( config.setting.GCLOUD_PROJECT_ID ),
|
|
38
|
+
key: config.getSetting( config.setting.GCLOUD_API_KEY ),
|
|
39
|
+
reportMode: "production",
|
|
40
|
+
logLevel: 2,
|
|
41
|
+
reportUnhandledRejections: true
|
|
42
|
+
} );
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Reports an error to the GCloud error reporting system.
|
|
46
|
+
*
|
|
47
|
+
* @method
|
|
48
|
+
* @param {Error} error
|
|
49
|
+
* @override
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
module.exports.reportError = ( error ) => {
|
|
53
|
+
errorReporting.report( {
|
|
54
|
+
eventTime: ( new Date() ).toISOString(),
|
|
55
|
+
message: error.stack,
|
|
56
|
+
serviceContext: {
|
|
57
|
+
service: process.env.TI_INSTANCE_ID
|
|
58
|
+
}
|
|
59
|
+
} );
|
|
60
|
+
};
|
|
61
|
+
}
|