@restorecommerce/chassis-srv 0.3.2 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/lib/cache/index.js +22 -3
  3. package/lib/cache/index.js.map +1 -1
  4. package/lib/command-interface/index.js +481 -493
  5. package/lib/command-interface/index.js.map +1 -1
  6. package/lib/config/index.js +6 -15
  7. package/lib/config/index.js.map +1 -1
  8. package/lib/database/index.d.ts +2 -4
  9. package/lib/database/index.js +4 -13
  10. package/lib/database/index.js.map +1 -1
  11. package/lib/database/provider/arango/base.js +314 -322
  12. package/lib/database/provider/arango/base.js.map +1 -1
  13. package/lib/database/provider/arango/common.js +70 -57
  14. package/lib/database/provider/arango/common.js.map +1 -1
  15. package/lib/database/provider/arango/graph.d.ts +4 -8
  16. package/lib/database/provider/arango/graph.js +335 -466
  17. package/lib/database/provider/arango/graph.js.map +1 -1
  18. package/lib/database/provider/arango/index.js +34 -21
  19. package/lib/database/provider/arango/index.js.map +1 -1
  20. package/lib/database/provider/arango/interface.d.ts +70 -0
  21. package/lib/database/provider/arango/interface.js +46 -0
  22. package/lib/database/provider/arango/interface.js.map +1 -0
  23. package/lib/database/provider/arango/utils.d.ts +77 -0
  24. package/lib/database/provider/arango/utils.js +587 -0
  25. package/lib/database/provider/arango/utils.js.map +1 -0
  26. package/lib/database/provider/nedb/index.js +203 -206
  27. package/lib/database/provider/nedb/index.js.map +1 -1
  28. package/lib/health/index.js +36 -42
  29. package/lib/health/index.js.map +1 -1
  30. package/lib/index.d.ts +4 -0
  31. package/lib/index.js +27 -6
  32. package/lib/index.js.map +1 -1
  33. package/lib/microservice/endpoint.js +33 -23
  34. package/lib/microservice/endpoint.js.map +1 -1
  35. package/lib/microservice/server.js +115 -106
  36. package/lib/microservice/server.js.map +1 -1
  37. package/lib/microservice/transport/provider/grpc/index.js +58 -52
  38. package/lib/microservice/transport/provider/grpc/index.js.map +1 -1
  39. package/lib/microservice/transport/provider/grpc/reflection.js +101 -93
  40. package/lib/microservice/transport/provider/grpc/reflection.js.map +1 -1
  41. package/lib/offsets/index.d.ts +1 -1
  42. package/lib/offsets/index.js +46 -39
  43. package/lib/offsets/index.js.map +1 -1
  44. package/package.json +27 -27
  45. package/test.js +56 -0
  46. package/tsconfig.json +13 -6
  47. package/setupTopics.js +0 -32
@@ -1,18 +1,28 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
10
20
  };
11
21
  Object.defineProperty(exports, "__esModule", { value: true });
12
22
  exports.CommandInterface = void 0;
13
- const _ = require("lodash");
14
- const database = require("./../database");
15
- const async = require("async");
23
+ const _ = __importStar(require("lodash"));
24
+ const database = __importStar(require("./../database"));
25
+ const async = __importStar(require("async"));
16
26
  // For some reason this is required
17
27
  const crypto = require('crypto');
