@ti-engine/core 1.6.1 → 1.7.2
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 +383 -364
- package/LICENSE.md +321 -321
- package/README.md +597 -548
- package/bin/localization/labels.json +122 -122
- package/bin/settings.json +41 -41
- package/bin/start-instance.js +164 -156
- 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 -234
- 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 -90
- 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
package/utils/cache.js
CHANGED
|
@@ -1,773 +1,773 @@
|
|
|
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 ConnectionObserver = require( "#connection-observer" );
|
|
10
|
-
const _ = require( "lodash" );
|
|
11
|
-
const config = require( "#config" );
|
|
12
|
-
const tools = require( "#tools" );
|
|
13
|
-
const redis = require( "#redis-integration" );
|
|
14
|
-
const exceptions = require( "#exceptions" );
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Used to create and/or return a Common Memory Cache singleton instance.
|
|
18
|
-
*
|
|
19
|
-
* @class CommonMemoryCache
|
|
20
|
-
* @extends ConnectionObserver
|
|
21
|
-
* @singleton
|
|
22
|
-
* @public
|
|
23
|
-
*/
|
|
24
|
-
class CommonMemoryCache extends ConnectionObserver {
|
|
25
|
-
|
|
26
|
-
static #instance = null;
|
|
27
|
-
#redisClient = null;
|
|
28
|
-
#isOperational = false;
|
|
29
|
-
#connectionIdentifier = "system-cache";
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @constructor
|
|
33
|
-
* @return {CommonMemoryCache}
|
|
34
|
-
*/
|
|
35
|
-
constructor() {
|
|
36
|
-
super();
|
|
37
|
-
|
|
38
|
-
if ( !CommonMemoryCache.#instance ) {
|
|
39
|
-
this.#redisClient = redis.createRedisClient( this.#connectionIdentifier );
|
|
40
|
-
this.#redisClient.addConnectionObserver( this );
|
|
41
|
-
|
|
42
|
-
CommonMemoryCache.#instance = this;
|
|
43
|
-
}
|
|
44
|
-
return CommonMemoryCache.#instance;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/* Public interface */
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Property returning the operational state of the cache.
|
|
51
|
-
*
|
|
52
|
-
* @property
|
|
53
|
-
* @returns {boolean}
|
|
54
|
-
* @public
|
|
55
|
-
*/
|
|
56
|
-
get isOperational() {
|
|
57
|
-
return this.#isOperational;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Property returning the connection identifier of the cache service.
|
|
62
|
-
*
|
|
63
|
-
* @property
|
|
64
|
-
* @returns {string}
|
|
65
|
-
* @public
|
|
66
|
-
*/
|
|
67
|
-
get connectionIdentifier() {
|
|
68
|
-
return this.#connectionIdentifier;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Used to initialize the cache service.
|
|
73
|
-
*
|
|
74
|
-
* @method
|
|
75
|
-
* @returns {Promise}
|
|
76
|
-
* @public
|
|
77
|
-
*/
|
|
78
|
-
initialize() {
|
|
79
|
-
let host = config.getSetting( config.setting.MEMORY_CACHE_REDIS_HOST );
|
|
80
|
-
let port = config.getSetting( config.setting.MEMORY_CACHE_REDIS_PORT );
|
|
81
|
-
let db = config.getSetting( config.setting.MEMORY_CACHE_REDIS_DB );
|
|
82
|
-
let authKey = config.getSetting( config.setting.MEMORY_CACHE_AUTH_KEY );
|
|
83
|
-
let user = config.getSetting( config.setting.MEMORY_CACHE_USER );
|
|
84
|
-
|
|
85
|
-
return this.#redisClient.initialize( host, port, authKey, user, db );
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Used to gracefully shut down the cache service.
|
|
90
|
-
*
|
|
91
|
-
* @method
|
|
92
|
-
* @return {Promise}
|
|
93
|
-
* @public
|
|
94
|
-
*/
|
|
95
|
-
shutDown() {
|
|
96
|
-
return this.#redisClient.shutDown( 250 );
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Needs to be invoked by the connection handler when the connection is disrupted.
|
|
101
|
-
*
|
|
102
|
-
* @method
|
|
103
|
-
* @param {string} identifier The identifier of the observed connection.
|
|
104
|
-
* @override
|
|
105
|
-
* @public
|
|
106
|
-
*/
|
|
107
|
-
onConnectionDisrupted( identifier ) {
|
|
108
|
-
if ( identifier === this.#connectionIdentifier ) {
|
|
109
|
-
this.#isOperational = false;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Needs to be invoked by the connection handler when the connection is recovered.
|
|
115
|
-
*
|
|
116
|
-
* @method
|
|
117
|
-
* @param {string} identifier The identifier of the observed connection.
|
|
118
|
-
* @override
|
|
119
|
-
* @public
|
|
120
|
-
*/
|
|
121
|
-
onConnectionRecovered( identifier ) {
|
|
122
|
-
if ( identifier === this.#connectionIdentifier ) {
|
|
123
|
-
this.#isOperational = true;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Needs to be invoked by the connection handler when the connection is irrevocably lost.
|
|
129
|
-
*
|
|
130
|
-
* @method
|
|
131
|
-
* @param {string} identifier The identifier of the observed connection.
|
|
132
|
-
* @throws {TiException.E_GEN_SYSTEM_CACHE_UNAVAILABLE} If the cache service is no longer available.
|
|
133
|
-
* @override
|
|
134
|
-
* @public
|
|
135
|
-
*/
|
|
136
|
-
onConnectionLost( identifier ) {
|
|
137
|
-
if ( identifier === this.#connectionIdentifier ) {
|
|
138
|
-
this.#isOperational = false;
|
|
139
|
-
// TODO: implement forced shut down of the service instance instead of crashing it outright
|
|
140
|
-
throw exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE );
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Used to register a new {@link ConnectionObserver} for events related to the underlying Redis connection state.
|
|
146
|
-
*
|
|
147
|
-
* @method
|
|
148
|
-
* @param {ConnectionObserver} connectionObserver The {@link ConnectionObserver} that will be notified of any changes.
|
|
149
|
-
* @public
|
|
150
|
-
*/
|
|
151
|
-
addConnectionObserver( connectionObserver ) {
|
|
152
|
-
this.#redisClient.addConnectionObserver( connectionObserver );
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Used to search for keys by a given pattern.
|
|
157
|
-
*
|
|
158
|
-
* @method
|
|
159
|
-
* @param {string} pattern
|
|
160
|
-
* @returns {Promise<Array>}
|
|
161
|
-
* @public
|
|
162
|
-
*/
|
|
163
|
-
matchKeys( pattern ) {
|
|
164
|
-
return new Promise( ( resolve, reject ) => {
|
|
165
|
-
if ( this.#isOperational === true ) {
|
|
166
|
-
let commandKeys = [ redis.cacheCommands.KEYS, pattern ];
|
|
167
|
-
this.#redisClient.executeCommands( [ commandKeys ] ).then( ( results ) => {
|
|
168
|
-
results = results[ 0 ];
|
|
169
|
-
resolve( ( results && results.length > 1 ) ? results[ 1 ] : [] );
|
|
170
|
-
} ).catch( ( error ) => {
|
|
171
|
-
reject( error );
|
|
172
|
-
} );
|
|
173
|
-
} else {
|
|
174
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
175
|
-
}
|
|
176
|
-
} );
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Used to set a specific string value.
|
|
181
|
-
*
|
|
182
|
-
* @method
|
|
183
|
-
* @param {string} key
|
|
184
|
-
* @param {string} value
|
|
185
|
-
* @param {number} [expiration] Expiration value is in seconds.
|
|
186
|
-
* @return {Promise<string>}
|
|
187
|
-
* @public
|
|
188
|
-
*/
|
|
189
|
-
setValue( key, value, expiration ) {
|
|
190
|
-
return new Promise( ( resolve, reject ) => {
|
|
191
|
-
if ( this.#isOperational === true ) {
|
|
192
|
-
if ( value ) {
|
|
193
|
-
let commandSetValue = [ redis.cacheCommands.SET_VALUE, key, tools.stringifyJSON( value ) ];
|
|
194
|
-
if ( expiration ) {
|
|
195
|
-
commandSetValue.push( "EX" );
|
|
196
|
-
commandSetValue.push( expiration );
|
|
197
|
-
}
|
|
198
|
-
this.#redisClient.executeCommands( [ commandSetValue ] ).then( () => {
|
|
199
|
-
resolve( value );
|
|
200
|
-
} ).catch( ( error ) => {
|
|
201
|
-
reject( error );
|
|
202
|
-
} );
|
|
203
|
-
} else {
|
|
204
|
-
resolve( value );
|
|
205
|
-
}
|
|
206
|
-
} else {
|
|
207
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
208
|
-
}
|
|
209
|
-
} );
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Used to set multiple string values.
|
|
214
|
-
*
|
|
215
|
-
* @method
|
|
216
|
-
* @param {Object} keyValues
|
|
217
|
-
* @param {string} [prefix]
|
|
218
|
-
* @param {number} [expiration]
|
|
219
|
-
* @return {Promise}
|
|
220
|
-
* @public
|
|
221
|
-
*/
|
|
222
|
-
setValues( keyValues, prefix, expiration ) {
|
|
223
|
-
return new Promise( ( resolve, reject ) => {
|
|
224
|
-
if ( this.#isOperational === true ) {
|
|
225
|
-
if ( keyValues ) {
|
|
226
|
-
let commands = [];
|
|
227
|
-
_.forEach( keyValues, ( value, key ) => {
|
|
228
|
-
let commandSetValue = [ redis.cacheCommands.SET_VALUE, ( ( prefix ) ? prefix : "" ) + key, tools.stringifyJSON( value ) ];
|
|
229
|
-
if ( expiration ) {
|
|
230
|
-
commandSetValue.push( "EX" );
|
|
231
|
-
commandSetValue.push( expiration );
|
|
232
|
-
}
|
|
233
|
-
commands.push( commandSetValue );
|
|
234
|
-
} );
|
|
235
|
-
|
|
236
|
-
this.#redisClient.executeCommands( commands ).then( () => {
|
|
237
|
-
resolve( keyValues );
|
|
238
|
-
} ).catch( ( error ) => {
|
|
239
|
-
reject( error );
|
|
240
|
-
} );
|
|
241
|
-
} else {
|
|
242
|
-
resolve( keyValues );
|
|
243
|
-
}
|
|
244
|
-
} else {
|
|
245
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
246
|
-
}
|
|
247
|
-
} );
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Used to get a string value.
|
|
252
|
-
*
|
|
253
|
-
* @method
|
|
254
|
-
* @param {string} key
|
|
255
|
-
* @return {Promise}
|
|
256
|
-
* @public
|
|
257
|
-
*/
|
|
258
|
-
getValue( key ) {
|
|
259
|
-
return new Promise( ( resolve, reject ) => {
|
|
260
|
-
if ( this.#isOperational === true ) {
|
|
261
|
-
let commandGetValue = [ redis.cacheCommands.GET_VALUE, key ];
|
|
262
|
-
this.#redisClient.executeCommands( [ commandGetValue ] ).then( ( results ) => {
|
|
263
|
-
results = results[ 0 ];
|
|
264
|
-
resolve( ( results && results.length > 1 && _.isString( results[ 1 ] ) ) ? tools.parseJSON( results[ 1 ] ) : undefined );
|
|
265
|
-
} ).catch( ( error ) => {
|
|
266
|
-
reject( error );
|
|
267
|
-
} );
|
|
268
|
-
} else {
|
|
269
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
270
|
-
}
|
|
271
|
-
} );
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Used to get multiple string values.
|
|
276
|
-
*
|
|
277
|
-
* @method
|
|
278
|
-
* @param {string[]} keys
|
|
279
|
-
* @param {string} [prefix]
|
|
280
|
-
* @return {Promise}
|
|
281
|
-
* @public
|
|
282
|
-
*/
|
|
283
|
-
getValues( keys, prefix ) {
|
|
284
|
-
return new Promise( ( resolve, reject ) => {
|
|
285
|
-
if ( this.#isOperational === true ) {
|
|
286
|
-
let commands = [];
|
|
287
|
-
_.forEach( keys, ( key ) => {
|
|
288
|
-
commands.push( [ redis.cacheCommands.GET_VALUE, ( ( prefix ) ? prefix : "" ) + key ] );
|
|
289
|
-
} );
|
|
290
|
-
this.#redisClient.executeCommands( commands ).then( ( rawResults ) => {
|
|
291
|
-
let results = {};
|
|
292
|
-
_.forEach( rawResults, ( result, idx ) => {
|
|
293
|
-
results[ keys[ idx ] ] = ( results && results.length > 1 && _.isString( results[ 1 ] ) ) ? tools.parseJSON( results[ 1 ] ) : null;
|
|
294
|
-
} );
|
|
295
|
-
resolve( results );
|
|
296
|
-
} ).catch( ( error ) => {
|
|
297
|
-
reject( error );
|
|
298
|
-
} );
|
|
299
|
-
} else {
|
|
300
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
301
|
-
}
|
|
302
|
-
} );
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Used to delete a value / item.
|
|
307
|
-
*
|
|
308
|
-
* @method
|
|
309
|
-
* @param {string} key
|
|
310
|
-
* @returns {Promise<boolean>}
|
|
311
|
-
* @public
|
|
312
|
-
*/
|
|
313
|
-
deleteValue( key ) {
|
|
314
|
-
return new Promise( ( resolve, reject ) => {
|
|
315
|
-
if ( this.#isOperational === true ) {
|
|
316
|
-
let commandDeleteValue = [ redis.cacheCommands.DELETE_VALUE, key ];
|
|
317
|
-
this.#redisClient.executeCommands( [ commandDeleteValue ] ).then( ( results ) => {
|
|
318
|
-
results = results[ 0 ];
|
|
319
|
-
resolve( ( results && results.length > 1 ) ? results[ 1 ] : undefined );
|
|
320
|
-
} ).catch( ( error ) => {
|
|
321
|
-
reject( error );
|
|
322
|
-
} );
|
|
323
|
-
} else {
|
|
324
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
325
|
-
}
|
|
326
|
-
} );
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Used to set expiration in seconds to an existing key.
|
|
331
|
-
* <br/>
|
|
332
|
-
* NOTE: For performance optimization reasons, only use this only if the Redis command does not itself support the 'EX' argument.
|
|
333
|
-
*
|
|
334
|
-
* @method
|
|
335
|
-
* @param {string} key
|
|
336
|
-
* @param {number} seconds
|
|
337
|
-
* @param {string} [name] If you need to expire a field in a hash set instead, provide the name of the set here.
|
|
338
|
-
* @returns {Promise<number>} This will resolve with the seconds as provided initially by the caller.
|
|
339
|
-
* @public
|
|
340
|
-
*/
|
|
341
|
-
expireValue( key, seconds, name ) {
|
|
342
|
-
return new Promise( ( resolve, reject ) => {
|
|
343
|
-
if ( this.#isOperational === true ) {
|
|
344
|
-
let commandExpire = ( name ) ? [ redis.cacheCommands.HASH_EXPIRE, name, seconds, "FIELDS", 1, key ] : [ redis.cacheCommands.EXPIRE, key, seconds ];
|
|
345
|
-
this.#redisClient.executeCommands( [ commandExpire ] ).then( () => {
|
|
346
|
-
resolve( seconds );
|
|
347
|
-
} ).catch( ( error ) => {
|
|
348
|
-
reject( error );
|
|
349
|
-
} );
|
|
350
|
-
} else {
|
|
351
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
352
|
-
}
|
|
353
|
-
} );
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Used to add the specified values to a list.
|
|
358
|
-
*
|
|
359
|
-
* @method
|
|
360
|
-
* @param {string} listName
|
|
361
|
-
* @param {Object[]} values
|
|
362
|
-
* @returns {Promise<number>}
|
|
363
|
-
* @public
|
|
364
|
-
*/
|
|
365
|
-
listPushValue( listName, values ) {
|
|
366
|
-
return new Promise( ( resolve, reject ) => {
|
|
367
|
-
if ( this.#isOperational === true ) {
|
|
368
|
-
let commandPushValues = [ redis.cacheCommands.LIST_PUSH, listName ];
|
|
369
|
-
_.forEach( values, ( value ) => {
|
|
370
|
-
if ( value ) {
|
|
371
|
-
commandPushValues.push( tools.stringifyJSON( value ) );
|
|
372
|
-
}
|
|
373
|
-
} );
|
|
374
|
-
this.#redisClient.executeCommands( [ commandPushValues ] ).then( ( results ) => {
|
|
375
|
-
results = results[ 0 ];
|
|
376
|
-
resolve( ( results && results.length > 1 ) ? results[ 1 ] : undefined );
|
|
377
|
-
} ).catch( ( error ) => {
|
|
378
|
-
reject( error );
|
|
379
|
-
} );
|
|
380
|
-
} else {
|
|
381
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
382
|
-
}
|
|
383
|
-
} );
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Used to add the specified value to a set.
|
|
388
|
-
*
|
|
389
|
-
* @method
|
|
390
|
-
* @param {string} key
|
|
391
|
-
* @param {string|Object} value
|
|
392
|
-
* @returns {Promise}
|
|
393
|
-
* @public
|
|
394
|
-
*/
|
|
395
|
-
addToSet( key, value ) {
|
|
396
|
-
return new Promise( ( resolve, reject ) => {
|
|
397
|
-
if ( this.#isOperational === true ) {
|
|
398
|
-
let commandAddToSet = [ redis.cacheCommands.ADD_TO_SET, key, tools.stringifyJSON( value ) ];
|
|
399
|
-
this.#redisClient.executeCommands( [ commandAddToSet ] ).then( () => {
|
|
400
|
-
resolve();
|
|
401
|
-
} ).catch( ( error ) => {
|
|
402
|
-
reject( error );
|
|
403
|
-
} );
|
|
404
|
-
} else {
|
|
405
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
406
|
-
}
|
|
407
|
-
} );
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Used to add multiple values to multiple sets in one transactional request.
|
|
412
|
-
* <br/>
|
|
413
|
-
* NOTE: The two arrays of keys and values must have correct index relations (i.e., first pair on keys[0] and values[0] and so on)!
|
|
414
|
-
*
|
|
415
|
-
* @method
|
|
416
|
-
* @param {string[]} keys
|
|
417
|
-
* @param {string[]} values
|
|
418
|
-
* @returns {Promise}
|
|
419
|
-
* @public
|
|
420
|
-
*/
|
|
421
|
-
addToSetMulti( keys, values ) {
|
|
422
|
-
return new Promise( ( resolve, reject ) => {
|
|
423
|
-
if ( this.#isOperational === true ) {
|
|
424
|
-
let commands = [];
|
|
425
|
-
_.forEach( keys, ( key, idx ) => {
|
|
426
|
-
commands.push( [ redis.cacheCommands.ADD_TO_SET, key, tools.stringifyJSON( values[ idx ] ) ] );
|
|
427
|
-
} );
|
|
428
|
-
this.#redisClient.executeCommands( commands ).then( () => {
|
|
429
|
-
resolve();
|
|
430
|
-
} ).catch( ( error ) => {
|
|
431
|
-
reject( error );
|
|
432
|
-
} );
|
|
433
|
-
} else {
|
|
434
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
435
|
-
}
|
|
436
|
-
} );
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* Used to check if the provided value is a member of the specified set.
|
|
441
|
-
*
|
|
442
|
-
* @method
|
|
443
|
-
* @param {string} setName
|
|
444
|
-
* @param {string} value
|
|
445
|
-
* @returns {Promise<boolean>}
|
|
446
|
-
* @public
|
|
447
|
-
*/
|
|
448
|
-
isSetMember( setName, value ) {
|
|
449
|
-
return new Promise( ( resolve, reject ) => {
|
|
450
|
-
if ( this.#isOperational === true ) {
|
|
451
|
-
let commandIsSetMember = [ redis.cacheCommands.IS_SET_MEMBER, setName, value ];
|
|
452
|
-
this.#redisClient.executeCommands( [ commandIsSetMember ] ).then( ( results ) => {
|
|
453
|
-
results = results[ 0 ];
|
|
454
|
-
let result = !!( results && results.length > 1 && results[ 1 ] === 1 );
|
|
455
|
-
resolve( result );
|
|
456
|
-
} ).catch( ( error ) => {
|
|
457
|
-
reject( error );
|
|
458
|
-
} );
|
|
459
|
-
} else {
|
|
460
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
461
|
-
}
|
|
462
|
-
} );
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
/**
|
|
466
|
-
* Used to get all elements of a set.
|
|
467
|
-
*
|
|
468
|
-
* @method
|
|
469
|
-
* @param {string} key
|
|
470
|
-
* @returns {Promise<Object[]>}
|
|
471
|
-
* @public
|
|
472
|
-
*/
|
|
473
|
-
membersOfSet( key ) {
|
|
474
|
-
return new Promise( ( resolve, reject ) => {
|
|
475
|
-
if ( this.#isOperational === true ) {
|
|
476
|
-
let commandMembersOfSet = [ redis.cacheCommands.GET_ALL_FROM_SET, key ];
|
|
477
|
-
this.#redisClient.executeCommands( [ commandMembersOfSet ] ).then( ( results ) => {
|
|
478
|
-
results = results[ 0 ];
|
|
479
|
-
let parsedResults = ( results && results.length > 1 && results[ 1 ] ) ? results[ 1 ] : [];
|
|
480
|
-
resolve( parsedResults );
|
|
481
|
-
} ).catch( ( error ) => {
|
|
482
|
-
reject( error );
|
|
483
|
-
} );
|
|
484
|
-
} else {
|
|
485
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
486
|
-
}
|
|
487
|
-
} );
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
/**
|
|
491
|
-
* Used to get a union of all elements in the list of sets.
|
|
492
|
-
*
|
|
493
|
-
* @method
|
|
494
|
-
* @param {string[]} keys
|
|
495
|
-
* @returns {Promise<Object[]>}
|
|
496
|
-
* @public
|
|
497
|
-
*/
|
|
498
|
-
unionOfSets( keys ) {
|
|
499
|
-
return new Promise( ( resolve, reject ) => {
|
|
500
|
-
if ( this.#isOperational === true ) {
|
|
501
|
-
let commandUnionOfSets = _.concat( [ redis.cacheCommands.UNION_OF_SETS ], keys );
|
|
502
|
-
this.#redisClient.executeCommands( [ commandUnionOfSets ] ).then( ( results ) => {
|
|
503
|
-
results = results[ 0 ];
|
|
504
|
-
let parsedResults = ( results && results.length > 1 && results[ 1 ] ) ? results[ 1 ] : [];
|
|
505
|
-
resolve( parsedResults );
|
|
506
|
-
} ).catch( ( error ) => {
|
|
507
|
-
reject( error );
|
|
508
|
-
} );
|
|
509
|
-
} else {
|
|
510
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
511
|
-
}
|
|
512
|
-
} );
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* Used to set a single hash field.
|
|
517
|
-
*
|
|
518
|
-
* @method
|
|
519
|
-
* @deprecated
|
|
520
|
-
* @param {string} key
|
|
521
|
-
* @param {string} name
|
|
522
|
-
* @param {*} value
|
|
523
|
-
* @returns {Promise}
|
|
524
|
-
* @public
|
|
525
|
-
*/
|
|
526
|
-
hashSetField( key, name, value ) {
|
|
527
|
-
return new Promise( ( resolve, reject ) => {
|
|
528
|
-
if ( this.#isOperational === true ) {
|
|
529
|
-
let commandHashSetField = [ redis.cacheCommands.HASH_SET, key, name, tools.stringifyJSON( value ) ];
|
|
530
|
-
this.#redisClient.executeCommands( [ commandHashSetField ] ).then( () => {
|
|
531
|
-
resolve();
|
|
532
|
-
} ).catch( ( error ) => {
|
|
533
|
-
reject( error );
|
|
534
|
-
} );
|
|
535
|
-
} else {
|
|
536
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
537
|
-
}
|
|
538
|
-
} );
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
/**
|
|
542
|
-
* Used to set multiple hash fields.
|
|
543
|
-
*
|
|
544
|
-
* @method
|
|
545
|
-
* @deprecated
|
|
546
|
-
* @param {string} key
|
|
547
|
-
* @param {Object[]} fields
|
|
548
|
-
* @param {string} fields[].name
|
|
549
|
-
* @param {*} fields[].value
|
|
550
|
-
* @returns {Promise}
|
|
551
|
-
* @public
|
|
552
|
-
*/
|
|
553
|
-
hashSetFields( key, fields ) {
|
|
554
|
-
return new Promise( ( resolve, reject ) => {
|
|
555
|
-
if ( this.#isOperational === true ) {
|
|
556
|
-
let commandHashSetFields = [ redis.cacheCommands.HASH_SET, key ];
|
|
557
|
-
_.forEach( fields, ( field ) => {
|
|
558
|
-
commandHashSetFields.push( field.name );
|
|
559
|
-
commandHashSetFields.push( tools.stringifyJSON( field.value ) );
|
|
560
|
-
} );
|
|
561
|
-
this.#redisClient.executeCommands( [ commandHashSetFields ] ).then( () => {
|
|
562
|
-
resolve();
|
|
563
|
-
} ).catch( ( error ) => {
|
|
564
|
-
reject( error );
|
|
565
|
-
} );
|
|
566
|
-
} else {
|
|
567
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
568
|
-
}
|
|
569
|
-
} );
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Used to get a single field from a hash.
|
|
574
|
-
*
|
|
575
|
-
* @method
|
|
576
|
-
* @param {string} key
|
|
577
|
-
* @param {string} field
|
|
578
|
-
* @return {Promise}
|
|
579
|
-
* @public
|
|
580
|
-
*/
|
|
581
|
-
hashGetField( key, field ) {
|
|
582
|
-
return new Promise( ( resolve, reject ) => {
|
|
583
|
-
if ( this.#isOperational === true ) {
|
|
584
|
-
let commandHashGetField = [ redis.cacheCommands.HASH_GET, key, field ];
|
|
585
|
-
this.#redisClient.executeCommands( [ commandHashGetField ] ).then( ( results ) => {
|
|
586
|
-
results = results[ 0 ];
|
|
587
|
-
resolve( ( results && results.length > 1 && _.isString( results[ 1 ] ) ) ? tools.parseJSON( results[ 1 ] ) : null );
|
|
588
|
-
} ).catch( ( error ) => {
|
|
589
|
-
reject( error );
|
|
590
|
-
} );
|
|
591
|
-
} else {
|
|
592
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
593
|
-
}
|
|
594
|
-
} );
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
/**
|
|
598
|
-
* Used to remove a single field from a hash.
|
|
599
|
-
*
|
|
600
|
-
* @method
|
|
601
|
-
* @param {string} key
|
|
602
|
-
* @param {string} field
|
|
603
|
-
* @return {Promise<boolean>} Will return 'true' if the field was removed, 'false' otherwise.
|
|
604
|
-
* @public
|
|
605
|
-
*/
|
|
606
|
-
hashDeleteField( key, field ) {
|
|
607
|
-
return new Promise( ( resolve, reject ) => {
|
|
608
|
-
if ( this.#isOperational === true ) {
|
|
609
|
-
let commandHashGetField = [ redis.cacheCommands.HASH_REMOVE, key, field ];
|
|
610
|
-
this.#redisClient.executeCommands( [ commandHashGetField ] ).then( ( results ) => {
|
|
611
|
-
results = results[ 0 ];
|
|
612
|
-
resolve( ( results && results.length > 1 ) ? tools.toBool( results[ 1 ] ) : false );
|
|
613
|
-
} ).catch( ( error ) => {
|
|
614
|
-
reject( error );
|
|
615
|
-
} );
|
|
616
|
-
} else {
|
|
617
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
618
|
-
}
|
|
619
|
-
} );
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
/**
|
|
623
|
-
* Used to store a JSON variable.
|
|
624
|
-
* <br/>
|
|
625
|
-
* NOTE: Requires ReJSON module installed on server to work.
|
|
626
|
-
*
|
|
627
|
-
* @method
|
|
628
|
-
* @param {string} key
|
|
629
|
-
* @param {Object} value
|
|
630
|
-
* @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
|
|
631
|
-
* @param {number} [overrideMode=0] By default this allows full override for existing keys.
|
|
632
|
-
* Option 1 will set the key only if it doesn't already exist. Option 2 will set it only if it already exists.
|
|
633
|
-
* @returns {Promise}
|
|
634
|
-
* @public
|
|
635
|
-
*/
|
|
636
|
-
setJSON( key, value, path = "$", overrideMode = 0 ) {
|
|
637
|
-
return new Promise( ( resolve, reject ) => {
|
|
638
|
-
if ( this.#isOperational === true ) {
|
|
639
|
-
if ( this.#redisClient.isJSONSupported ) {
|
|
640
|
-
let commandArguments = [ redis.cacheCommands.JSON_SET, key, this.#normalizeJSONPath( path ), tools.stringifyJSON( value ) ];
|
|
641
|
-
if ( overrideMode !== 0 ) {
|
|
642
|
-
commandArguments.push( overrideMode === 1 ? redis.cacheOverrideMode.NX : redis.cacheOverrideMode.XX );
|
|
643
|
-
}
|
|
644
|
-
this.#redisClient.callCommand( commandArguments ).then( () => {
|
|
645
|
-
resolve();
|
|
646
|
-
} ).catch( ( error ) => {
|
|
647
|
-
reject( error );
|
|
648
|
-
} );
|
|
649
|
-
} else {
|
|
650
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
|
|
651
|
-
}
|
|
652
|
-
} else {
|
|
653
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
654
|
-
}
|
|
655
|
-
} );
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
/**
|
|
659
|
-
* Used to fetch a JSON variable.
|
|
660
|
-
* <br/>
|
|
661
|
-
* NOTE: Requires ReJSON module installed on server to work.
|
|
662
|
-
*
|
|
663
|
-
* @method
|
|
664
|
-
* @param {string} key
|
|
665
|
-
* @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
|
|
666
|
-
* @returns {Promise<Object>}
|
|
667
|
-
* @public
|
|
668
|
-
*/
|
|
669
|
-
getJSON( key, path = "$" ) {
|
|
670
|
-
return new Promise( ( resolve, reject ) => {
|
|
671
|
-
if ( this.#isOperational === true ) {
|
|
672
|
-
if ( this.#redisClient.isJSONSupported ) {
|
|
673
|
-
let commandArguments = [ redis.cacheCommands.JSON_GET, key, this.#normalizeJSONPath( path ) ];
|
|
674
|
-
this.#redisClient.callCommand( commandArguments ).then( ( result ) => {
|
|
675
|
-
resolve( result != null ? tools.parseJSON( String( result ) ) : null );
|
|
676
|
-
} ).catch( ( error ) => {
|
|
677
|
-
reject( error );
|
|
678
|
-
} );
|
|
679
|
-
} else {
|
|
680
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
|
|
681
|
-
}
|
|
682
|
-
} else {
|
|
683
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
684
|
-
}
|
|
685
|
-
} );
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
/**
|
|
689
|
-
* Used to update/edit an existing JSON variable.
|
|
690
|
-
* <br/>
|
|
691
|
-
* NOTE: Requires ReJSON module installed on server to work.
|
|
692
|
-
*
|
|
693
|
-
* @method
|
|
694
|
-
* @param {string} key
|
|
695
|
-
* @param {Object} value
|
|
696
|
-
* @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
|
|
697
|
-
* @returns {Promise}
|
|
698
|
-
* @public
|
|
699
|
-
*/
|
|
700
|
-
editJSON( key, value, path = "$" ) {
|
|
701
|
-
return new Promise( ( resolve, reject ) => {
|
|
702
|
-
if ( this.#isOperational === true ) {
|
|
703
|
-
if ( this.#redisClient.isJSONSupported ) {
|
|
704
|
-
let commandArguments = [ redis.cacheCommands.JSON_MERGE, key, this.#normalizeJSONPath( path ), tools.stringifyJSON( value ) ];
|
|
705
|
-
this.#redisClient.callCommand( commandArguments ).then( () => {
|
|
706
|
-
resolve();
|
|
707
|
-
} ).catch( ( error ) => {
|
|
708
|
-
reject( error );
|
|
709
|
-
} );
|
|
710
|
-
} else {
|
|
711
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
|
|
712
|
-
}
|
|
713
|
-
} else {
|
|
714
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
715
|
-
}
|
|
716
|
-
} );
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
/**
|
|
720
|
-
* Used to add an item to a JSON array. That array needs to exist already.
|
|
721
|
-
* <br/>
|
|
722
|
-
* NOTE: Requires ReJSON module installed on server to work.
|
|
723
|
-
*
|
|
724
|
-
* @method
|
|
725
|
-
* @param {string} key
|
|
726
|
-
* @param {Object} value
|
|
727
|
-
* @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
|
|
728
|
-
* @returns {Promise}
|
|
729
|
-
* @public
|
|
730
|
-
*/
|
|
731
|
-
arrayAppendJSON( key, value, path = "$" ) {
|
|
732
|
-
return new Promise( ( resolve, reject ) => {
|
|
733
|
-
if ( this.#isOperational === true ) {
|
|
734
|
-
if ( this.#redisClient.isJSONSupported ) {
|
|
735
|
-
let commandArguments = [ redis.cacheCommands.JSON_ARRAY_APPEND, key, this.#normalizeJSONPath( path ), tools.stringifyJSON( value ) ];
|
|
736
|
-
this.#redisClient.callCommand( commandArguments ).then( () => {
|
|
737
|
-
resolve();
|
|
738
|
-
} ).catch( ( error ) => {
|
|
739
|
-
reject( error );
|
|
740
|
-
} );
|
|
741
|
-
} else {
|
|
742
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
|
|
743
|
-
}
|
|
744
|
-
} else {
|
|
745
|
-
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
746
|
-
}
|
|
747
|
-
} );
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
/* Private interface */
|
|
751
|
-
|
|
752
|
-
/**
|
|
753
|
-
* Used to normalize a JSON path.
|
|
754
|
-
* <br/>
|
|
755
|
-
* NOTE: If "path" is an array, each element is treated as a literal key name and encoded with bracket notation,
|
|
756
|
-
* which correctly handles key names that contain dots or other JSONPath special characters.
|
|
757
|
-
*
|
|
758
|
-
* @method
|
|
759
|
-
* @param {string|string[]} path
|
|
760
|
-
* @returns {string}
|
|
761
|
-
* @private
|
|
762
|
-
*/
|
|
763
|
-
#normalizeJSONPath( path ) {
|
|
764
|
-
if ( Array.isArray( path ) ) {
|
|
765
|
-
return "$" + path.map( ( segment ) => `["${ String( segment ).replace( /\\/g, "\\\\" ).replace( /"/g, '\\"' ) }"]` ).join( "" );
|
|
766
|
-
}
|
|
767
|
-
return ( path.startsWith( "$" ) === false ) ? ( "$." + path ) : path;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
const instance = new CommonMemoryCache();
|
|
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 ConnectionObserver = require( "#connection-observer" );
|
|
10
|
+
const _ = require( "lodash" );
|
|
11
|
+
const config = require( "#config" );
|
|
12
|
+
const tools = require( "#tools" );
|
|
13
|
+
const redis = require( "#redis-integration" );
|
|
14
|
+
const exceptions = require( "#exceptions" );
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Used to create and/or return a Common Memory Cache singleton instance.
|
|
18
|
+
*
|
|
19
|
+
* @class CommonMemoryCache
|
|
20
|
+
* @extends ConnectionObserver
|
|
21
|
+
* @singleton
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
class CommonMemoryCache extends ConnectionObserver {
|
|
25
|
+
|
|
26
|
+
static #instance = null;
|
|
27
|
+
#redisClient = null;
|
|
28
|
+
#isOperational = false;
|
|
29
|
+
#connectionIdentifier = "system-cache";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @constructor
|
|
33
|
+
* @return {CommonMemoryCache}
|
|
34
|
+
*/
|
|
35
|
+
constructor() {
|
|
36
|
+
super();
|
|
37
|
+
|
|
38
|
+
if ( !CommonMemoryCache.#instance ) {
|
|
39
|
+
this.#redisClient = redis.createRedisClient( this.#connectionIdentifier );
|
|
40
|
+
this.#redisClient.addConnectionObserver( this );
|
|
41
|
+
|
|
42
|
+
CommonMemoryCache.#instance = this;
|
|
43
|
+
}
|
|
44
|
+
return CommonMemoryCache.#instance;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Public interface */
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Property returning the operational state of the cache.
|
|
51
|
+
*
|
|
52
|
+
* @property
|
|
53
|
+
* @returns {boolean}
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
get isOperational() {
|
|
57
|
+
return this.#isOperational;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Property returning the connection identifier of the cache service.
|
|
62
|
+
*
|
|
63
|
+
* @property
|
|
64
|
+
* @returns {string}
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
get connectionIdentifier() {
|
|
68
|
+
return this.#connectionIdentifier;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Used to initialize the cache service.
|
|
73
|
+
*
|
|
74
|
+
* @method
|
|
75
|
+
* @returns {Promise}
|
|
76
|
+
* @public
|
|
77
|
+
*/
|
|
78
|
+
initialize() {
|
|
79
|
+
let host = config.getSetting( config.setting.MEMORY_CACHE_REDIS_HOST );
|
|
80
|
+
let port = config.getSetting( config.setting.MEMORY_CACHE_REDIS_PORT );
|
|
81
|
+
let db = config.getSetting( config.setting.MEMORY_CACHE_REDIS_DB );
|
|
82
|
+
let authKey = config.getSetting( config.setting.MEMORY_CACHE_AUTH_KEY );
|
|
83
|
+
let user = config.getSetting( config.setting.MEMORY_CACHE_USER );
|
|
84
|
+
|
|
85
|
+
return this.#redisClient.initialize( host, port, authKey, user, db );
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Used to gracefully shut down the cache service.
|
|
90
|
+
*
|
|
91
|
+
* @method
|
|
92
|
+
* @return {Promise}
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
95
|
+
shutDown() {
|
|
96
|
+
return this.#redisClient.shutDown( 250 );
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Needs to be invoked by the connection handler when the connection is disrupted.
|
|
101
|
+
*
|
|
102
|
+
* @method
|
|
103
|
+
* @param {string} identifier The identifier of the observed connection.
|
|
104
|
+
* @override
|
|
105
|
+
* @public
|
|
106
|
+
*/
|
|
107
|
+
onConnectionDisrupted( identifier ) {
|
|
108
|
+
if ( identifier === this.#connectionIdentifier ) {
|
|
109
|
+
this.#isOperational = false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Needs to be invoked by the connection handler when the connection is recovered.
|
|
115
|
+
*
|
|
116
|
+
* @method
|
|
117
|
+
* @param {string} identifier The identifier of the observed connection.
|
|
118
|
+
* @override
|
|
119
|
+
* @public
|
|
120
|
+
*/
|
|
121
|
+
onConnectionRecovered( identifier ) {
|
|
122
|
+
if ( identifier === this.#connectionIdentifier ) {
|
|
123
|
+
this.#isOperational = true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Needs to be invoked by the connection handler when the connection is irrevocably lost.
|
|
129
|
+
*
|
|
130
|
+
* @method
|
|
131
|
+
* @param {string} identifier The identifier of the observed connection.
|
|
132
|
+
* @throws {TiException.E_GEN_SYSTEM_CACHE_UNAVAILABLE} If the cache service is no longer available.
|
|
133
|
+
* @override
|
|
134
|
+
* @public
|
|
135
|
+
*/
|
|
136
|
+
onConnectionLost( identifier ) {
|
|
137
|
+
if ( identifier === this.#connectionIdentifier ) {
|
|
138
|
+
this.#isOperational = false;
|
|
139
|
+
// TODO: implement forced shut down of the service instance instead of crashing it outright
|
|
140
|
+
throw exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE );
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Used to register a new {@link ConnectionObserver} for events related to the underlying Redis connection state.
|
|
146
|
+
*
|
|
147
|
+
* @method
|
|
148
|
+
* @param {ConnectionObserver} connectionObserver The {@link ConnectionObserver} that will be notified of any changes.
|
|
149
|
+
* @public
|
|
150
|
+
*/
|
|
151
|
+
addConnectionObserver( connectionObserver ) {
|
|
152
|
+
this.#redisClient.addConnectionObserver( connectionObserver );
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Used to search for keys by a given pattern.
|
|
157
|
+
*
|
|
158
|
+
* @method
|
|
159
|
+
* @param {string} pattern
|
|
160
|
+
* @returns {Promise<Array>}
|
|
161
|
+
* @public
|
|
162
|
+
*/
|
|
163
|
+
matchKeys( pattern ) {
|
|
164
|
+
return new Promise( ( resolve, reject ) => {
|
|
165
|
+
if ( this.#isOperational === true ) {
|
|
166
|
+
let commandKeys = [ redis.cacheCommands.KEYS, pattern ];
|
|
167
|
+
this.#redisClient.executeCommands( [ commandKeys ] ).then( ( results ) => {
|
|
168
|
+
results = results[ 0 ];
|
|
169
|
+
resolve( ( results && results.length > 1 ) ? results[ 1 ] : [] );
|
|
170
|
+
} ).catch( ( error ) => {
|
|
171
|
+
reject( error );
|
|
172
|
+
} );
|
|
173
|
+
} else {
|
|
174
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
175
|
+
}
|
|
176
|
+
} );
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Used to set a specific string value.
|
|
181
|
+
*
|
|
182
|
+
* @method
|
|
183
|
+
* @param {string} key
|
|
184
|
+
* @param {string} value
|
|
185
|
+
* @param {number} [expiration] Expiration value is in seconds.
|
|
186
|
+
* @return {Promise<string>}
|
|
187
|
+
* @public
|
|
188
|
+
*/
|
|
189
|
+
setValue( key, value, expiration ) {
|
|
190
|
+
return new Promise( ( resolve, reject ) => {
|
|
191
|
+
if ( this.#isOperational === true ) {
|
|
192
|
+
if ( value ) {
|
|
193
|
+
let commandSetValue = [ redis.cacheCommands.SET_VALUE, key, tools.stringifyJSON( value ) ];
|
|
194
|
+
if ( expiration ) {
|
|
195
|
+
commandSetValue.push( "EX" );
|
|
196
|
+
commandSetValue.push( expiration );
|
|
197
|
+
}
|
|
198
|
+
this.#redisClient.executeCommands( [ commandSetValue ] ).then( () => {
|
|
199
|
+
resolve( value );
|
|
200
|
+
} ).catch( ( error ) => {
|
|
201
|
+
reject( error );
|
|
202
|
+
} );
|
|
203
|
+
} else {
|
|
204
|
+
resolve( value );
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
208
|
+
}
|
|
209
|
+
} );
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Used to set multiple string values.
|
|
214
|
+
*
|
|
215
|
+
* @method
|
|
216
|
+
* @param {Object} keyValues
|
|
217
|
+
* @param {string} [prefix]
|
|
218
|
+
* @param {number} [expiration]
|
|
219
|
+
* @return {Promise}
|
|
220
|
+
* @public
|
|
221
|
+
*/
|
|
222
|
+
setValues( keyValues, prefix, expiration ) {
|
|
223
|
+
return new Promise( ( resolve, reject ) => {
|
|
224
|
+
if ( this.#isOperational === true ) {
|
|
225
|
+
if ( keyValues ) {
|
|
226
|
+
let commands = [];
|
|
227
|
+
_.forEach( keyValues, ( value, key ) => {
|
|
228
|
+
let commandSetValue = [ redis.cacheCommands.SET_VALUE, ( ( prefix ) ? prefix : "" ) + key, tools.stringifyJSON( value ) ];
|
|
229
|
+
if ( expiration ) {
|
|
230
|
+
commandSetValue.push( "EX" );
|
|
231
|
+
commandSetValue.push( expiration );
|
|
232
|
+
}
|
|
233
|
+
commands.push( commandSetValue );
|
|
234
|
+
} );
|
|
235
|
+
|
|
236
|
+
this.#redisClient.executeCommands( commands ).then( () => {
|
|
237
|
+
resolve( keyValues );
|
|
238
|
+
} ).catch( ( error ) => {
|
|
239
|
+
reject( error );
|
|
240
|
+
} );
|
|
241
|
+
} else {
|
|
242
|
+
resolve( keyValues );
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
246
|
+
}
|
|
247
|
+
} );
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Used to get a string value.
|
|
252
|
+
*
|
|
253
|
+
* @method
|
|
254
|
+
* @param {string} key
|
|
255
|
+
* @return {Promise}
|
|
256
|
+
* @public
|
|
257
|
+
*/
|
|
258
|
+
getValue( key ) {
|
|
259
|
+
return new Promise( ( resolve, reject ) => {
|
|
260
|
+
if ( this.#isOperational === true ) {
|
|
261
|
+
let commandGetValue = [ redis.cacheCommands.GET_VALUE, key ];
|
|
262
|
+
this.#redisClient.executeCommands( [ commandGetValue ] ).then( ( results ) => {
|
|
263
|
+
results = results[ 0 ];
|
|
264
|
+
resolve( ( results && results.length > 1 && _.isString( results[ 1 ] ) ) ? tools.parseJSON( results[ 1 ] ) : undefined );
|
|
265
|
+
} ).catch( ( error ) => {
|
|
266
|
+
reject( error );
|
|
267
|
+
} );
|
|
268
|
+
} else {
|
|
269
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
270
|
+
}
|
|
271
|
+
} );
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Used to get multiple string values.
|
|
276
|
+
*
|
|
277
|
+
* @method
|
|
278
|
+
* @param {string[]} keys
|
|
279
|
+
* @param {string} [prefix]
|
|
280
|
+
* @return {Promise}
|
|
281
|
+
* @public
|
|
282
|
+
*/
|
|
283
|
+
getValues( keys, prefix ) {
|
|
284
|
+
return new Promise( ( resolve, reject ) => {
|
|
285
|
+
if ( this.#isOperational === true ) {
|
|
286
|
+
let commands = [];
|
|
287
|
+
_.forEach( keys, ( key ) => {
|
|
288
|
+
commands.push( [ redis.cacheCommands.GET_VALUE, ( ( prefix ) ? prefix : "" ) + key ] );
|
|
289
|
+
} );
|
|
290
|
+
this.#redisClient.executeCommands( commands ).then( ( rawResults ) => {
|
|
291
|
+
let results = {};
|
|
292
|
+
_.forEach( rawResults, ( result, idx ) => {
|
|
293
|
+
results[ keys[ idx ] ] = ( results && results.length > 1 && _.isString( results[ 1 ] ) ) ? tools.parseJSON( results[ 1 ] ) : null;
|
|
294
|
+
} );
|
|
295
|
+
resolve( results );
|
|
296
|
+
} ).catch( ( error ) => {
|
|
297
|
+
reject( error );
|
|
298
|
+
} );
|
|
299
|
+
} else {
|
|
300
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
301
|
+
}
|
|
302
|
+
} );
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Used to delete a value / item.
|
|
307
|
+
*
|
|
308
|
+
* @method
|
|
309
|
+
* @param {string} key
|
|
310
|
+
* @returns {Promise<boolean>}
|
|
311
|
+
* @public
|
|
312
|
+
*/
|
|
313
|
+
deleteValue( key ) {
|
|
314
|
+
return new Promise( ( resolve, reject ) => {
|
|
315
|
+
if ( this.#isOperational === true ) {
|
|
316
|
+
let commandDeleteValue = [ redis.cacheCommands.DELETE_VALUE, key ];
|
|
317
|
+
this.#redisClient.executeCommands( [ commandDeleteValue ] ).then( ( results ) => {
|
|
318
|
+
results = results[ 0 ];
|
|
319
|
+
resolve( ( results && results.length > 1 ) ? results[ 1 ] : undefined );
|
|
320
|
+
} ).catch( ( error ) => {
|
|
321
|
+
reject( error );
|
|
322
|
+
} );
|
|
323
|
+
} else {
|
|
324
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
325
|
+
}
|
|
326
|
+
} );
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Used to set expiration in seconds to an existing key.
|
|
331
|
+
* <br/>
|
|
332
|
+
* NOTE: For performance optimization reasons, only use this only if the Redis command does not itself support the 'EX' argument.
|
|
333
|
+
*
|
|
334
|
+
* @method
|
|
335
|
+
* @param {string} key
|
|
336
|
+
* @param {number} seconds
|
|
337
|
+
* @param {string} [name] If you need to expire a field in a hash set instead, provide the name of the set here.
|
|
338
|
+
* @returns {Promise<number>} This will resolve with the seconds as provided initially by the caller.
|
|
339
|
+
* @public
|
|
340
|
+
*/
|
|
341
|
+
expireValue( key, seconds, name ) {
|
|
342
|
+
return new Promise( ( resolve, reject ) => {
|
|
343
|
+
if ( this.#isOperational === true ) {
|
|
344
|
+
let commandExpire = ( name ) ? [ redis.cacheCommands.HASH_EXPIRE, name, seconds, "FIELDS", 1, key ] : [ redis.cacheCommands.EXPIRE, key, seconds ];
|
|
345
|
+
this.#redisClient.executeCommands( [ commandExpire ] ).then( () => {
|
|
346
|
+
resolve( seconds );
|
|
347
|
+
} ).catch( ( error ) => {
|
|
348
|
+
reject( error );
|
|
349
|
+
} );
|
|
350
|
+
} else {
|
|
351
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
352
|
+
}
|
|
353
|
+
} );
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Used to add the specified values to a list.
|
|
358
|
+
*
|
|
359
|
+
* @method
|
|
360
|
+
* @param {string} listName
|
|
361
|
+
* @param {Object[]} values
|
|
362
|
+
* @returns {Promise<number>}
|
|
363
|
+
* @public
|
|
364
|
+
*/
|
|
365
|
+
listPushValue( listName, values ) {
|
|
366
|
+
return new Promise( ( resolve, reject ) => {
|
|
367
|
+
if ( this.#isOperational === true ) {
|
|
368
|
+
let commandPushValues = [ redis.cacheCommands.LIST_PUSH, listName ];
|
|
369
|
+
_.forEach( values, ( value ) => {
|
|
370
|
+
if ( value ) {
|
|
371
|
+
commandPushValues.push( tools.stringifyJSON( value ) );
|
|
372
|
+
}
|
|
373
|
+
} );
|
|
374
|
+
this.#redisClient.executeCommands( [ commandPushValues ] ).then( ( results ) => {
|
|
375
|
+
results = results[ 0 ];
|
|
376
|
+
resolve( ( results && results.length > 1 ) ? results[ 1 ] : undefined );
|
|
377
|
+
} ).catch( ( error ) => {
|
|
378
|
+
reject( error );
|
|
379
|
+
} );
|
|
380
|
+
} else {
|
|
381
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
382
|
+
}
|
|
383
|
+
} );
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Used to add the specified value to a set.
|
|
388
|
+
*
|
|
389
|
+
* @method
|
|
390
|
+
* @param {string} key
|
|
391
|
+
* @param {string|Object} value
|
|
392
|
+
* @returns {Promise}
|
|
393
|
+
* @public
|
|
394
|
+
*/
|
|
395
|
+
addToSet( key, value ) {
|
|
396
|
+
return new Promise( ( resolve, reject ) => {
|
|
397
|
+
if ( this.#isOperational === true ) {
|
|
398
|
+
let commandAddToSet = [ redis.cacheCommands.ADD_TO_SET, key, tools.stringifyJSON( value ) ];
|
|
399
|
+
this.#redisClient.executeCommands( [ commandAddToSet ] ).then( () => {
|
|
400
|
+
resolve();
|
|
401
|
+
} ).catch( ( error ) => {
|
|
402
|
+
reject( error );
|
|
403
|
+
} );
|
|
404
|
+
} else {
|
|
405
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
406
|
+
}
|
|
407
|
+
} );
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Used to add multiple values to multiple sets in one transactional request.
|
|
412
|
+
* <br/>
|
|
413
|
+
* NOTE: The two arrays of keys and values must have correct index relations (i.e., first pair on keys[0] and values[0] and so on)!
|
|
414
|
+
*
|
|
415
|
+
* @method
|
|
416
|
+
* @param {string[]} keys
|
|
417
|
+
* @param {string[]} values
|
|
418
|
+
* @returns {Promise}
|
|
419
|
+
* @public
|
|
420
|
+
*/
|
|
421
|
+
addToSetMulti( keys, values ) {
|
|
422
|
+
return new Promise( ( resolve, reject ) => {
|
|
423
|
+
if ( this.#isOperational === true ) {
|
|
424
|
+
let commands = [];
|
|
425
|
+
_.forEach( keys, ( key, idx ) => {
|
|
426
|
+
commands.push( [ redis.cacheCommands.ADD_TO_SET, key, tools.stringifyJSON( values[ idx ] ) ] );
|
|
427
|
+
} );
|
|
428
|
+
this.#redisClient.executeCommands( commands ).then( () => {
|
|
429
|
+
resolve();
|
|
430
|
+
} ).catch( ( error ) => {
|
|
431
|
+
reject( error );
|
|
432
|
+
} );
|
|
433
|
+
} else {
|
|
434
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
435
|
+
}
|
|
436
|
+
} );
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Used to check if the provided value is a member of the specified set.
|
|
441
|
+
*
|
|
442
|
+
* @method
|
|
443
|
+
* @param {string} setName
|
|
444
|
+
* @param {string} value
|
|
445
|
+
* @returns {Promise<boolean>}
|
|
446
|
+
* @public
|
|
447
|
+
*/
|
|
448
|
+
isSetMember( setName, value ) {
|
|
449
|
+
return new Promise( ( resolve, reject ) => {
|
|
450
|
+
if ( this.#isOperational === true ) {
|
|
451
|
+
let commandIsSetMember = [ redis.cacheCommands.IS_SET_MEMBER, setName, value ];
|
|
452
|
+
this.#redisClient.executeCommands( [ commandIsSetMember ] ).then( ( results ) => {
|
|
453
|
+
results = results[ 0 ];
|
|
454
|
+
let result = !!( results && results.length > 1 && results[ 1 ] === 1 );
|
|
455
|
+
resolve( result );
|
|
456
|
+
} ).catch( ( error ) => {
|
|
457
|
+
reject( error );
|
|
458
|
+
} );
|
|
459
|
+
} else {
|
|
460
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
461
|
+
}
|
|
462
|
+
} );
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Used to get all elements of a set.
|
|
467
|
+
*
|
|
468
|
+
* @method
|
|
469
|
+
* @param {string} key
|
|
470
|
+
* @returns {Promise<Object[]>}
|
|
471
|
+
* @public
|
|
472
|
+
*/
|
|
473
|
+
membersOfSet( key ) {
|
|
474
|
+
return new Promise( ( resolve, reject ) => {
|
|
475
|
+
if ( this.#isOperational === true ) {
|
|
476
|
+
let commandMembersOfSet = [ redis.cacheCommands.GET_ALL_FROM_SET, key ];
|
|
477
|
+
this.#redisClient.executeCommands( [ commandMembersOfSet ] ).then( ( results ) => {
|
|
478
|
+
results = results[ 0 ];
|
|
479
|
+
let parsedResults = ( results && results.length > 1 && results[ 1 ] ) ? results[ 1 ] : [];
|
|
480
|
+
resolve( parsedResults );
|
|
481
|
+
} ).catch( ( error ) => {
|
|
482
|
+
reject( error );
|
|
483
|
+
} );
|
|
484
|
+
} else {
|
|
485
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
486
|
+
}
|
|
487
|
+
} );
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Used to get a union of all elements in the list of sets.
|
|
492
|
+
*
|
|
493
|
+
* @method
|
|
494
|
+
* @param {string[]} keys
|
|
495
|
+
* @returns {Promise<Object[]>}
|
|
496
|
+
* @public
|
|
497
|
+
*/
|
|
498
|
+
unionOfSets( keys ) {
|
|
499
|
+
return new Promise( ( resolve, reject ) => {
|
|
500
|
+
if ( this.#isOperational === true ) {
|
|
501
|
+
let commandUnionOfSets = _.concat( [ redis.cacheCommands.UNION_OF_SETS ], keys );
|
|
502
|
+
this.#redisClient.executeCommands( [ commandUnionOfSets ] ).then( ( results ) => {
|
|
503
|
+
results = results[ 0 ];
|
|
504
|
+
let parsedResults = ( results && results.length > 1 && results[ 1 ] ) ? results[ 1 ] : [];
|
|
505
|
+
resolve( parsedResults );
|
|
506
|
+
} ).catch( ( error ) => {
|
|
507
|
+
reject( error );
|
|
508
|
+
} );
|
|
509
|
+
} else {
|
|
510
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
511
|
+
}
|
|
512
|
+
} );
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Used to set a single hash field.
|
|
517
|
+
*
|
|
518
|
+
* @method
|
|
519
|
+
* @deprecated
|
|
520
|
+
* @param {string} key
|
|
521
|
+
* @param {string} name
|
|
522
|
+
* @param {*} value
|
|
523
|
+
* @returns {Promise}
|
|
524
|
+
* @public
|
|
525
|
+
*/
|
|
526
|
+
hashSetField( key, name, value ) {
|
|
527
|
+
return new Promise( ( resolve, reject ) => {
|
|
528
|
+
if ( this.#isOperational === true ) {
|
|
529
|
+
let commandHashSetField = [ redis.cacheCommands.HASH_SET, key, name, tools.stringifyJSON( value ) ];
|
|
530
|
+
this.#redisClient.executeCommands( [ commandHashSetField ] ).then( () => {
|
|
531
|
+
resolve();
|
|
532
|
+
} ).catch( ( error ) => {
|
|
533
|
+
reject( error );
|
|
534
|
+
} );
|
|
535
|
+
} else {
|
|
536
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
537
|
+
}
|
|
538
|
+
} );
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Used to set multiple hash fields.
|
|
543
|
+
*
|
|
544
|
+
* @method
|
|
545
|
+
* @deprecated
|
|
546
|
+
* @param {string} key
|
|
547
|
+
* @param {Object[]} fields
|
|
548
|
+
* @param {string} fields[].name
|
|
549
|
+
* @param {*} fields[].value
|
|
550
|
+
* @returns {Promise}
|
|
551
|
+
* @public
|
|
552
|
+
*/
|
|
553
|
+
hashSetFields( key, fields ) {
|
|
554
|
+
return new Promise( ( resolve, reject ) => {
|
|
555
|
+
if ( this.#isOperational === true ) {
|
|
556
|
+
let commandHashSetFields = [ redis.cacheCommands.HASH_SET, key ];
|
|
557
|
+
_.forEach( fields, ( field ) => {
|
|
558
|
+
commandHashSetFields.push( field.name );
|
|
559
|
+
commandHashSetFields.push( tools.stringifyJSON( field.value ) );
|
|
560
|
+
} );
|
|
561
|
+
this.#redisClient.executeCommands( [ commandHashSetFields ] ).then( () => {
|
|
562
|
+
resolve();
|
|
563
|
+
} ).catch( ( error ) => {
|
|
564
|
+
reject( error );
|
|
565
|
+
} );
|
|
566
|
+
} else {
|
|
567
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
568
|
+
}
|
|
569
|
+
} );
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Used to get a single field from a hash.
|
|
574
|
+
*
|
|
575
|
+
* @method
|
|
576
|
+
* @param {string} key
|
|
577
|
+
* @param {string} field
|
|
578
|
+
* @return {Promise}
|
|
579
|
+
* @public
|
|
580
|
+
*/
|
|
581
|
+
hashGetField( key, field ) {
|
|
582
|
+
return new Promise( ( resolve, reject ) => {
|
|
583
|
+
if ( this.#isOperational === true ) {
|
|
584
|
+
let commandHashGetField = [ redis.cacheCommands.HASH_GET, key, field ];
|
|
585
|
+
this.#redisClient.executeCommands( [ commandHashGetField ] ).then( ( results ) => {
|
|
586
|
+
results = results[ 0 ];
|
|
587
|
+
resolve( ( results && results.length > 1 && _.isString( results[ 1 ] ) ) ? tools.parseJSON( results[ 1 ] ) : null );
|
|
588
|
+
} ).catch( ( error ) => {
|
|
589
|
+
reject( error );
|
|
590
|
+
} );
|
|
591
|
+
} else {
|
|
592
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
593
|
+
}
|
|
594
|
+
} );
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Used to remove a single field from a hash.
|
|
599
|
+
*
|
|
600
|
+
* @method
|
|
601
|
+
* @param {string} key
|
|
602
|
+
* @param {string} field
|
|
603
|
+
* @return {Promise<boolean>} Will return 'true' if the field was removed, 'false' otherwise.
|
|
604
|
+
* @public
|
|
605
|
+
*/
|
|
606
|
+
hashDeleteField( key, field ) {
|
|
607
|
+
return new Promise( ( resolve, reject ) => {
|
|
608
|
+
if ( this.#isOperational === true ) {
|
|
609
|
+
let commandHashGetField = [ redis.cacheCommands.HASH_REMOVE, key, field ];
|
|
610
|
+
this.#redisClient.executeCommands( [ commandHashGetField ] ).then( ( results ) => {
|
|
611
|
+
results = results[ 0 ];
|
|
612
|
+
resolve( ( results && results.length > 1 ) ? tools.toBool( results[ 1 ] ) : false );
|
|
613
|
+
} ).catch( ( error ) => {
|
|
614
|
+
reject( error );
|
|
615
|
+
} );
|
|
616
|
+
} else {
|
|
617
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
618
|
+
}
|
|
619
|
+
} );
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Used to store a JSON variable.
|
|
624
|
+
* <br/>
|
|
625
|
+
* NOTE: Requires ReJSON module installed on server to work.
|
|
626
|
+
*
|
|
627
|
+
* @method
|
|
628
|
+
* @param {string} key
|
|
629
|
+
* @param {Object} value
|
|
630
|
+
* @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
|
|
631
|
+
* @param {number} [overrideMode=0] By default this allows full override for existing keys.
|
|
632
|
+
* Option 1 will set the key only if it doesn't already exist. Option 2 will set it only if it already exists.
|
|
633
|
+
* @returns {Promise}
|
|
634
|
+
* @public
|
|
635
|
+
*/
|
|
636
|
+
setJSON( key, value, path = "$", overrideMode = 0 ) {
|
|
637
|
+
return new Promise( ( resolve, reject ) => {
|
|
638
|
+
if ( this.#isOperational === true ) {
|
|
639
|
+
if ( this.#redisClient.isJSONSupported ) {
|
|
640
|
+
let commandArguments = [ redis.cacheCommands.JSON_SET, key, this.#normalizeJSONPath( path ), tools.stringifyJSON( value ) ];
|
|
641
|
+
if ( overrideMode !== 0 ) {
|
|
642
|
+
commandArguments.push( overrideMode === 1 ? redis.cacheOverrideMode.NX : redis.cacheOverrideMode.XX );
|
|
643
|
+
}
|
|
644
|
+
this.#redisClient.callCommand( commandArguments ).then( () => {
|
|
645
|
+
resolve();
|
|
646
|
+
} ).catch( ( error ) => {
|
|
647
|
+
reject( error );
|
|
648
|
+
} );
|
|
649
|
+
} else {
|
|
650
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
|
|
651
|
+
}
|
|
652
|
+
} else {
|
|
653
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
654
|
+
}
|
|
655
|
+
} );
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Used to fetch a JSON variable.
|
|
660
|
+
* <br/>
|
|
661
|
+
* NOTE: Requires ReJSON module installed on server to work.
|
|
662
|
+
*
|
|
663
|
+
* @method
|
|
664
|
+
* @param {string} key
|
|
665
|
+
* @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
|
|
666
|
+
* @returns {Promise<Object>}
|
|
667
|
+
* @public
|
|
668
|
+
*/
|
|
669
|
+
getJSON( key, path = "$" ) {
|
|
670
|
+
return new Promise( ( resolve, reject ) => {
|
|
671
|
+
if ( this.#isOperational === true ) {
|
|
672
|
+
if ( this.#redisClient.isJSONSupported ) {
|
|
673
|
+
let commandArguments = [ redis.cacheCommands.JSON_GET, key, this.#normalizeJSONPath( path ) ];
|
|
674
|
+
this.#redisClient.callCommand( commandArguments ).then( ( result ) => {
|
|
675
|
+
resolve( result != null ? tools.parseJSON( String( result ) ) : null );
|
|
676
|
+
} ).catch( ( error ) => {
|
|
677
|
+
reject( error );
|
|
678
|
+
} );
|
|
679
|
+
} else {
|
|
680
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
|
|
681
|
+
}
|
|
682
|
+
} else {
|
|
683
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
684
|
+
}
|
|
685
|
+
} );
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Used to update/edit an existing JSON variable.
|
|
690
|
+
* <br/>
|
|
691
|
+
* NOTE: Requires ReJSON module installed on server to work.
|
|
692
|
+
*
|
|
693
|
+
* @method
|
|
694
|
+
* @param {string} key
|
|
695
|
+
* @param {Object} value
|
|
696
|
+
* @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
|
|
697
|
+
* @returns {Promise}
|
|
698
|
+
* @public
|
|
699
|
+
*/
|
|
700
|
+
editJSON( key, value, path = "$" ) {
|
|
701
|
+
return new Promise( ( resolve, reject ) => {
|
|
702
|
+
if ( this.#isOperational === true ) {
|
|
703
|
+
if ( this.#redisClient.isJSONSupported ) {
|
|
704
|
+
let commandArguments = [ redis.cacheCommands.JSON_MERGE, key, this.#normalizeJSONPath( path ), tools.stringifyJSON( value ) ];
|
|
705
|
+
this.#redisClient.callCommand( commandArguments ).then( () => {
|
|
706
|
+
resolve();
|
|
707
|
+
} ).catch( ( error ) => {
|
|
708
|
+
reject( error );
|
|
709
|
+
} );
|
|
710
|
+
} else {
|
|
711
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
|
|
712
|
+
}
|
|
713
|
+
} else {
|
|
714
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
715
|
+
}
|
|
716
|
+
} );
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Used to add an item to a JSON array. That array needs to exist already.
|
|
721
|
+
* <br/>
|
|
722
|
+
* NOTE: Requires ReJSON module installed on server to work.
|
|
723
|
+
*
|
|
724
|
+
* @method
|
|
725
|
+
* @param {string} key
|
|
726
|
+
* @param {Object} value
|
|
727
|
+
* @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
|
|
728
|
+
* @returns {Promise}
|
|
729
|
+
* @public
|
|
730
|
+
*/
|
|
731
|
+
arrayAppendJSON( key, value, path = "$" ) {
|
|
732
|
+
return new Promise( ( resolve, reject ) => {
|
|
733
|
+
if ( this.#isOperational === true ) {
|
|
734
|
+
if ( this.#redisClient.isJSONSupported ) {
|
|
735
|
+
let commandArguments = [ redis.cacheCommands.JSON_ARRAY_APPEND, key, this.#normalizeJSONPath( path ), tools.stringifyJSON( value ) ];
|
|
736
|
+
this.#redisClient.callCommand( commandArguments ).then( () => {
|
|
737
|
+
resolve();
|
|
738
|
+
} ).catch( ( error ) => {
|
|
739
|
+
reject( error );
|
|
740
|
+
} );
|
|
741
|
+
} else {
|
|
742
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
|
|
743
|
+
}
|
|
744
|
+
} else {
|
|
745
|
+
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
|
|
746
|
+
}
|
|
747
|
+
} );
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/* Private interface */
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Used to normalize a JSON path.
|
|
754
|
+
* <br/>
|
|
755
|
+
* NOTE: If "path" is an array, each element is treated as a literal key name and encoded with bracket notation,
|
|
756
|
+
* which correctly handles key names that contain dots or other JSONPath special characters.
|
|
757
|
+
*
|
|
758
|
+
* @method
|
|
759
|
+
* @param {string|string[]} path
|
|
760
|
+
* @returns {string}
|
|
761
|
+
* @private
|
|
762
|
+
*/
|
|
763
|
+
#normalizeJSONPath( path ) {
|
|
764
|
+
if ( Array.isArray( path ) ) {
|
|
765
|
+
return "$" + path.map( ( segment ) => `["${ String( segment ).replace( /\\/g, "\\\\" ).replace( /"/g, '\\"' ) }"]` ).join( "" );
|
|
766
|
+
}
|
|
767
|
+
return ( path.startsWith( "$" ) === false ) ? ( "$." + path ) : path;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
const instance = new CommonMemoryCache();
|
|
773
773
|
module.exports.instance = Object.freeze( instance );
|