@ti-engine/core 1.0.14 → 1.1.1
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 +36 -0
- package/README.md +216 -24
- package/bin/settings.json +3 -1
- package/bin/start-instance.js +1 -1
- package/components/auditing.js +1 -1
- package/components/connection-observer.js +1 -1
- package/components/exchange/default/default-message-exchange.js +1 -1
- package/components/exchange/default/default-message-receiver.js +1 -1
- package/components/exchange/default/default-message-sender.js +1 -1
- package/components/exchange/message-dispatcher.js +1 -1
- package/components/exchange/message-exchange.js +1 -1
- package/components/exchange/message-handler.js +1 -1
- package/components/exchange/message-memory-cache.js +1 -1
- package/components/exchange/message-observer.js +1 -1
- package/components/exchange/message-receiver.js +1 -1
- package/components/exchange/message-sender.js +1 -1
- package/components/exchange/message-tracer.js +45 -10
- package/components/service-caller.js +6 -6
- package/components/service-consumer.js +1 -1
- package/components/service-executor.js +20 -2
- package/components/service-instance.js +10 -4
- package/components/service-provider.js +17 -5
- package/integrations/gcloud-integration.js +1 -1
- package/integrations/redis-integration.js +97 -3
- package/package.json +3 -3
- package/utils/cache.js +126 -2
- package/utils/config.js +10 -3
- package/utils/exceptions.js +2 -1
- package/utils/logger.js +20 -6
- package/utils/tools.js +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
2
|
+
* SPDX-FileCopyrightText: © 2021-2023 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
3
3
|
* SPDX-License-Identifier: ICU
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -7,6 +7,8 @@ const _ = require( "lodash" );
|
|
|
7
7
|
const tools = require( "#tools" );
|
|
8
8
|
const config = require( "#config" );
|
|
9
9
|
const logger = require( "#logger" );
|
|
10
|
+
const exceptions = require( "#exceptions" );
|
|
11
|
+
const cache = require( "#cache" );
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* @typedef {Object} TiTraceEntry
|
|
@@ -14,13 +16,18 @@ const logger = require( "#logger" );
|
|
|
14
16
|
* @property {string} dispatchEvent
|
|
15
17
|
* @property {string} fromAddress
|
|
16
18
|
* @property {string} messageID
|
|
17
|
-
* @property {
|
|
19
|
+
* @property {Object} messageSnapshot
|
|
18
20
|
* @property {string} messageState
|
|
19
21
|
* @property {string} messageType
|
|
20
22
|
* @property {string} toAddress
|
|
21
23
|
* @property {string} traceID
|
|
24
|
+
* @property {number} traceTimestamp
|
|
22
25
|
*/
|
|
23
26
|
|
|
27
|
+
const traceRoot = {
|
|
28
|
+
trace: []
|
|
29
|
+
};
|
|
30
|
+
|
|
24
31
|
/**
|
|
25
32
|
* Enum for listing message types.
|
|
26
33
|
*
|
|
@@ -96,22 +103,28 @@ let formatLogEntry = ( traceEntry ) => {
|
|
|
96
103
|
};
|
|
97
104
|
|
|
98
105
|
/**
|
|
99
|
-
* Used to obscure sensitive data in the message
|
|
106
|
+
* Used to obscure sensitive data in the message, remove the payload, and return a snapshot.
|
|
100
107
|
*
|
|
101
108
|
* @method
|
|
102
109
|
* @param {Message} message
|
|
103
|
-
* @returns {
|
|
110
|
+
* @returns {Message}
|
|
104
111
|
* @private
|
|
105
112
|
*/
|
|
106
113
|
let obscureSensitiveData = ( message ) => {
|
|
107
|
-
|
|
108
|
-
|
|
114
|
+
/** @type Message */
|
|
115
|
+
let messageSnapshot = tools.parseJSON( _.replace( tools.stringifyJSON( message ), /("\w*?pin\w*?"|"\w*?pass\w*?"|"\w*?otp\w*?"):"(.*?)"/gmi, "\"SENSITIVE_PROPERTY\":\"OBSCURED_BY_SYSTEM\"" ) );
|
|
116
|
+
delete messageSnapshot.payload;
|
|
117
|
+
return messageSnapshot;
|
|
109
118
|
};
|
|
110
119
|
|
|
111
120
|
/**
|
|
112
121
|
* Used to create a trace entry for the provided {@link Message} and parameters.
|
|
113
122
|
* <br/>
|
|
114
|
-
* NOTE:
|
|
123
|
+
* NOTE: By default all trace events are stored in the memory cache for further processing and analysis. The
|
|
124
|
+
* location is configured in the MESSAGE_EXCHANGE_TRACE_REPOSITORY setting.
|
|
125
|
+
* <br/>
|
|
126
|
+
* NOTE: Trace events are logged with severity level NOTICE or ERROR for failed dispatches. They still might be
|
|
127
|
+
* filtered out if the minimum log level setting is set too high.
|
|
115
128
|
*
|
|
116
129
|
* @method
|
|
117
130
|
* @param {Message} message The message to trace.
|
|
@@ -124,6 +137,10 @@ module.exports.recordTraceEntry = ( message, messageType, dispatchEvent, message
|
|
|
124
137
|
// depending on whether the message comes as request or response, the from and to addresses will be opposite:
|
|
125
138
|
let source = message.source.route + "." + message.source.instanceID;
|
|
126
139
|
let destination = message.destination.route + ( ( message.destination.instanceID != null ) ? "." + message.destination.instanceID : "" );
|
|
140
|
+
let messageSnapshot = obscureSensitiveData( message );
|
|
141
|
+
delete messageSnapshot.chainID;
|
|
142
|
+
delete messageSnapshot.messageID;
|
|
143
|
+
let currentDate = new Date();
|
|
127
144
|
|
|
128
145
|
/** @type TiTraceEntry */
|
|
129
146
|
let traceEntry = {
|
|
@@ -131,16 +148,34 @@ module.exports.recordTraceEntry = ( message, messageType, dispatchEvent, message
|
|
|
131
148
|
dispatchEvent: tools.getEnumName( dispatchEventEnum, dispatchEvent ),
|
|
132
149
|
fromAddress: ( messageType === messageTypeEnum.MESSAGE_REQUEST ) ? source : destination,
|
|
133
150
|
messageID: message.messageID,
|
|
134
|
-
messageSnapshot:
|
|
151
|
+
messageSnapshot: messageSnapshot,
|
|
135
152
|
messageState: tools.getEnumName( messageStateEnum, messageState ),
|
|
136
153
|
messageType: tools.getEnumName( messageTypeEnum, messageType ),
|
|
137
154
|
toAddress: ( messageType === messageTypeEnum.MESSAGE_REQUEST ) ? destination : source,
|
|
155
|
+
traceTimestamp: currentDate.getTime(),
|
|
138
156
|
traceID: tools.getUUID()
|
|
139
157
|
};
|
|
140
158
|
|
|
159
|
+
// only write the trace in the general log if this is enabled:
|
|
141
160
|
if ( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_LOG_ENABLED ) === true ) {
|
|
142
|
-
createLogEntry( traceEntry, ( dispatchEvent === dispatchEventEnum.FAILED ) ? logger.logSeverity.ERROR : logger.logSeverity.
|
|
161
|
+
createLogEntry( traceEntry, ( dispatchEvent === dispatchEventEnum.FAILED ) ? logger.logSeverity.ERROR : logger.logSeverity.NOTICE );
|
|
143
162
|
}
|
|
144
163
|
|
|
145
|
-
//
|
|
164
|
+
// add the trace entry to the repository in the memory cache:
|
|
165
|
+
cache.setJSON( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), traceRoot, "$", 1 ).then( () => {
|
|
166
|
+
return cache.arrayAppendJSON( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), traceEntry, "$.trace" );
|
|
167
|
+
} ).then( () => {
|
|
168
|
+
// this will refresh the expiration time for the trace repository on each new record:
|
|
169
|
+
let expiration = config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_EXPIRATION_TIME );
|
|
170
|
+
return ( expiration > 0 ) ? cache.expireValue( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), expiration ) : expiration;
|
|
171
|
+
} ).catch( ( error ) => {
|
|
172
|
+
if ( error.code === exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED ) {
|
|
173
|
+
cache.addToSet( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), traceEntry ).catch( ( error ) => {
|
|
174
|
+
logger.log( `Failed to add message trace entry to the trace repository. While this will not prevent the application from running, it might still be a sign of a more serious problem!`, logger.logSeverity.WARNING, error );
|
|
175
|
+
} );
|
|
176
|
+
} else {
|
|
177
|
+
logger.log( `Failed to add message trace entry to the trace repository. While this will not prevent the application from running, it might still be a sign of a more serious problem!`, logger.logSeverity.WARNING, error );
|
|
178
|
+
}
|
|
179
|
+
} );
|
|
180
|
+
|
|
146
181
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
2
|
+
* SPDX-FileCopyrightText: © 2021-2023 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
3
3
|
* SPDX-License-Identifier: ICU
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -20,7 +20,7 @@ const messageDispatcher = require( "#message-dispatcher" );
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* @typedef {Object} ServiceExecContext
|
|
23
|
-
* @property {string} authToken A valid authentication token that initialized the service call.
|
|
23
|
+
* @property {string|undefined} authToken A valid authentication token that initialized the service call (if applicable).
|
|
24
24
|
* @property {ServiceCallPredecessor|undefined} previousServiceCall The previous service call in the execution chain (if such exists).
|
|
25
25
|
*/
|
|
26
26
|
|
|
@@ -36,7 +36,7 @@ const messageDispatcher = require( "#message-dispatcher" );
|
|
|
36
36
|
* @property {string} authToken A valid authentication token that initialized the service call.
|
|
37
37
|
* @property {number} createdOn A unix timestamp taken at creation time of the service call.
|
|
38
38
|
* @property {number} executionTime The total execution time of this service call in milliseconds.
|
|
39
|
-
* @property {Object|undefined} exception If there was exception during the service call processing, it will be set here. Otherwise it will be 'undefined'.
|
|
39
|
+
* @property {Object|undefined} exception If there was exception during the service call processing, it will be set here. Otherwise, it will be 'undefined'.
|
|
40
40
|
* @property {number|undefined} finishedOn A unix timestamp taken at finish time of the service call.
|
|
41
41
|
* @property {boolean} isCompleted Flag to indicate if this service call has been completed.
|
|
42
42
|
* @property {boolean|undefined} isSuccessful A flag indicating if this service call can be considered successful or not. Will be 'undefined' until the service call is processed.
|
|
@@ -45,9 +45,9 @@ const messageDispatcher = require( "#message-dispatcher" );
|
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* @typedef {Object} ServiceCallResult
|
|
48
|
-
* @property {Object|undefined} exception If there was exception during the service call processing, it will be set here. Otherwise it will be 'undefined'.
|
|
48
|
+
* @property {Object|undefined} exception If there was exception during the service call processing, it will be set here. Otherwise, it will be 'undefined'.
|
|
49
49
|
* @property {boolean} isSuccessful A flag indicating if this service call can be considered successful or not.
|
|
50
|
-
* @property {Object|string|undefined} payload The payload containing the results from the service call processing. If string
|
|
50
|
+
* @property {Object|string|undefined} payload The payload containing the results from the service call processing. If string it is ID of the payload in the memory cache instead.
|
|
51
51
|
*/
|
|
52
52
|
|
|
53
53
|
/**
|
|
@@ -78,7 +78,7 @@ class ServiceCaller extends MessageObserver {
|
|
|
78
78
|
/**
|
|
79
79
|
* Used to call a service in the service ecosystem asynchronously.
|
|
80
80
|
* <br/>
|
|
81
|
-
* NOTE: This method will
|
|
81
|
+
* NOTE: This method will time out after specific preconfigured time, in which case it will resolve with {@link E_COM_SERVICE_EXEC_TIMEOUT} error.
|
|
82
82
|
*
|
|
83
83
|
* @method
|
|
84
84
|
* @param {ServiceAddress} serviceAddress The service address has to define a valid service domain name, service alias, and optionally a service version.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
2
|
+
* SPDX-FileCopyrightText: © 2021-2023 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
3
3
|
* SPDX-License-Identifier: ICU
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -19,6 +19,14 @@ const messageDispatcher = require( "#message-dispatcher" );
|
|
|
19
19
|
* @property {number} [serviceVersion] Service version.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {Object.<string, ServiceInterfaceVersion>} ServiceInterface
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {Object.<number, ServiceHandlerMethod>} ServiceInterfaceVersion
|
|
28
|
+
*/
|
|
29
|
+
|
|
22
30
|
/**
|
|
23
31
|
* @callback VerifyAccessMethod
|
|
24
32
|
* @param {string} authToken
|
|
@@ -43,6 +51,7 @@ const messageDispatcher = require( "#message-dispatcher" );
|
|
|
43
51
|
*/
|
|
44
52
|
class ServiceExecutor extends MessageObserver {
|
|
45
53
|
|
|
54
|
+
/** @type ServiceInterface */
|
|
46
55
|
#serviceInterface = {};
|
|
47
56
|
/** @type VerifyAccessMethod */
|
|
48
57
|
#verifyAccess;
|
|
@@ -62,6 +71,15 @@ class ServiceExecutor extends MessageObserver {
|
|
|
62
71
|
|
|
63
72
|
/* Public interface */
|
|
64
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Property returning the current service interface.
|
|
76
|
+
*
|
|
77
|
+
* @property
|
|
78
|
+
* @returns {ServiceInterface}
|
|
79
|
+
* @public
|
|
80
|
+
*/
|
|
81
|
+
get serviceInterface() { return this.#serviceInterface; }
|
|
82
|
+
|
|
65
83
|
/**
|
|
66
84
|
*
|
|
67
85
|
*
|
|
@@ -117,7 +135,7 @@ class ServiceExecutor extends MessageObserver {
|
|
|
117
135
|
}
|
|
118
136
|
|
|
119
137
|
/**
|
|
120
|
-
* Used to
|
|
138
|
+
* Used to set up the method for service access verification.
|
|
121
139
|
*
|
|
122
140
|
* @method
|
|
123
141
|
* @param {VerifyAccessMethod} verifyAccess
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
2
|
+
* SPDX-FileCopyrightText: © 2021-2023 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
3
3
|
* SPDX-License-Identifier: ICU
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -12,10 +12,15 @@ const exceptions = require( "#exceptions" );
|
|
|
12
12
|
const cache = require( "#cache" );
|
|
13
13
|
const messageDispatcher = require( "#message-dispatcher" );
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} ServiceConfiguration
|
|
17
|
+
* @property {ServiceDefinition[]} services A list of service definitions to be registered with the {@link ServiceProvider}.
|
|
18
|
+
*/
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
21
|
* Abstract class used to define a Service Instance behavior.
|
|
17
22
|
* <br/>
|
|
18
|
-
* NOTE: Inherit this to create
|
|
23
|
+
* NOTE: Inherit this to create a module that can be started as a microservice instance.
|
|
19
24
|
* <br/>
|
|
20
25
|
* NOTE: This class does not
|
|
21
26
|
*
|
|
@@ -27,6 +32,7 @@ class ServiceInstance {
|
|
|
27
32
|
|
|
28
33
|
static #instanceID;
|
|
29
34
|
static #serviceDomainName;
|
|
35
|
+
/** @type ServiceConfiguration */
|
|
30
36
|
#serviceConfig;
|
|
31
37
|
#serviceHealthCheck;
|
|
32
38
|
#reportHealthyJob;
|
|
@@ -44,7 +50,7 @@ class ServiceInstance {
|
|
|
44
50
|
|
|
45
51
|
ServiceInstance.#instanceID = process.env.TI_INSTANCE_ID || tools.getUUID();
|
|
46
52
|
ServiceInstance.#serviceDomainName = serviceDomainName;
|
|
47
|
-
this.#serviceConfig = ( _.isObjectLike( serviceConfig ) ) ? serviceConfig : {};
|
|
53
|
+
this.#serviceConfig = ( _.isObjectLike( serviceConfig ) ) ? serviceConfig : { services: [] };
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
/* Public interface */
|
|
@@ -80,7 +86,7 @@ class ServiceInstance {
|
|
|
80
86
|
* Property returning the service configuration JSON.
|
|
81
87
|
*
|
|
82
88
|
* @property
|
|
83
|
-
* @returns {
|
|
89
|
+
* @returns {ServiceConfiguration}
|
|
84
90
|
* @public
|
|
85
91
|
*/
|
|
86
92
|
get serviceConfig() { return this.#serviceConfig; }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
2
|
+
* SPDX-FileCopyrightText: © 2021-2023 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
3
3
|
* SPDX-License-Identifier: ICU
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -13,7 +13,7 @@ const messageDispatcher = require( "#message-dispatcher" );
|
|
|
13
13
|
/**
|
|
14
14
|
* Abstract class used to define a Service Provider behavior.
|
|
15
15
|
* <br/>
|
|
16
|
-
* NOTE: Inherit this to create
|
|
16
|
+
* NOTE: Inherit this to create a module that can be started as a microservice provider instance.
|
|
17
17
|
* <br/>
|
|
18
18
|
* NOTE: A service provider is a microservice that offers an API of named business services that can be invoked by other
|
|
19
19
|
* microservices using {@link ServiceCall} objects. The provider will take care of the actual execution of that service and
|
|
@@ -28,6 +28,7 @@ const messageDispatcher = require( "#message-dispatcher" );
|
|
|
28
28
|
*/
|
|
29
29
|
class ServiceProvider extends ServiceConsumer {
|
|
30
30
|
|
|
31
|
+
/** @type ServiceExecutor */
|
|
31
32
|
#serviceExecutor;
|
|
32
33
|
|
|
33
34
|
/**
|
|
@@ -104,7 +105,7 @@ class ServiceProvider extends ServiceConsumer {
|
|
|
104
105
|
/**
|
|
105
106
|
* Used to verify whether the service caller has authorization to access the service.
|
|
106
107
|
* <br/>
|
|
107
|
-
* NOTE: Override this to implement authorization check. By default this method simply returns.
|
|
108
|
+
* NOTE: Override this to implement authorization check. By default, this method simply returns.
|
|
108
109
|
*
|
|
109
110
|
* @method
|
|
110
111
|
* @param {string} authToken
|
|
@@ -124,7 +125,7 @@ class ServiceProvider extends ServiceConsumer {
|
|
|
124
125
|
* keep in mind that your first param must always be the 'serviceDefinition' and the second one will be the general 'serviceParams' object.
|
|
125
126
|
* <br/>
|
|
126
127
|
* 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
|
+
* 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
|
*
|
|
129
130
|
* @method
|
|
130
131
|
* @param {ServiceDefinition} serviceDefinition Full service definition object.
|
|
@@ -180,7 +181,7 @@ class ServiceProvider extends ServiceConsumer {
|
|
|
180
181
|
let promises = [];
|
|
181
182
|
_.forEach( serviceDefinitions, ( serviceDefinition ) => {
|
|
182
183
|
// NOTE: we are not going to interrupt the service interface loading if one of the services fails to load or is not found!
|
|
183
|
-
// If this happens, a corresponding log entry will be created but the loading process will continue. Therefore the following
|
|
184
|
+
// If this happens, a corresponding log entry will be created but the loading process will continue. Therefore, the following
|
|
184
185
|
// promise will always resolve (unless a programming error occurs in it of course).
|
|
185
186
|
let registrationPromise = ( serviceDefinition, defaultServiceHandler ) => {
|
|
186
187
|
return new Promise( ( resolve, reject ) => {
|
|
@@ -211,6 +212,17 @@ class ServiceProvider extends ServiceConsumer {
|
|
|
211
212
|
} );
|
|
212
213
|
}
|
|
213
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Used to get an ordered list of all currently registered services. This does not include the service versions.
|
|
217
|
+
*
|
|
218
|
+
* @method
|
|
219
|
+
* @returns {string[]}
|
|
220
|
+
* @public
|
|
221
|
+
*/
|
|
222
|
+
getRegisteredServices() {
|
|
223
|
+
return _.sortBy( _.keys( this.#serviceExecutor.serviceInterface ) );
|
|
224
|
+
}
|
|
225
|
+
|
|
214
226
|
}
|
|
215
227
|
|
|
216
228
|
module.exports = ServiceProvider;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
2
|
+
* SPDX-FileCopyrightText: © 2021-2023 Boris Kostadinov <kostadinov.boris@gmail.com>
|
|
3
3
|
* SPDX-License-Identifier: ICU
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -19,6 +19,7 @@ const _ = require( "lodash" );
|
|
|
19
19
|
let cacheCommandsEnum = tools.enum( {
|
|
20
20
|
ADD_TO_SET: [ "sadd", "add to set", "https://redis.io/commands/sadd" ],
|
|
21
21
|
DELETE_VALUE: [ "del", "delete value", "https://redis.io/commands/del" ],
|
|
22
|
+
EXPIRE: [ "expire", "expire", "https://redis.io/commands/expire" ],
|
|
22
23
|
GET_ALL_FROM_SET: [ "smembers", "get all set members", "https://redis.io/commands/smembers" ],
|
|
23
24
|
GET_VALUE: [ "get", "get value", "https://redis.io/commands/get" ],
|
|
24
25
|
HASH_GET: [ "hget", "hash get", "https://redis.io/commands/hget" ],
|
|
@@ -27,6 +28,9 @@ let cacheCommandsEnum = tools.enum( {
|
|
|
27
28
|
HASH_SET: [ "hset", "", "https://redis.io/commands/hset" ],
|
|
28
29
|
HASH_SET_MANY: [ "hmset", "", "https://redis.io/commands/hmset" ],
|
|
29
30
|
IS_SET_MEMBER: [ "sismember", "", "https://redis.io/commands/sismember" ],
|
|
31
|
+
JSON_ARRAY_APPEND: [ "json.arrappend", "", "https://redis.io/commands/json.arrappend" ],
|
|
32
|
+
JSON_GET: [ "json.get", "", "https://redis.io/commands/json.get" ],
|
|
33
|
+
JSON_SET: [ "json.set", "", "https://redis.io/commands/json.set" ],
|
|
30
34
|
KEYS: [ "keys", "", "https://redis.io/commands/keys" ],
|
|
31
35
|
LIST_PUSH: [ "lpush", "list push", "https://redis.io/commands/lpush" ],
|
|
32
36
|
LIST_POP_TAIL_BLOCKING: [ "brpop", "list pop tail blocking", "https://redis.io/commands/brpop" ],
|
|
@@ -36,10 +40,26 @@ let cacheCommandsEnum = tools.enum( {
|
|
|
36
40
|
UNION_OF_SETS: [ "sunion", "union of sets", "https://redis.io/commands/sunion" ]
|
|
37
41
|
} );
|
|
38
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Enum for listing the Redis key override modes.
|
|
45
|
+
*
|
|
46
|
+
* @readonly
|
|
47
|
+
* @enum {string}
|
|
48
|
+
*/
|
|
49
|
+
let cacheOverrideModeEnum = tools.enum( {
|
|
50
|
+
DEFAULT: [ "", "default", "Standard Redis behaviour when setting new key." ],
|
|
51
|
+
NX: [ "nx", "nx", "Sets the key only if it does not already exist." ],
|
|
52
|
+
XX: [ "xx", "xx", "Sets the key only if it already exists." ]
|
|
53
|
+
} );
|
|
54
|
+
|
|
39
55
|
/**
|
|
40
56
|
* @typedef {string} TiRedisCommand
|
|
41
57
|
*/
|
|
42
58
|
module.exports.cacheCommands = cacheCommandsEnum;
|
|
59
|
+
/**
|
|
60
|
+
* @typedef {string} TiRedisOverrideMode
|
|
61
|
+
*/
|
|
62
|
+
module.exports.cacheOverrideMode = cacheOverrideModeEnum;
|
|
43
63
|
|
|
44
64
|
/**
|
|
45
65
|
* Used to create a Redis Cache client.
|
|
@@ -53,6 +73,8 @@ class RedisClient {
|
|
|
53
73
|
#retryMaxInterval = 1000;
|
|
54
74
|
#retryMaxAttempts = undefined;
|
|
55
75
|
#redisClient = undefined;
|
|
76
|
+
#serverInfo = {};
|
|
77
|
+
#serverFeatures = {};
|
|
56
78
|
#connectionObservers = [];
|
|
57
79
|
|
|
58
80
|
/**
|
|
@@ -101,13 +123,40 @@ class RedisClient {
|
|
|
101
123
|
this.#redisClient = new Redis( options );
|
|
102
124
|
|
|
103
125
|
this.#redisClient.on( "ready", () => {
|
|
104
|
-
|
|
105
|
-
logger.log( `Connection to Redis server ${ host }:${ port } (re)established by client '${ this.identifier }' and is ready to be used.`, logger.logSeverity.INFO, serverInfo );
|
|
126
|
+
logger.log( `Connection to Redis server ${ host }:${ port } (re)established by client '${ this.identifier }' and is ready to be used.`, logger.logSeverity.INFO );
|
|
106
127
|
|
|
107
128
|
// notify all connection observers about this event:
|
|
108
129
|
_.forEach( this.#connectionObservers, ( connectionObservers ) => {
|
|
109
130
|
connectionObservers.onConnectionRecovered( this.#clientIdentifier );
|
|
110
131
|
} );
|
|
132
|
+
|
|
133
|
+
// fetch the server information and store it:
|
|
134
|
+
this.#redisClient.info().then( ( result ) => {
|
|
135
|
+
this.#serverInfo = {};
|
|
136
|
+
if ( _.isString( result ) ) {
|
|
137
|
+
let rawData = _.split( result, "\r\n" );
|
|
138
|
+
_.forEach( rawData, ( entry ) => {
|
|
139
|
+
let details = _.split( entry, ":" );
|
|
140
|
+
if ( !_.startsWith( details[ 0 ], "#" ) && details[ 0 ] !== "" && details[ 0 ] ) {
|
|
141
|
+
if ( _.isNaN( _.toNumber( details[ 1 ] ) ) ) {
|
|
142
|
+
this.#serverInfo[ details[ 0 ] ] = details[ 1 ];
|
|
143
|
+
} else {
|
|
144
|
+
this.#serverInfo[ details[ 0 ] ] = _.toNumber( details[ 1 ] );
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} );
|
|
148
|
+
}
|
|
149
|
+
return this.#redisClient.module( "LIST" );
|
|
150
|
+
} ).then( ( result ) => {
|
|
151
|
+
this.#serverFeatures = {};
|
|
152
|
+
if ( _.isArray( result ) ) {
|
|
153
|
+
_.forEach( result, ( entry ) => {
|
|
154
|
+
this.#serverFeatures[ entry[ 1 ] ] = entry[ 3 ];
|
|
155
|
+
} );
|
|
156
|
+
}
|
|
157
|
+
} ).catch( ( error ) => {
|
|
158
|
+
logger.log( `Failed to fetch server information by client '${ this.identifier }'!`, logger.logSeverity.WARNING, error );
|
|
159
|
+
} );
|
|
111
160
|
} );
|
|
112
161
|
this.#redisClient.on( "error", ( error ) => {
|
|
113
162
|
logger.log( `Error received in Redis client '${ this.identifier }'.`, logger.logSeverity.ERROR, error );
|
|
@@ -137,6 +186,28 @@ class RedisClient {
|
|
|
137
186
|
return this.#clientIdentifier;
|
|
138
187
|
}
|
|
139
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Used to return the Redis server version.
|
|
191
|
+
*
|
|
192
|
+
* @property
|
|
193
|
+
* @return {number}
|
|
194
|
+
* @public
|
|
195
|
+
*/
|
|
196
|
+
get serverVersion() {
|
|
197
|
+
return this.#serverInfo[ "redis_version" ];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Verify if Redis server supports JSON data types.
|
|
202
|
+
*
|
|
203
|
+
* @property
|
|
204
|
+
* @returns {boolean}
|
|
205
|
+
* @public
|
|
206
|
+
*/
|
|
207
|
+
get isJSONSupported() {
|
|
208
|
+
return !_.isNil( this.#serverFeatures[ "ReJSON" ] );
|
|
209
|
+
}
|
|
210
|
+
|
|
140
211
|
/**
|
|
141
212
|
* Used to register a new {@link ConnectionObserver} for events related to the Redis connection state.
|
|
142
213
|
*
|
|
@@ -172,6 +243,7 @@ class RedisClient {
|
|
|
172
243
|
|
|
173
244
|
/**
|
|
174
245
|
* Used to send a new blocking command to Redis.
|
|
246
|
+
* <br/>
|
|
175
247
|
* WARNING: This will reserve the client connection until a result is received.
|
|
176
248
|
*
|
|
177
249
|
* @method
|
|
@@ -235,6 +307,28 @@ class RedisClient {
|
|
|
235
307
|
} );
|
|
236
308
|
}
|
|
237
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Used to execute any Redis command in an unmanaged way.
|
|
312
|
+
* <br/>
|
|
313
|
+
* WARNING: Use this only if there is no other implemented function in this module and the command
|
|
314
|
+
* you want to execute is not supported by the 'multi' Redis command (implemented in {@link RedisClient.executeCommands}).
|
|
315
|
+
* Make sure to handle the result as it will be returned raw.
|
|
316
|
+
*
|
|
317
|
+
* @method
|
|
318
|
+
* @param {string[]} commandArguments
|
|
319
|
+
* @returns {Promise<Object>}
|
|
320
|
+
* @public
|
|
321
|
+
*/
|
|
322
|
+
callCommand( commandArguments ) {
|
|
323
|
+
return new Promise( ( resolve, reject ) => {
|
|
324
|
+
this.#redisClient[ "call" ].apply( this.#redisClient, commandArguments ).then( ( result ) => {
|
|
325
|
+
resolve( result );
|
|
326
|
+
} ).catch( ( error ) => {
|
|
327
|
+
reject( exceptions.raise( error ) );
|
|
328
|
+
} );
|
|
329
|
+
} );
|
|
330
|
+
}
|
|
331
|
+
|
|
238
332
|
}
|
|
239
333
|
|
|
240
334
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ti-engine/core",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "The ti-engine is an open source, free to use
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "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.",
|
|
5
5
|
"author": "Boris Kostadinov <kostadinov.boris@gmail.com>",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"exports": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"optionalDependencies": {
|
|
52
52
|
"@google-cloud/error-reporting": "^3.0.5",
|
|
53
|
-
"zeromq": "^6.0.0-beta.
|
|
53
|
+
"zeromq": "^6.0.0-beta.19"
|
|
54
54
|
},
|
|
55
55
|
"repository": {
|
|
56
56
|
"type": "git",
|