18
28
  const ServingStatus = {
@@ -114,33 +124,31 @@ class CommandInterface {
114
124
  * @param call
115
125
  * @param context
116
126
  */
117
- command(call, context) {
118
- return __awaiter(this, void 0, void 0, function* () {
119
- if (_.isNil(call.request) && _.isNil(call.name)) {
120
- const result = {
121
- error: {
122
- code: 400,
123
- message: 'No command name provided',
124
- }
125
- };
126
- return this.encodeMsg(result);
127
- }
128
- const name = call.name || call.request.name;
129
- if (_.isNil(this.commands[name])) {
130
- const result = {
131
- error: {
132
- code: 400,
133
- message: `Command name ${name} does not exist`
134
- }
135
- };
136
- return this.encodeMsg(result);
137
- }
138
- const payload = call.payload ? this.decodeMsg(call.payload) :
139
- (call.request.payload ? this.decodeMsg(call.request.payload) : null);
140
- // calling operation bound to the command name
141
- const result = yield this.commands[name].apply(this, [payload]);
127
+ async command(call, context) {
128
+ if (_.isNil(call.request) && _.isNil(call.name)) {
129
+ const result = {
130
+ error: {
131
+ code: 400,
132
+ message: 'No command name provided',
133
+ }
134
+ };
142
135
  return this.encodeMsg(result);
143
- });
136
+ }
137
+ const name = call.name || call.request.name;
138
+ if (_.isNil(this.commands[name])) {
139
+ const result = {
140
+ error: {
141
+ code: 400,
142
+ message: `Command name ${name} does not exist`
143
+ }
144
+ };
145
+ return this.encodeMsg(result);
146
+ }
147
+ const payload = call.payload ? this.decodeMsg(call.payload) :
148
+ (call.request.payload ? this.decodeMsg(call.request.payload) : null);
149
+ // calling operation bound to the command name
150
+ const result = await this.commands[name].apply(this, [payload]);
151
+ return this.encodeMsg(result);
144
152
  }
145
153
  /**
146
154
  * Reconfigure service
@@ -162,200 +170,198 @@ class CommandInterface {
162
170
  * ArangoDB database collections, using the chassis-srv database provider.
163
171
  * @param topics list of Kafka topics to be restored
164
172
  */
165
- restore(payload) {
166
- return __awaiter(this, void 0, void 0, function* () {
167
- if (_.isEmpty(payload) || _.isEmpty(payload.data)) {
168
- // throw new errors.InvalidArgument('Invalid payload for restore command');
169
- return {
170
- error: {
171
- code: 400,
172
- message: 'Invalid payload for restore command'
173
+ async restore(payload) {
174
+ if (_.isEmpty(payload) || _.isEmpty(payload.data)) {
175
+ // throw new errors.InvalidArgument('Invalid payload for restore command');
176
+ return {
177
+ error: {
178
+ code: 400,
179
+ message: 'Invalid payload for restore command'
180
+ }
181
+ };
182
+ }
183
+ const restoreData = payload.data || [];
184
+ // the Kafka config should contains a key-value pair, mapping
185
+ // a label with the topic's name
186
+ const kafkaEventsCfg = this.config.get('events:kafka');
187
+ const kafkaCfg = this.config.get('events:kafka:topics');
188
+ if (_.isNil(kafkaCfg) || kafkaCfg.length == 0) {
189
+ return {
190
+ error: {
191
+ code: 500,
192
+ message: 'Kafka topics config not available'
193
+ }
194
+ };
195
+ }
196
+ const topicLabels = _.keys(kafkaCfg).filter((elem, index) => {
197
+ return elem.includes('.resource');
198
+ }).map((elem) => {
199
+ return elem.replace('.resource', '');
200
+ });
201
+ const restoreSetup = {};
202
+ const restoreEventSetup = {};
203
+ restoreData.forEach((data) => {
204
+ const ignoreOffset = (data.ignore_offset || []).filter((offset) => {
205
+ const isNumber = Number(offset) != NaN;
206
+ if (!isNumber) {
207
+ this.logger.warn(`Invalid value for "ignore_offset" parameter in restore: ${offset}`);
208
+ }
209
+ return isNumber;
210
+ });
211
+ restoreSetup[data.entity] = {
212
+ baseOffset: Number(data.base_offset) || 0,
213
+ ignoreOffset
214
+ };
215
+ });
216
+ const restoreCollections = _.keys(restoreSetup);
217
+ try {
218
+ const dbCfgs = this.config.get('database');
219
+ const dbCfgNames = _.keys(dbCfgs);
220
+ for (let i = 0; i < dbCfgNames.length; i += 1) {
221
+ const dbCfgName = dbCfgNames[i];
222
+ const dbCfg = dbCfgs[dbCfgName];
223
+ const collections = dbCfg.collections;
224
+ let graphName, edgeConfigDefs;
225
+ if (this.config.get('graph')) {
226
+ graphName = this.config.get('graph:graphName');
227
+ edgeConfigDefs = this.config.get('graph:edgeDefinitions');
228
+ }
229
+ const db = await database.get(dbCfg, this.logger, graphName, edgeConfigDefs);
230
+ if (_.isNil(collections)) {
231
+ this.logger.warn('No collections found on DB config');
232
+ return {};
233
+ }
234
+ let intersection = _.intersection(restoreCollections, collections);
235
+ if (intersection.length > 0) {
236
+ intersection = _.intersection(intersection, topicLabels);
237
+ for (let resource of intersection) {
238
+ const topicName = kafkaCfg[`${resource}.resource`].topic;
239
+ restoreEventSetup[topicName] = {
240
+ topic: await this.kafkaEvents.topic(topicName),
241
+ events: this.makeResourcesRestoreSetup(db, resource),
242
+ baseOffset: restoreSetup[resource].baseOffset,
243
+ ignoreOffset: restoreSetup[resource].ignoreOffset
244
+ };
173
245
  }
174
- };
246
+ }
175
247
  }
176
- const restoreData = payload.data || [];
177
- // the Kafka config should contains a key-value pair, mapping
178
- // a label with the topic's name
179
- const kafkaEventsCfg = this.config.get('events:kafka');
180
- const kafkaCfg = this.config.get('events:kafka:topics');
181
- if (_.isNil(kafkaCfg) || kafkaCfg.length == 0) {
182
- return {
183
- error: {
184
- code: 500,
185
- message: 'Kafka topics config not available'
186
- }
187
- };
248
+ if (_.isEmpty(restoreEventSetup)) {
249
+ this.logger.warn('No data was setup for the restore process.');
188
250
  }
189
- const topicLabels = _.keys(kafkaCfg).filter((elem, index) => {
190
- return elem.includes('.resource');
191
- }).map((elem) => {
192
- return elem.replace('.resource', '');
193
- });
194
- const restoreSetup = {};
195
- const restoreEventSetup = {};
196
- restoreData.forEach((data) => {
197
- const ignoreOffset = (data.ignore_offset || []).filter((offset) => {
198
- const isNumber = Number(offset) != NaN;
199
- if (!isNumber) {
200
- this.logger.warn(`Invalid value for "ignore_offset" parameter in restore: ${offset}`);
201
- }
202
- return isNumber;
203
- });
204
- restoreSetup[data.entity] = {
205
- baseOffset: Number(data.base_offset) || 0,
206
- ignoreOffset
207
- };
208
- });
209
- const restoreCollections = _.keys(restoreSetup);
210
- try {
211
- const dbCfgs = this.config.get('database');
212
- const dbCfgNames = _.keys(dbCfgs);
213
- for (let i = 0; i < dbCfgNames.length; i += 1) {
214
- const dbCfgName = dbCfgNames[i];
215
- const dbCfg = dbCfgs[dbCfgName];
216
- const collections = dbCfg.collections;
217
- let graphName, edgeConfigDefs;
218
- if (this.config.get('graph')) {
219
- graphName = this.config.get('graph:graphName');
220
- edgeConfigDefs = this.config.get('graph:edgeDefinitions');
221
- }
222
- const db = yield database.get(dbCfg, this.logger, graphName, edgeConfigDefs);
223
- if (_.isNil(collections)) {
224
- this.logger.warn('No collections found on DB config');
225
- return {};
226
- }
227
- let intersection = _.intersection(restoreCollections, collections);
228
- if (intersection.length > 0) {
229
- intersection = _.intersection(intersection, topicLabels);
230
- for (let resource of intersection) {
231
- const topicName = kafkaCfg[`${resource}.resource`].topic;
232
- restoreEventSetup[topicName] = {
233
- topic: yield this.kafkaEvents.topic(topicName),
234
- events: this.makeResourcesRestoreSetup(db, resource),
235
- baseOffset: restoreSetup[resource].baseOffset,
236
- ignoreOffset: restoreSetup[resource].ignoreOffset
237
- };
238
- }
251
+ else {
252
+ const that = this;
253
+ // Start the restore process
254
+ this.logger.warn('restoring data');
255
+ for (let topicName in restoreEventSetup) {
256
+ const topicSetup = restoreEventSetup[topicName];
257
+ const restoreTopic = topicSetup.topic;
258
+ const topicEvents = topicSetup.events;
259
+ // saving listeners for potentially subscribed events on this topic,
260
+ // so they don't get called during the restore process
261
+ const previousEvents = _.cloneDeep(restoreTopic.subscribed);
262
+ const listenersBackup = new Map();
263
+ for (let event of previousEvents) {
264
+ listenersBackup.set(event, restoreTopic.emitter.listeners(event));
265
+ await restoreTopic.removeAllListeners(event);
239
266
  }
240
- }
241
- if (_.isEmpty(restoreEventSetup)) {
242
- this.logger.warn('No data was setup for the restore process.');
243
- }
244
- else {
245
- const that = this;
246
- // Start the restore process
247
- this.logger.warn('restoring data');
248
- for (let topicName in restoreEventSetup) {
249
- const topicSetup = restoreEventSetup[topicName];
250
- const restoreTopic = topicSetup.topic;
251
- const topicEvents = topicSetup.events;
252
- // saving listeners for potentially subscribed events on this topic,
253
- // so they don't get called during the restore process
254
- const previousEvents = _.cloneDeep(restoreTopic.subscribed);
255
- const listenersBackup = new Map();
256
- for (let event of previousEvents) {
257
- listenersBackup.set(event, restoreTopic.emitter.listeners(event));
258
- yield restoreTopic.removeAllListeners(event);
259
- }
260
- // const eventNames = _.keys(restoreTopic.events);
261
- const baseOffset = topicSetup.baseOffset;
262
- const targetOffset = (yield restoreTopic.$offset(-1)) - 1;
263
- const ignoreOffsets = topicSetup.ignoreOffset;
264
- const eventNames = _.keys(topicEvents);
265
- this.logger.debug(`topic ${topicName} has current offset ${targetOffset}`);
266
- const restoreGroupId = kafkaEventsCfg.groupId + '-restore-' + crypto.randomBytes(32).toString('hex');
267
- const consumer = this.kafkaEvents.provider.client.consumer({
268
- groupId: restoreGroupId
267
+ // const eventNames = _.keys(restoreTopic.events);
268
+ const baseOffset = topicSetup.baseOffset;
269
+ const targetOffset = (await restoreTopic.$offset(-1)) - 1;
270
+ const ignoreOffsets = topicSetup.ignoreOffset;
271
+ const eventNames = _.keys(topicEvents);
272
+ this.logger.debug(`topic ${topicName} has current offset ${targetOffset}`);
273
+ const restoreGroupId = kafkaEventsCfg.groupId + '-restore-' + crypto.randomBytes(32).toString('hex');
274
+ const consumer = this.kafkaEvents.provider.client.consumer({
275
+ groupId: restoreGroupId
276
+ });
277
+ const drainEvent = (message, done) => {
278
+ const msg = message.value;
279
+ const eventName = message.key.toString();
280
+ const context = _.pick(message, ['offset', 'partition', 'topic']);
281
+ const eventListener = topicEvents[message.key];
282
+ // decode protobuf
283
+ let decodedMsg = that.kafkaEvents.provider.decodeObject(kafkaEventsCfg, eventName, msg);
284
+ decodedMsg = _.pick(decodedMsg, _.keys(decodedMsg)); // preventing protobuf.js special fields
285
+ eventListener(decodedMsg, context, that.config.get(), eventName).then(() => {
286
+ done();
287
+ }).catch((err) => {
288
+ that.logger.error(`Exception caught invoking restore listener for event ${eventName}:`, err);
289
+ done(err);
269
290
  });
270
- const drainEvent = (message, done) => {
271
- const msg = message.value;
272
- const eventName = message.key.toString();
273
- const context = _.pick(message, ['offset', 'partition', 'topic']);
274
- const eventListener = topicEvents[message.key];
275
- // decode protobuf
276
- let decodedMsg = that.kafkaEvents.provider.decodeObject(kafkaEventsCfg, eventName, msg);
277
- decodedMsg = _.pick(decodedMsg, _.keys(decodedMsg)); // preventing protobuf.js special fields
278
- eventListener(decodedMsg, context, that.config.get(), eventName).then(() => {
279
- done();
280
- }).catch((err) => {
281
- that.logger.error(`Exception caught invoking restore listener for event ${eventName}:`, err);
282
- done(err);
283
- });
284
- if (message.offset >= targetOffset) {
285
- for (let event of eventNames) {
286
- restoreTopic.removeAllListeners(event).then(() => { }).catch((err) => {
287
- that.logger.error('Error removing listeners after restore', err);
291
+ if (message.offset >= targetOffset) {
292
+ for (let event of eventNames) {
293
+ restoreTopic.removeAllListeners(event).then(() => { }).catch((err) => {
294
+ that.logger.error('Error removing listeners after restore', err);
295
+ });
296
+ }
297
+ for (let event of previousEvents) {
298
+ const listeners = listenersBackup.get(event);
299
+ for (let listener of listeners) {
300
+ restoreTopic.on(event, listener).then(() => { }).catch((err) => {
301
+ that.logger.error('Error subscribing to listeners after restore', err);
288
302
  });
289
303
  }
290
- for (let event of previousEvents) {
291
- const listeners = listenersBackup.get(event);
292
- for (let listener of listeners) {
293
- restoreTopic.on(event, listener).then(() => { }).catch((err) => {
294
- that.logger.error('Error subscribing to listeners after restore', err);
295
- });
296
- }
297
- }
298
- consumer.stop().then(() => consumer.disconnect()).then(() => {
299
- this.kafkaEvents.provider.admin.deleteGroups([restoreGroupId]).then(() => {
300
- that.logger.debug('restore kafka group deleted');
301
- const msg = {
302
- topic: topicName,
303
- offset: message.offset
304
- };
305
- that.commandTopic.emit('restoreResponse', {
306
- services: _.keys(that.service),
307
- payload: that.encodeMsg(msg)
308
- }).then(() => {
309
- that.logger.info('Restore response emitted');
310
- }).catch((err) => {
311
- that.logger.error('Error emitting command response', err);
312
- });
313
- that.logger.info('restore process done');
314
- }).catch(err => {
315
- that.logger.error('Error deleting restore kafka group:', err);
304
+ }
305
+ consumer.stop().then(() => consumer.disconnect()).then(() => {
306
+ this.kafkaEvents.provider.admin.deleteGroups([restoreGroupId]).then(() => {
307
+ that.logger.debug('restore kafka group deleted');
308
+ const msg = {
309
+ topic: topicName,
310
+ offset: message.offset
311
+ };
312
+ that.commandTopic.emit('restoreResponse', {
313
+ services: _.keys(that.service),
314
+ payload: that.encodeMsg(msg)
315
+ }).then(() => {
316
+ that.logger.info('Restore response emitted');
317
+ }).catch((err) => {
318
+ that.logger.error('Error emitting command response', err);
316
319
  });
320
+ that.logger.info('restore process done');
317
321
  }).catch(err => {
318
- that.logger.error('Error stopping consumer:', err);
322
+ that.logger.error('Error deleting restore kafka group:', err);
319
323
  });
324
+ }).catch(err => {
325
+ that.logger.error('Error stopping consumer:', err);
326
+ });
327
+ }
328
+ };
329
+ const asyncQueue = that.startToReceiveRestoreMessages(restoreTopic, drainEvent);
330
+ await consumer.connect().catch(err => {
331
+ that.logger.error(`error connecting consumer:`, err);
332
+ throw err;
333
+ });
334
+ await consumer.subscribe({
335
+ topic: topicName,
336
+ });
337
+ await consumer.run({
338
+ eachMessage: async (payload) => {
339
+ if (payload.message.key.toString() in topicEvents && !_.includes(ignoreOffsets, parseInt(payload.message.offset))) {
340
+ asyncQueue.push(payload.message);
341
+ that.logger.debug(`received message ${payload.message.offset}/${targetOffset}`);
320
342
  }
321
- };
322
- const asyncQueue = that.startToReceiveRestoreMessages(restoreTopic, drainEvent);
323
- yield consumer.connect().catch(err => {
324
- that.logger.error(`error connecting consumer:`, err);
325
- throw err;
326
- });
327
- yield consumer.subscribe({
328
- topic: topicName,
329
- });
330
- yield consumer.run({
331
- eachMessage: (payload) => __awaiter(this, void 0, void 0, function* () {
332
- if (payload.message.key.toString() in topicEvents && !_.includes(ignoreOffsets, parseInt(payload.message.offset))) {
333
- asyncQueue.push(payload.message);
334
- that.logger.debug(`received message ${payload.message.offset}/${targetOffset}`);
335
- }
336
- })
337
- });
338
- yield consumer.seek({
339
- topic: topicName,
340
- partition: 0,
341
- offset: baseOffset.toString(10)
342
- });
343
- }
344
- this.logger.debug('waiting until all messages are processed');
343
+ }
344
+ });
345
+ await consumer.seek({
346
+ topic: topicName,
347
+ partition: 0,
348
+ offset: baseOffset.toString(10)
349
+ });
345
350
  }
351
+ this.logger.debug('waiting until all messages are processed');
346
352
  }
347
- catch (err) {
348
- console.log('Err is...........', err);
349
- this.logger.error('Error occurred while restoring the system', err.message);
350
- yield this.commandTopic.emit('restoreResponse', {
351
- services: _.keys(this.service),
352
- payload: this.encodeMsg({
353
- error: err.message
354
- })
355
- });
356
- }
357
- return {};
358
- });
353
+ }
354
+ catch (err) {
355
+ console.log('Err is...........', err);
356
+ this.logger.error('Error occurred while restoring the system', err.message);
357
+ await this.commandTopic.emit('restoreResponse', {
358
+ services: _.keys(this.service),
359
+ payload: this.encodeMsg({
360
+ error: err.message
361
+ })
362
+ });
363
+ }
364
+ return {};
359
365
  }
360
366
  startToReceiveRestoreMessages(restoreTopic, drainEvent) {
361
367
  const asyncQueue = async.queue((msg, done) => {
@@ -382,202 +388,192 @@ class CommandInterface {
382
388
  * Reset system data related to a service. Default implementation truncates
383
389
  * a set of ArangoDB instances, using the chassis-srv database provider.
384
390
  */
385
- reset() {
386
- return __awaiter(this, void 0, void 0, function* () {
387
- this.logger.info('reset process started');
388
- if (this.health.status !== ServingStatus.NOT_SERVING) {
389
- this.logger.warn('reset process starting while server is serving');
390
- }
391
- let errorMsg = null;
392
- try {
393
- const dbCfgs = this.config.get('database');
394
- const dbCfgNames = _.keys(dbCfgs);
395
- for (let i = 0; i < dbCfgNames.length; i += 1) {
396
- const dbCfgName = dbCfgNames[i];
397
- const dbCfg = dbCfgs[dbCfgName];
398
- const db = yield database.get(dbCfg, this.logger);
399
- switch (dbCfg.provider) {
400
- case 'arango':
401
- yield db.truncate();
402
- this.logger.info(`arangodb ${dbCfg.database} truncated`);
403
- break;
404
- default:
405
- this.logger.error(`unsupported database provider ${dbCfg.provider} in database config ${dbCfgName}`);
406
- break;
407
- }
391
+ async reset() {
392
+ this.logger.info('reset process started');
393
+ if (this.health.status !== ServingStatus.NOT_SERVING) {
394
+ this.logger.warn('reset process starting while server is serving');
395
+ }
396
+ let errorMsg = null;
397
+ try {
398
+ const dbCfgs = this.config.get('database');
399
+ const dbCfgNames = _.keys(dbCfgs);
400
+ for (let i = 0; i < dbCfgNames.length; i += 1) {
401
+ const dbCfgName = dbCfgNames[i];
402
+ const dbCfg = dbCfgs[dbCfgName];
403
+ const db = await database.get(dbCfg, this.logger);
404
+ switch (dbCfg.provider) {
405
+ case 'arango':
406
+ await db.truncate();
407
+ this.logger.info(`arangodb ${dbCfg.database} truncated`);
408
+ break;
409
+ default:
410
+ this.logger.error(`unsupported database provider ${dbCfg.provider} in database config ${dbCfgName}`);
411
+ break;
408
412
  }
409
413
  }
410
- catch (err) {
411
- this.logger.error('Unexpected error while resetting the system', err.message);
412
- errorMsg = err.message;
413
- }
414
- const eventObject = {
415
- services: _.keys(this.service),
416
- payload: null
417
- };
418
- if (errorMsg) {
419
- eventObject.payload = this.encodeMsg({
420
- error: errorMsg
421
- });
422
- }
423
- else {
424
- eventObject.payload = this.encodeMsg({
425
- status: 'Reset concluded successfully'
426
- });
427
- }
428
- yield this.commandTopic.emit('resetResponse', eventObject);
429
- this.logger.info('reset process ended');
430
- if (errorMsg) {
431
- return {
432
- error: errorMsg
433
- };
434
- }
435
- return {
414
+ }
415
+ catch (err) {
416
+ this.logger.error('Unexpected error while resetting the system', err.message);
417
+ errorMsg = err.message;
418
+ }
419
+ const eventObject = {
420
+ services: _.keys(this.service),
421
+ payload: null
422
+ };
423
+ if (errorMsg) {
424
+ eventObject.payload = this.encodeMsg({
425
+ error: errorMsg
426
+ });
427
+ }
428
+ else {
429
+ eventObject.payload = this.encodeMsg({
436
430
  status: 'Reset concluded successfully'
431
+ });
432
+ }
433
+ await this.commandTopic.emit('resetResponse', eventObject);
434
+ this.logger.info('reset process ended');
435
+ if (errorMsg) {
436
+ return {
437
+ error: errorMsg
437
438
  };
438
- });
439
+ }
440
+ return {
441
+ status: 'Reset concluded successfully'
442
+ };
439
443
  }
440
444
  /**
441
445
  * Check the service status
442
446
  * @param call List of services to be checked
443
447
  * @param context
444
448
  */
445
- check(payload) {
446
- return __awaiter(this, void 0, void 0, function* () {
447
- if (_.isNil(payload)) {
448
- return {
449
- error: {
450
- code: 400,
451
- message: 'Invalid payload for restore command'
452
- }
453
- };
454
- }
455
- const serviceName = payload.service;
456
- if (_.isNil(serviceName) || _.size(serviceName) === 0) {
457
- yield this.commandTopic.emit('healthCheckResponse', {
458
- services: _.keys(this.service),
459
- payload: this.encodeMsg({
460
- status: this.health.status,
461
- })
462
- });
463
- return {
464
- status: this.health.status,
465
- };
466
- }
467
- const service = this.service[serviceName];
468
- if (_.isNil(service)) {
469
- const errorMsg = 'Service ' + serviceName + ' does not exist';
470
- this.logger.warn(errorMsg);
471
- return {
472
- error: {
473
- code: 404,
474
- message: errorMsg
475
- }
476
- };
477
- }
478
- let status = ServingStatus.UNKNOWN;
479
- // If one transports serves the service, set it to SERVING
480
- _.forEach(service.transport, (transportStatus) => {
481
- if (transportStatus === ServingStatus.SERVING) {
482
- status = transportStatus;
449
+ async check(payload) {
450
+ if (_.isNil(payload)) {
451
+ return {
452
+ error: {
453
+ code: 400,
454
+ message: 'Invalid payload for restore command'
483
455
  }
484
- });
485
- yield this.commandTopic.emit('healthCheckResponse', {
486
- services: [serviceName],
456
+ };
457
+ }
458
+ const serviceName = payload.service;
459
+ if (_.isNil(serviceName) || _.size(serviceName) === 0) {
460
+ await this.commandTopic.emit('healthCheckResponse', {
461
+ services: _.keys(this.service),
487
462
  payload: this.encodeMsg({
488
- status,
463
+ status: this.health.status,
489
464
  })
490
465
  });
491
466
  return {
492
- status,
467
+ status: this.health.status,
493
468
  };
469
+ }
470
+ const service = this.service[serviceName];
471
+ if (_.isNil(service)) {
472
+ const errorMsg = 'Service ' + serviceName + ' does not exist';
473
+ this.logger.warn(errorMsg);
474
+ return {
475
+ error: {
476
+ code: 404,
477
+ message: errorMsg
478
+ }
479
+ };
480
+ }
481
+ let status = ServingStatus.UNKNOWN;
482
+ // If one transports serves the service, set it to SERVING
483
+ _.forEach(service.transport, (transportStatus) => {
484
+ if (transportStatus === ServingStatus.SERVING) {
485
+ status = transportStatus;
486
+ }
494
487
  });
488
+ await this.commandTopic.emit('healthCheckResponse', {
489
+ services: [serviceName],
490
+ payload: this.encodeMsg({
491
+ status,
492
+ })
493
+ });
494
+ return {
495
+ status,
496
+ };
495
497
  }
496
498
  /**
497
499
  * Retrieve current NPM package and Node version of service
498
500
  */
499
- version() {
500
- return __awaiter(this, void 0, void 0, function* () {
501
- const response = {
502
- nodejs: process.version,
503
- version: process.env.npm_package_version,
504
- };
505
- yield this.commandTopic.emit('versionResponse', {
506
- services: _.keys(this.service),
507
- payload: this.encodeMsg(response)
508
- });
509
- return response;
501
+ async version() {
502
+ const response = {
503
+ nodejs: process.version,
504
+ version: process.env.npm_package_version,
505
+ };
506
+ await this.commandTopic.emit('versionResponse', {
507
+ services: _.keys(this.service),
508
+ payload: this.encodeMsg(response)
510
509
  });
510
+ return response;
511
511
  }
512
512
  /**
513
513
  * Update config for acs-client to disable it
514
514
  * @param payload JSON object containing key value pairs for configuration
515
515
  */
516
- configUpdate(payload) {
517
- return __awaiter(this, void 0, void 0, function* () {
518
- if (_.isNil(payload)) {
519
- return {
520
- error: {
521
- code: 400,
522
- message: 'Invalid payload for configUpdate command'
523
- }
524
- };
525
- }
526
- let response;
527
- try {
528
- let configProperties = Object.keys(payload);
529
- for (let key of configProperties) {
530
- this.config.set(key, payload[key]);
516
+ async configUpdate(payload) {
517
+ if (_.isNil(payload)) {
518
+ return {
519
+ error: {
520
+ code: 400,
521
+ message: 'Invalid payload for configUpdate command'
531
522
  }
532
- response = {
533
- status: 'Configuration updated successfully'
534
- };
535
- yield this.commandTopic.emit('configUpdateResponse', {
536
- services: _.keys(this.service),
537
- payload: this.encodeMsg(response)
538
- });
539
- }
540
- catch (error) {
541
- this.logger.error('Error executing configUpdate Command', { message: error.message });
542
- response = error.message;
523
+ };
524
+ }
525
+ let response;
526
+ try {
527
+ let configProperties = Object.keys(payload);
528
+ for (let key of configProperties) {
529
+ this.config.set(key, payload[key]);
543
530
  }
544
- return response;
545
- });
531
+ response = {
532
+ status: 'Configuration updated successfully'
533
+ };
534
+ await this.commandTopic.emit('configUpdateResponse', {
535
+ services: _.keys(this.service),
536
+ payload: this.encodeMsg(response)
537
+ });
538
+ }
539
+ catch (error) {
540
+ this.logger.error('Error executing configUpdate Command', { message: error.message });
541
+ response = error.message;
542
+ }
543
+ return response;
546
544
  }
547
545
  /**
548
546
  * Sets provided authentication apiKey on configuration
549
547
  * @param payload JSON object containing key value pairs for authentication apiKey
550
548
  */
551
- setApiKey(payload) {
552
- return __awaiter(this, void 0, void 0, function* () {
553
- if (_.isNil(payload)) {
554
- return {
555
- error: {
556
- code: 400,
557
- message: 'Invalid payload for setApiKey command'
558
- }
559
- };
560
- }
561
- let response;
562
- try {
563
- let configProperties = Object.keys(payload);
564
- for (let key of configProperties) {
565
- this.config.set(key, payload[key]);
549
+ async setApiKey(payload) {
550
+ if (_.isNil(payload)) {
551
+ return {
552
+ error: {
553
+ code: 400,
554
+ message: 'Invalid payload for setApiKey command'
566
555
  }
567
- response = {
568
- status: 'ApiKey set successfully'
569
- };
570
- yield this.commandTopic.emit('setApiKeyResponse', {
571
- services: _.keys(this.service),
572
- payload: this.encodeMsg(response)
573
- });
574
- }
575
- catch (err) {
576
- this.logger.error('Error executing setApiKey Command', { message: err.message });
577
- response = err.message;
556
+ };
557
+ }
558
+ let response;
559
+ try {
560
+ let configProperties = Object.keys(payload);
561
+ for (let key of configProperties) {
562
+ this.config.set(key, payload[key]);
578
563
  }
579
- return response;
580
- });
564
+ response = {
565
+ status: 'ApiKey set successfully'
566
+ };
567
+ await this.commandTopic.emit('setApiKeyResponse', {
568
+ services: _.keys(this.service),
569
+ payload: this.encodeMsg(response)
570
+ });
571
+ }
572
+ catch (err) {
573
+ this.logger.error('Error executing setApiKey Command', { message: err.message });
574
+ response = err.message;
575
+ }
576
+ return response;
581
577
  }
582
578
  /**
583
579
  * Flush the cache based on DB index and prefix passed, if no dbIndex is passed
@@ -585,112 +581,110 @@ class CommandInterface {
585
581
  *
586
582
  * @param prefix An optional prefix to flush instead of entire cache
587
583
  */
588
- flushCache(payload) {
589
- return __awaiter(this, void 0, void 0, function* () {
590
- let flushCachePayload;
591
- if (payload && payload.data) {
592
- flushCachePayload = payload.data;
593
- }
594
- let dbIndex, pattern, response;
595
- if (flushCachePayload) {
596
- dbIndex = flushCachePayload.db_index;
597
- pattern = flushCachePayload.pattern;
598
- }
599
- if (dbIndex === undefined || !dbIndex) {
600
- dbIndex = 0;
584
+ async flushCache(payload) {
585
+ let flushCachePayload;
586
+ if (payload && payload.data) {
587
+ flushCachePayload = payload.data;
588
+ }
589
+ let dbIndex, pattern, response;
590
+ if (flushCachePayload) {
591
+ dbIndex = flushCachePayload.db_index;
592
+ pattern = flushCachePayload.pattern;
593
+ }
594
+ if (dbIndex === undefined || !dbIndex) {
595
+ dbIndex = 0;
596
+ }
597
+ // select the particular dbIndex
598
+ await this.redisClient.select(dbIndex);
599
+ try {
600
+ if (pattern != undefined) {
601
+ let flushPattern = '*' + pattern + '*';
602
+ this.logger.debug('Flushing cache wiht pattern', { dbIndex, flushPattern });
603
+ let stream, pipeline;
604
+ try {
605
+ stream = this.redisClient.scanStream({ match: flushPattern, count: 100 });
606
+ pipeline = this.redisClient.pipeline();
607
+ }
608
+ catch (err) {
609
+ this.logger.error('Error creating stream / pipeline in Redis', { message: err.message });
610
+ response = err.message;
611
+ }
612
+ let localKeys = [];
613
+ if (stream && pipeline) {
614
+ await new Promise((resolve, reject) => {
615
+ stream.on('data', (resultKeys) => {
616
+ this.logger.info('Data Received:', localKeys.length);
617
+ for (let i = 0; i < resultKeys.length; i++) {
618
+ localKeys.push(resultKeys[i]);
619
+ pipeline.del(resultKeys[i]);
620
+ }
621
+ if (localKeys.length > 100) {
622
+ pipeline.exec(() => { this.logger.info('one batch delete complete'); });
623
+ localKeys = [];
624
+ pipeline = this.redisClient.pipeline();
625
+ }
626
+ });
627
+ stream.on('end', () => {
628
+ pipeline.exec(() => { this.logger.info('final batch delete complete'); });
629
+ response = {
630
+ status: 'Successfully flushed cache pattern'
631
+ };
632
+ resolve(response);
633
+ });
634
+ stream.on('error', (err) => {
635
+ this.logger.error('error', err);
636
+ response = err.message;
637
+ resolve(err);
638
+ });
639
+ });
640
+ }
601
641
  }
602
- // select the particular dbIndex
603
- yield this.redisClient.select(dbIndex);
604
- try {
605
- if (pattern != undefined) {
606
- let flushPattern = '*' + pattern + '*';
607
- this.logger.debug('Flushing cache wiht pattern', { dbIndex, flushPattern });
608
- let stream, pipeline;
609
- try {
610
- stream = this.redisClient.scanStream({ match: flushPattern, count: 100 });
611
- pipeline = this.redisClient.pipeline();
612
- }
613
- catch (err) {
614
- this.logger.error('Error creating stream / pipeline in Redis', { message: err.message });
615
- response = err.message;
642
+ else {
643
+ this.logger.debug('Flushing cache', { dbIndex });
644
+ await new Promise((resolve, reject) => {
645
+ if (dbIndex || dbIndex === 0) {
646
+ // Flush all keys in the given dbIndex (flushDB)
647
+ this.redisClient.flushdb(async (err, reply) => {
648
+ if (err) {
649
+ this.logger.error('Failed flushing cache with DB index', { err, dbIndex });
650
+ return reject();
651
+ }
652
+ if (reply) {
653
+ this.logger.debug('Successfully flushed cache with DB index', { dbIndex });
654
+ response = {
655
+ status: `Successfully flushed cache with DB index ${dbIndex}`
656
+ };
657
+ return resolve(response);
658
+ }
659
+ });
616
660
  }
617
- let localKeys = [];
618
- if (stream && pipeline) {
619
- yield new Promise((resolve, reject) => {
620
- stream.on('data', (resultKeys) => {
621
- this.logger.info('Data Received:', localKeys.length);
622
- for (let i = 0; i < resultKeys.length; i++) {
623
- localKeys.push(resultKeys[i]);
624
- pipeline.del(resultKeys[i]);
625
- }
626
- if (localKeys.length > 100) {
627
- pipeline.exec(() => { this.logger.info('one batch delete complete'); });
628
- localKeys = [];
629
- pipeline = this.redisClient.pipeline();
630
- }
631
- });
632
- stream.on('end', () => {
633
- pipeline.exec(() => { this.logger.info('final batch delete complete'); });
661
+ else {
662
+ // Flush Complete Redis Cache (flushAll)
663
+ this.redisClient.flushall(async (err, reply) => {
664
+ if (err) {
665
+ this.logger.error('Failed flushing complete cache', { err });
666
+ return reject();
667
+ }
668
+ if (reply) {
669
+ this.logger.debug('Successfully flushed complete cache');
634
670
  response = {
635
- status: 'Successfully flushed cache pattern'
671
+ status: 'Successfully flushed complete cache'
636
672
  };
637
- resolve(response);
638
- });
639
- stream.on('error', (err) => {
640
- this.logger.error('error', err);
641
- response = err.message;
642
- resolve(err);
643
- });
673
+ return resolve(response);
674
+ }
644
675
  });
645
676
  }
646
- }
647
- else {
648
- this.logger.debug('Flushing cache', { dbIndex });
649
- yield new Promise((resolve, reject) => {
650
- if (dbIndex || dbIndex === 0) {
651
- // Flush all keys in the given dbIndex (flushDB)
652
- this.redisClient.flushdb((err, reply) => __awaiter(this, void 0, void 0, function* () {
653
- if (err) {
654
- this.logger.error('Failed flushing cache with DB index', { err, dbIndex });
655
- return reject();
656
- }
657
- if (reply) {
658
- this.logger.debug('Successfully flushed cache with DB index', { dbIndex });
659
- response = {
660
- status: `Successfully flushed cache with DB index ${dbIndex}`
661
- };
662
- return resolve(response);
663
- }
664
- }));
665
- }
666
- else {
667
- // Flush Complete Redis Cache (flushAll)
668
- this.redisClient.flushall((err, reply) => __awaiter(this, void 0, void 0, function* () {
669
- if (err) {
670
- this.logger.error('Failed flushing complete cache', { err });
671
- return reject();
672
- }
673
- if (reply) {
674
- this.logger.debug('Successfully flushed complete cache');
675
- response = {
676
- status: 'Successfully flushed complete cache'
677
- };
678
- return resolve(response);
679
- }
680
- }));
681
- }
682
- });
683
- }
684
- }
685
- catch (err) {
686
- response = err.message;
677
+ });
687
678
  }
688
- yield this.commandTopic.emit('flushCacheResponse', {
689
- services: _.keys(this.service),
690
- payload: this.encodeMsg(response)
691
- });
692
- return response;
679
+ }
680
+ catch (err) {
681
+ response = err.message;
682
+ }
683
+ await this.commandTopic.emit('flushCacheResponse', {
684
+ services: _.keys(this.service),
685
+ payload: this.encodeMsg(response)
693
686
  });
687
+ return response;
694
688
  }
695
689
  // Helper functions
696
690
  /**
@@ -701,25 +695,19 @@ class CommandInterface {
701
695
  makeResourcesRestoreSetup(db, resource) {
702
696
  const that = this;
703
697
  return {
704
- [`${resource}Created`]: function restoreCreated(message, ctx, config, eventName) {
705
- return __awaiter(this, void 0, void 0, function* () {
706
- that.decodeBufferField(message, resource);
707
- yield db.insert(`${resource}s`, message);
708
- return {};
709
- });
698
+ [`${resource}Created`]: async function restoreCreated(message, ctx, config, eventName) {
699
+ that.decodeBufferField(message, resource);
700
+ await db.insert(`${resource}s`, message);
701
+ return {};
710
702
  },
711
- [`${resource}Modified`]: function restoreModified(message, ctx, config, eventName) {
712
- return __awaiter(this, void 0, void 0, function* () {
713
- that.decodeBufferField(message, resource);
714
- yield db.update(`${resource}s`, { id: message.id }, _.omitBy(message, _.isNil));
715
- return {};
716
- });
703
+ [`${resource}Modified`]: async function restoreModified(message, ctx, config, eventName) {
704
+ that.decodeBufferField(message, resource);
705
+ await db.update(`${resource}s`, { id: message.id }, _.omitBy(message, _.isNil));
706
+ return {};
717
707
  },
718
- [`${resource}Deleted`]: function restoreDeleted(message, ctx, config, eventName) {
719
- return __awaiter(this, void 0, void 0, function* () {
720
- yield db.delete(`${resource}s`, { id: message.id });
721
- return {};
722
- });
708
+ [`${resource}Deleted`]: async function restoreDeleted(message, ctx, config, eventName) {
709
+ await db.delete(`${resource}s`, { id: message.id });
710
+ return {};
723
711
  }
724
712
  };
725
713
  }