@mojaloop/sdk-scheme-adapter 13.0.4 → 15.0.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/.circleci/config.yml +1 -1
- package/.nvmrc +1 -1
- package/CHANGELOG.md +24 -0
- package/Dockerfile +2 -2
- package/audit-resolve.json +48 -457
- package/docs/dfspInboundApi.yaml +11 -0
- package/package.json +27 -32
- package/src/ControlAgent/index.js +8 -11
- package/src/ControlServer/index.js +13 -13
- package/src/InboundServer/index.js +12 -61
- package/src/OAuthTestServer/index.js +0 -13
- package/src/OutboundServer/api.yaml +370 -44
- package/src/OutboundServer/index.js +11 -54
- package/src/TestServer/index.js +6 -33
- package/src/config.js +1 -4
- package/src/index.js +163 -146
- package/src/lib/cache.js +93 -186
- package/src/lib/metrics.js +1 -1
- package/src/lib/model/InboundTransfersModel.js +10 -6
- package/src/lib/model/OutboundTransfersModel.js +1 -1
- package/test/__mocks__/redis.js +51 -26
- package/test/config/integration.env +1 -2
- package/test/integration/lib/cache.test.js +1 -2
- package/test/integration/testEnv.js +1 -4
- package/test/unit/ControlClient.test.js +1 -45
- package/test/unit/ControlServer/index.js +18 -22
- package/test/unit/ControlServer.test.js +0 -60
- package/test/unit/InboundServer.test.js +8 -8
- package/test/unit/TestServer.test.js +1 -1
- package/test/unit/api/accounts/accounts.test.js +2 -2
- package/test/unit/api/transfers/transfers.test.js +1 -1
- package/test/unit/api/utils.js +12 -4
- package/test/unit/config.test.js +1 -2
- package/test/unit/data/defaultConfig.json +1 -5
- package/test/unit/index.test.js +5 -64
- package/test/unit/lib/cache.test.js +5 -6
- package/test/unit/lib/model/AccountsModel.test.js +3 -4
- package/test/unit/lib/model/InboundTransfersModel.test.js +55 -16
- package/test/unit/lib/model/OutboundBulkQuotesModel.test.js +3 -4
- package/test/unit/lib/model/OutboundBulkTransfersModel.test.js +1 -2
- package/test/unit/lib/model/OutboundRequestToPayModel.test.js +3 -4
- package/test/unit/lib/model/OutboundRequestToPayTransferModel.test.js +3 -4
- package/test/unit/lib/model/OutboundTransfersModel.test.js +2 -3
- package/test/unit/lib/model/common/PersistentStateMachine.test.js +3 -4
- package/test/unit/lib/model/data/defaultConfig.json +1 -4
- package/test/unit/lib/model/data/notificationAbortedToPayee.json +10 -0
- package/test/unit/lib/model/data/notificationReservedToPayee.json +10 -0
package/src/lib/cache.js
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
-
const util = require('util');
|
|
14
13
|
const redis = require('redis');
|
|
15
14
|
|
|
16
15
|
const CONN_ST = {
|
|
@@ -27,11 +26,12 @@ class Cache {
|
|
|
27
26
|
constructor(config) {
|
|
28
27
|
this._config = config;
|
|
29
28
|
|
|
30
|
-
if(!config.
|
|
31
|
-
throw new Error('Cache config requires
|
|
29
|
+
if(!config.cacheUrl || !config.logger) {
|
|
30
|
+
throw new Error('Cache config requires cacheUrl and logger properties');
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
this._logger = config.logger;
|
|
34
|
+
this._url = config.cacheUrl;
|
|
35
35
|
|
|
36
36
|
// a redis connection to handle get, set and publish operations
|
|
37
37
|
this._client = null;
|
|
@@ -78,9 +78,6 @@ class Cache {
|
|
|
78
78
|
this._inProgressConnection = Promise.all([this._getClient(), this._getClient()]);
|
|
79
79
|
[this._client, this._subscriptionClient] = await this._inProgressConnection;
|
|
80
80
|
|
|
81
|
-
// hook up our sub message handler
|
|
82
|
-
this._subscriptionClient.on('message', this._onMessage.bind(this));
|
|
83
|
-
|
|
84
81
|
if (this._config.enableTestFeatures) {
|
|
85
82
|
this.setTestMode(true);
|
|
86
83
|
}
|
|
@@ -104,7 +101,7 @@ class Cache {
|
|
|
104
101
|
this._logger
|
|
105
102
|
.push({ 'notify-keyspace-events': mode })
|
|
106
103
|
.log('Configuring Redis to emit keyevent events');
|
|
107
|
-
this._client.
|
|
104
|
+
await this._client.configSet('notify-keyspace-events', mode);
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
async disconnect() {
|
|
@@ -124,8 +121,8 @@ class Cache {
|
|
|
124
121
|
}
|
|
125
122
|
this._connectionState = CONN_ST.DISCONNECTING;
|
|
126
123
|
this._inProgressDisconnection = Promise.all([
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
this._client.quit(),
|
|
125
|
+
this._subscriptionClient.quit()
|
|
129
126
|
]);
|
|
130
127
|
this._client = null;
|
|
131
128
|
this._subscriptionClient = null;
|
|
@@ -143,30 +140,36 @@ class Cache {
|
|
|
143
140
|
* @returns {Promise} - Promise that resolves with an integer callback Id to submit in unsubscribe request
|
|
144
141
|
*/
|
|
145
142
|
async subscribe(channel, callback) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
143
|
+
// get an id for this callback
|
|
144
|
+
const id = this._callbackId++;
|
|
145
|
+
|
|
146
|
+
if(!this._callbacks[channel]) {
|
|
147
|
+
// if this is the first subscriber for this channel we init the hashmap
|
|
148
|
+
this._callbacks[channel] = { [id]: callback };
|
|
149
|
+
await this._subscriptionClient.subscribe(channel, (msg) => {
|
|
150
|
+
// we have some callbacks to make
|
|
151
|
+
for (const [id, cb] of Object.entries(this._callbacks[channel])) {
|
|
152
|
+
this._logger.log(`Cache message received on channel ${channel}. Making callback with id ${id}`);
|
|
153
|
+
|
|
154
|
+
// call the callback with the channel name, message and callbackId...
|
|
155
|
+
// ...(which is useful for unsubscribe)
|
|
156
|
+
try {
|
|
157
|
+
cb(channel, msg, id);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
this._logger
|
|
160
|
+
.push({ callbackId: id, err })
|
|
161
|
+
.log('Unhandled error in cache subscription handler');
|
|
162
|
+
}
|
|
158
163
|
}
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
this._callbacks[channel][id] = callback;
|
|
167
|
+
}
|
|
159
168
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// store the callback against the channel/id
|
|
164
|
-
this._callbacks[channel][id] = callback;
|
|
169
|
+
// store the callback against the channel/id
|
|
170
|
+
this._logger.log(`Subscribed to cache pub/sub channel ${channel}`);
|
|
165
171
|
|
|
166
|
-
|
|
167
|
-
return resolve(id);
|
|
168
|
-
});
|
|
169
|
-
});
|
|
172
|
+
return id;
|
|
170
173
|
}
|
|
171
174
|
|
|
172
175
|
|
|
@@ -177,88 +180,59 @@ class Cache {
|
|
|
177
180
|
* @param callbackId {integer} - id of the callback to remove
|
|
178
181
|
*/
|
|
179
182
|
async unsubscribe(channel, callbackId) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return resolve();
|
|
183
|
+
if(this._callbacks[channel] && this._callbacks[channel][callbackId]) {
|
|
184
|
+
delete this._callbacks[channel][callbackId];
|
|
185
|
+
this._logger.log(`Cache unsubscribed callbackId ${callbackId} from channel ${channel}`);
|
|
186
|
+
|
|
187
|
+
if(Object.keys(this._callbacks[channel]).length < 1) {
|
|
188
|
+
//no more callbacks for this channel
|
|
189
|
+
delete this._callbacks[channel];
|
|
190
|
+
await this._subscriptionClient.unsubscribe(channel);
|
|
191
191
|
}
|
|
192
|
-
|
|
192
|
+
} else {
|
|
193
193
|
// we should not be asked to unsubscribe from a subscription we do not have. Raise this as a promise
|
|
194
194
|
// rejection so it can be spotted. It may indiate a logic bug somewhere else
|
|
195
195
|
this._logger.log(`Cache not subscribed to channel ${channel} for callbackId ${callbackId}`);
|
|
196
|
-
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Handler for published messages
|
|
203
|
-
*/
|
|
204
|
-
async _onMessage(channel, msg) {
|
|
205
|
-
if(this._callbacks[channel]) {
|
|
206
|
-
// we have some callbacks to make
|
|
207
|
-
Object.keys(this._callbacks[channel]).forEach(k => {
|
|
208
|
-
this._logger.log(`Cache message received on channel ${channel}. Making callback with id ${k}`);
|
|
209
|
-
|
|
210
|
-
// call the callback with the channel name, message and callbackId...
|
|
211
|
-
// ...(which is useful for unsubscribe)
|
|
212
|
-
try {
|
|
213
|
-
this._callbacks[channel][k](channel, msg, k);
|
|
214
|
-
} catch (err) {
|
|
215
|
-
this._logger
|
|
216
|
-
.push({ callbackId: k, err })
|
|
217
|
-
.log('Unhandled error in cache subscription handler');
|
|
218
|
-
}
|
|
219
|
-
});
|
|
196
|
+
throw new Error(`Channel ${channel} does not have a callback with id ${callbackId} subscribed`);
|
|
220
197
|
}
|
|
221
198
|
}
|
|
222
199
|
|
|
223
|
-
|
|
224
200
|
/**
|
|
225
201
|
* Returns a new redis client
|
|
226
202
|
*
|
|
227
203
|
* @returns {object} - a connected REDIS client
|
|
228
204
|
* */
|
|
229
205
|
async _getClient() {
|
|
230
|
-
|
|
231
|
-
const client = redis.createClient(this._config);
|
|
206
|
+
const client = redis.createClient({ url: this._url });
|
|
232
207
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
});
|
|
208
|
+
client.on('error', (err) => {
|
|
209
|
+
this._logger.push({ err }).log('Error from REDIS client getting subscriber');
|
|
210
|
+
});
|
|
237
211
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
});
|
|
212
|
+
client.on('reconnecting', (err) => {
|
|
213
|
+
this._logger.push({ err }).log('REDIS client Reconnecting');
|
|
214
|
+
});
|
|
242
215
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
216
|
+
client.on('subscribe', (channel, count) => {
|
|
217
|
+
this._logger.push({ channel, count }).log('REDIS client subscribe');
|
|
218
|
+
// On a subscribe event, ensure that testFeatures are enabled.
|
|
219
|
+
// This is required here in the advent of a disconnect/reconnect event. Redis client will re-subscribe all subscriptions, but previously enabledTestFeatures will be lost.
|
|
220
|
+
// Handling this on the on subscribe event will ensure its always configured.
|
|
221
|
+
if (this._config.enableTestFeatures) {
|
|
222
|
+
this.setTestMode(true);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
252
225
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
226
|
+
client.on('connect', () => {
|
|
227
|
+
this._logger.log(`REDIS client connected at: ${this._url}`);
|
|
228
|
+
});
|
|
256
229
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return resolve(client);
|
|
260
|
-
});
|
|
230
|
+
client.on('ready', () => {
|
|
231
|
+
this._logger.log(`Connected to REDIS at: ${this._url}`);
|
|
261
232
|
});
|
|
233
|
+
await client.connect();
|
|
234
|
+
|
|
235
|
+
return client;
|
|
262
236
|
}
|
|
263
237
|
|
|
264
238
|
|
|
@@ -270,23 +244,11 @@ class Cache {
|
|
|
270
244
|
* @returns {Promise} - Promise that will resolve with redis replies or reject with an error
|
|
271
245
|
*/
|
|
272
246
|
async publish(channelName, value) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
// note that we publish on the non-SUBSCRIBE connection
|
|
280
|
-
this._client.publish(channelName, value, (err, replies) => {
|
|
281
|
-
if(err) {
|
|
282
|
-
this._logger.push({ channelName, err }).log(`Error publishing to channel ${channelName}`);
|
|
283
|
-
return reject(err);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
this._logger.push({ channelName, value }).log(`Published to channel ${channelName}`);
|
|
287
|
-
return resolve(replies);
|
|
288
|
-
});
|
|
289
|
-
});
|
|
247
|
+
if(typeof(value) !== 'string') {
|
|
248
|
+
// ALWAYS publish string values
|
|
249
|
+
value = JSON.stringify(value);
|
|
250
|
+
}
|
|
251
|
+
await this._client.publish(channelName, value);
|
|
290
252
|
}
|
|
291
253
|
|
|
292
254
|
|
|
@@ -297,22 +259,11 @@ class Cache {
|
|
|
297
259
|
* @param value {stirng} - cache value
|
|
298
260
|
*/
|
|
299
261
|
async set(key, value) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
this._client.set(key, value, (err, replies) => {
|
|
307
|
-
if(err) {
|
|
308
|
-
this._logger.push({ key, value, err }).log(`Error setting cache key: ${key}`);
|
|
309
|
-
return reject(err);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
this._logger.push({ key, value, replies }).log(`Set cache key: ${key}`);
|
|
313
|
-
return resolve(replies);
|
|
314
|
-
});
|
|
315
|
-
});
|
|
262
|
+
//if we are given an object, turn it into a string
|
|
263
|
+
if(typeof(value) !== 'string') {
|
|
264
|
+
value = JSON.stringify(value);
|
|
265
|
+
}
|
|
266
|
+
await this._client.set(key, value);
|
|
316
267
|
}
|
|
317
268
|
|
|
318
269
|
/**
|
|
@@ -322,22 +273,11 @@ class Cache {
|
|
|
322
273
|
* @param value {string} - cache value
|
|
323
274
|
*/
|
|
324
275
|
async add(key, value) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
this._client.sadd(key, value, (err, replies) => {
|
|
332
|
-
if(err) {
|
|
333
|
-
this._logger.push({ key, value, err }).log(`Error setting cache key: ${key}`);
|
|
334
|
-
return reject(err);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
this._logger.push({ key, value, replies }).log(`Add cache key: ${key}`);
|
|
338
|
-
return resolve(replies);
|
|
339
|
-
});
|
|
340
|
-
});
|
|
276
|
+
//if we are given an object, turn it into a string
|
|
277
|
+
if(typeof(value) !== 'string') {
|
|
278
|
+
value = JSON.stringify(value);
|
|
279
|
+
}
|
|
280
|
+
await this._client.sAdd(key, value);
|
|
341
281
|
}
|
|
342
282
|
|
|
343
283
|
/**
|
|
@@ -346,28 +286,7 @@ class Cache {
|
|
|
346
286
|
* @param key {string} - cache key
|
|
347
287
|
*/
|
|
348
288
|
async members(key) {
|
|
349
|
-
return
|
|
350
|
-
this._client.smembers(key, (err, value) => {
|
|
351
|
-
if(err) {
|
|
352
|
-
this._logger.push({ key, err }).log(`Error getting cache key: ${key}`);
|
|
353
|
-
return reject(err);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
this._logger.push({ key, value }).log(`Got cache key: ${key}`);
|
|
357
|
-
|
|
358
|
-
if(typeof(value) === 'string') {
|
|
359
|
-
try {
|
|
360
|
-
value = JSON.parse(value);
|
|
361
|
-
}
|
|
362
|
-
catch(err) {
|
|
363
|
-
this._logger.push({ err }).log('Error parsing JSON cache value');
|
|
364
|
-
return reject(err);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
return resolve(value);
|
|
369
|
-
});
|
|
370
|
-
});
|
|
289
|
+
return this._client.sMembers(key);
|
|
371
290
|
}
|
|
372
291
|
|
|
373
292
|
/**
|
|
@@ -376,28 +295,16 @@ class Cache {
|
|
|
376
295
|
* @param key {string} - cache key
|
|
377
296
|
*/
|
|
378
297
|
async get(key) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
try {
|
|
390
|
-
value = JSON.parse(value);
|
|
391
|
-
}
|
|
392
|
-
catch(err) {
|
|
393
|
-
this._logger.push({ err }).log('Error parsing JSON cache value');
|
|
394
|
-
return reject(err);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return resolve(value);
|
|
399
|
-
});
|
|
400
|
-
});
|
|
298
|
+
let value = await this._client.get(key);
|
|
299
|
+
if(typeof(value) === 'string') {
|
|
300
|
+
try {
|
|
301
|
+
value = JSON.parse(value);
|
|
302
|
+
}
|
|
303
|
+
catch(err) {
|
|
304
|
+
this._logger.push({ err }).log('Error parsing JSON cache value');
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return value;
|
|
401
308
|
}
|
|
402
309
|
}
|
|
403
310
|
|
package/src/lib/metrics.js
CHANGED
|
@@ -134,7 +134,7 @@ class MetricsServer {
|
|
|
134
134
|
this._logger.log('Metrics request received');
|
|
135
135
|
|
|
136
136
|
ctx.response.set('Content-Type', this._prometheusRegister.contentType);
|
|
137
|
-
ctx.response.body = this._prometheusRegister.metrics();
|
|
137
|
+
ctx.response.body = await this._prometheusRegister.metrics();
|
|
138
138
|
});
|
|
139
139
|
|
|
140
140
|
return result;
|
|
@@ -389,9 +389,9 @@ class InboundTransfersModel {
|
|
|
389
389
|
|
|
390
390
|
// create a mojaloop transfer fulfil response
|
|
391
391
|
const mojaloopResponse = {
|
|
392
|
-
completedTimestamp: new Date(),
|
|
393
|
-
transferState: this._reserveNotification ?
|
|
394
|
-
fulfilment: fulfilment,
|
|
392
|
+
completedTimestamp: response.completedTimestamp || new Date(),
|
|
393
|
+
transferState: response.transferState || (this._reserveNotification ? TransferStateEnum.RESERVED : TransferStateEnum.COMPLETED),
|
|
394
|
+
fulfilment: response.fulfilment || fulfilment,
|
|
395
395
|
...response.extensionList && {
|
|
396
396
|
extensionList: {
|
|
397
397
|
extension: response.extensionList,
|
|
@@ -406,7 +406,7 @@ class InboundTransfersModel {
|
|
|
406
406
|
headers: res.originalRequest.headers,
|
|
407
407
|
body: res.originalRequest.body,
|
|
408
408
|
};
|
|
409
|
-
this.data.currentState = this._reserveNotification ? TransferStateEnum.RESERVED : TransferStateEnum.COMPLETED;
|
|
409
|
+
this.data.currentState = response.transferState || (this._reserveNotification ? TransferStateEnum.RESERVED : TransferStateEnum.COMPLETED);
|
|
410
410
|
await this._save();
|
|
411
411
|
return res;
|
|
412
412
|
} catch(err) {
|
|
@@ -788,10 +788,14 @@ class InboundTransfersModel {
|
|
|
788
788
|
// if the transfer was successful in the switch, set the overall transfer state to COMPLETED
|
|
789
789
|
this.data.currentState = TransferStateEnum.COMPLETED;
|
|
790
790
|
}
|
|
791
|
+
else if(body.transferState === 'ABORTED') {
|
|
792
|
+
// if the transfer was ABORTED in the switch, set the overall transfer state to ABORTED
|
|
793
|
+
this.data.currentState = TransferStateEnum.ABORTED;
|
|
794
|
+
}
|
|
791
795
|
else {
|
|
792
796
|
// if the final notification has anything other than COMMITTED as the final state, set an error
|
|
793
797
|
// in the transfer state.
|
|
794
|
-
this.data.currentState
|
|
798
|
+
this.data.currentState = TransferStateEnum.ERROR_OCCURED;
|
|
795
799
|
this.data.lastError = 'Final notification state not COMMITTED';
|
|
796
800
|
}
|
|
797
801
|
|
|
@@ -800,7 +804,7 @@ class InboundTransfersModel {
|
|
|
800
804
|
const res = await this._backendRequests.putTransfersNotification(this.data, transferId);
|
|
801
805
|
return res;
|
|
802
806
|
} catch (err) {
|
|
803
|
-
this._logger.push({ err }).log(
|
|
807
|
+
this._logger.push({ err }).log(`Error notifying backend of final transfer state equal to: ${body.transferState}`);
|
|
804
808
|
}
|
|
805
809
|
}
|
|
806
810
|
|
|
@@ -163,7 +163,7 @@ class OutboundTransfersModel {
|
|
|
163
163
|
if(!this.data.hasOwnProperty('direction')) {
|
|
164
164
|
this.data.direction = 'OUTBOUND';
|
|
165
165
|
}
|
|
166
|
-
if(this.data.skipPartyLookup &&
|
|
166
|
+
if(this.data.skipPartyLookup && !this.data.to.fspId) {
|
|
167
167
|
throw new Error('fspId of to party must be specific id when skipPartyLookup is truthy');
|
|
168
168
|
}
|
|
169
169
|
|
package/test/__mocks__/redis.js
CHANGED
|
@@ -1,53 +1,78 @@
|
|
|
1
1
|
/**************************************************************************
|
|
2
|
-
* (C) Copyright ModusBox Inc.
|
|
2
|
+
* (C) Copyright ModusBox Inc. 2020 - All rights reserved. *
|
|
3
3
|
* *
|
|
4
4
|
* This file is made available under the terms of the license agreement *
|
|
5
5
|
* specified in the corresponding source code repository. *
|
|
6
6
|
* *
|
|
7
7
|
* ORIGINAL AUTHOR: *
|
|
8
|
-
*
|
|
8
|
+
* Yevhen Kyriukha - yevhen.kyriukha@modusbox.com *
|
|
9
9
|
**************************************************************************/
|
|
10
10
|
|
|
11
|
-
'use strict';
|
|
12
|
-
|
|
13
11
|
const redisMock = require('redis-mock');
|
|
12
|
+
const { promisify } = require('util');
|
|
13
|
+
|
|
14
|
+
const { EventEmitter } = require('events');
|
|
15
|
+
|
|
16
|
+
const events = {};
|
|
14
17
|
|
|
15
|
-
// redis-mock currently ignores callback
|
|
18
|
+
// redis-mock currently ignores callback arguments, the following class fixes that
|
|
16
19
|
class RedisClient extends redisMock.RedisClient {
|
|
17
|
-
constructor(
|
|
18
|
-
super(
|
|
20
|
+
constructor(opts) {
|
|
21
|
+
super(opts);
|
|
22
|
+
events[opts.cacheUrl] = events[opts.cacheUrl] || new EventEmitter();
|
|
23
|
+
this.events = events[opts.cacheUrl];
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const argList = Array.prototype.slice.call(args, 0, args.length - 1);
|
|
25
|
-
callback(null, argList);
|
|
26
|
-
}
|
|
26
|
+
async subscribe(...args) {
|
|
27
|
+
this.events.on(...args);
|
|
28
|
+
// return promisify(super.subscribe.bind(this))(...args);
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
this.
|
|
31
|
+
async unsubscribe(channel) {
|
|
32
|
+
this.events.removeAllListeners(channel);
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this._executeCallback(...args);
|
|
35
|
+
async publish(...args) {
|
|
36
|
+
process.nextTick(() => this.events.emit(...args));
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
super.
|
|
40
|
-
this._executeCallback(...args);
|
|
39
|
+
set(...args) {
|
|
40
|
+
return promisify(super.set.bind(this))(...args);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
super.
|
|
45
|
-
|
|
43
|
+
get(...args) {
|
|
44
|
+
return promisify(super.get.bind(this))(...args);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
keys(...args) {
|
|
48
|
+
return promisify(super.keys.bind(this))(...args);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
end() {
|
|
52
|
+
this.events.removeAllListeners();
|
|
46
53
|
}
|
|
47
|
-
}
|
|
48
54
|
|
|
55
|
+
connect() {}
|
|
49
56
|
|
|
57
|
+
async disconnect() {
|
|
58
|
+
return this.end();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async quit() {
|
|
62
|
+
return this.end();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
sAdd(...args) {
|
|
66
|
+
return promisify(super.sadd.bind(this))(...args);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
sMembers(...args) {
|
|
70
|
+
return promisify(super.smembers.bind(this))(...args);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
configSet() {}
|
|
74
|
+
}
|
|
50
75
|
|
|
51
76
|
module.exports = {
|
|
52
|
-
createClient: (
|
|
77
|
+
createClient: (opts) => new RedisClient(opts),
|
|
53
78
|
};
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
const ControlAgent = require('~/ControlAgent');
|
|
3
3
|
const TestControlServer = require('./ControlServer');
|
|
4
|
-
const InboundServer = require('~/InboundServer');
|
|
5
|
-
const OutboundServer = require('~/OutboundServer');
|
|
6
|
-
const TestServer = require('~/TestServer');
|
|
7
|
-
const defaultConfig = require('./data/defaultConfig.json');
|
|
8
4
|
const { Logger } = require('@mojaloop/sdk-standard-components');
|
|
9
5
|
|
|
10
6
|
jest.mock('~/lib/cache');
|
|
11
|
-
const Cache = require('~/lib/cache');
|
|
12
7
|
|
|
13
8
|
// TODO:
|
|
14
9
|
// - diff against master to determine what else needs testing
|
|
@@ -66,48 +61,9 @@ describe('ControlAgent', () => {
|
|
|
66
61
|
const newConfigEvent = new Promise(
|
|
67
62
|
(resolve) => client.on(ControlAgent.EVENT.RECONFIGURE, resolve)
|
|
68
63
|
);
|
|
69
|
-
|
|
64
|
+
server.broadcastConfigChange(changedConfig);
|
|
70
65
|
const newConfEventData = await newConfigEvent;
|
|
71
66
|
expect(newConfEventData).toEqual(changedConfig);
|
|
72
67
|
});
|
|
73
68
|
});
|
|
74
69
|
});
|
|
75
|
-
|
|
76
|
-
describe('Server reconfigure methods', () => {
|
|
77
|
-
let conf, logger, cache;
|
|
78
|
-
|
|
79
|
-
const isPromise = (o) => Promise.resolve(o) === o;
|
|
80
|
-
|
|
81
|
-
beforeEach(() => {
|
|
82
|
-
conf = JSON.parse(JSON.stringify(defaultConfig));
|
|
83
|
-
logger = new Logger.Logger({ stringify: () => '' });
|
|
84
|
-
cache = new Cache({ ...conf.cacheConfig, logger: logger.push({ component: 'cache' }) });
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('InboundServer reconfigure method returns sync function', async () => {
|
|
88
|
-
const server = new InboundServer(conf, logger, cache);
|
|
89
|
-
const res = await server.reconfigure(conf, logger, cache);
|
|
90
|
-
expect(isPromise(res)).toEqual(false);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test('OutboundServer reconfigure method returns sync function', async () => {
|
|
94
|
-
const server = new OutboundServer(conf, logger, cache);
|
|
95
|
-
const res = await server.reconfigure(conf, logger, cache);
|
|
96
|
-
expect(isPromise(res)).toEqual(false);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test('TestServer reconfigure method returns sync function', async () => {
|
|
100
|
-
const server = new TestServer({ logger, cache });
|
|
101
|
-
const res = await server.reconfigure({ logger, cache });
|
|
102
|
-
expect(isPromise(res)).toEqual(false);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('ControlClient reconfigure method returns sync function', async () => {
|
|
106
|
-
const server = new TestControlServer.Server({ logger, appConfig: { ...conf, control: { port: 4005 }}});
|
|
107
|
-
const client = await ControlAgent.Client.Create({ port: 4005, logger, appConfig: {} });
|
|
108
|
-
const res = await client.reconfigure({ logger, port: 4005, appConfig: {} });
|
|
109
|
-
expect(isPromise(res)).toEqual(false);
|
|
110
|
-
await client.close();
|
|
111
|
-
await server.close();
|
|
112
|
-
});
|
|
113
|
-
});
|