@restorecommerce/chassis-srv 0.3.3 → 0.3.7
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 +19 -0
- package/lib/cache/index.js +22 -3
- package/lib/cache/index.js.map +1 -1
- package/lib/command-interface/index.d.ts +3 -3
- package/lib/command-interface/index.js +432 -498
- package/lib/command-interface/index.js.map +1 -1
- package/lib/config/index.js +6 -15
- package/lib/config/index.js.map +1 -1
- package/lib/database/index.d.ts +2 -4
- package/lib/database/index.js +4 -13
- package/lib/database/index.js.map +1 -1
- package/lib/database/provider/arango/base.js +314 -322
- package/lib/database/provider/arango/base.js.map +1 -1
- package/lib/database/provider/arango/common.js +70 -57
- package/lib/database/provider/arango/common.js.map +1 -1
- package/lib/database/provider/arango/graph.d.ts +4 -8
- package/lib/database/provider/arango/graph.js +335 -470
- package/lib/database/provider/arango/graph.js.map +1 -1
- package/lib/database/provider/arango/index.js +34 -21
- package/lib/database/provider/arango/index.js.map +1 -1
- package/lib/database/provider/arango/interface.d.ts +70 -0
- package/lib/database/provider/arango/interface.js +46 -0
- package/lib/database/provider/arango/interface.js.map +1 -0
- package/lib/database/provider/arango/utils.d.ts +77 -0
- package/lib/database/provider/arango/utils.js +587 -0
- package/lib/database/provider/arango/utils.js.map +1 -0
- package/lib/database/provider/nedb/index.js +203 -206
- package/lib/database/provider/nedb/index.js.map +1 -1
- package/lib/health/index.js +36 -42
- package/lib/health/index.js.map +1 -1
- package/lib/index.d.ts +4 -0
- package/lib/index.js +27 -6
- package/lib/index.js.map +1 -1
- package/lib/microservice/endpoint.js +33 -23
- package/lib/microservice/endpoint.js.map +1 -1
- package/lib/microservice/server.js +115 -106
- package/lib/microservice/server.js.map +1 -1
- package/lib/microservice/transport/provider/grpc/index.js +58 -52
- package/lib/microservice/transport/provider/grpc/index.js.map +1 -1
- package/lib/microservice/transport/provider/grpc/reflection.js +101 -93
- package/lib/microservice/transport/provider/grpc/reflection.js.map +1 -1
- package/lib/offsets/index.d.ts +2 -2
- package/lib/offsets/index.js +41 -42
- package/lib/offsets/index.js.map +1 -1
- package/package.json +29 -29
- package/tsconfig.json +13 -6
- package/setupTopics.js +0 -32
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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,197 @@ 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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
this.
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
let
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
that.
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
});
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
this.logger.error('Error occurred while restoring the system', err.message);
|
|
356
|
+
await this.commandTopic.emit('restoreResponse', {
|
|
357
|
+
services: _.keys(this.service),
|
|
358
|
+
payload: this.encodeMsg({
|
|
359
|
+
error: err.message
|
|
360
|
+
})
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
return {};
|
|
359
364
|
}
|
|
360
365
|
startToReceiveRestoreMessages(restoreTopic, drainEvent) {
|
|
361
366
|
const asyncQueue = async.queue((msg, done) => {
|
|
@@ -382,202 +387,180 @@ class CommandInterface {
|
|
|
382
387
|
* Reset system data related to a service. Default implementation truncates
|
|
383
388
|
* a set of ArangoDB instances, using the chassis-srv database provider.
|
|
384
389
|
*/
|
|
385
|
-
reset() {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
break;
|
|
407
|
-
}
|
|
390
|
+
async reset() {
|
|
391
|
+
this.logger.info('reset process started');
|
|
392
|
+
if (this.health.status !== ServingStatus.NOT_SERVING) {
|
|
393
|
+
this.logger.warn('reset process starting while server is serving');
|
|
394
|
+
}
|
|
395
|
+
let errorMsg = null;
|
|
396
|
+
try {
|
|
397
|
+
const dbCfgs = this.config.get('database');
|
|
398
|
+
const dbCfgNames = _.keys(dbCfgs);
|
|
399
|
+
for (let i = 0; i < dbCfgNames.length; i += 1) {
|
|
400
|
+
const dbCfgName = dbCfgNames[i];
|
|
401
|
+
const dbCfg = dbCfgs[dbCfgName];
|
|
402
|
+
const db = await database.get(dbCfg, this.logger);
|
|
403
|
+
switch (dbCfg.provider) {
|
|
404
|
+
case 'arango':
|
|
405
|
+
await db.truncate();
|
|
406
|
+
this.logger.info(`arangodb ${dbCfg.database} truncated`);
|
|
407
|
+
break;
|
|
408
|
+
default:
|
|
409
|
+
this.logger.error(`unsupported database provider ${dbCfg.provider} in database config ${dbCfgName}`);
|
|
410
|
+
break;
|
|
408
411
|
}
|
|
409
412
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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 {
|
|
413
|
+
}
|
|
414
|
+
catch (err) {
|
|
415
|
+
this.logger.error('Unexpected error while resetting the system', err.message);
|
|
416
|
+
errorMsg = err.message;
|
|
417
|
+
}
|
|
418
|
+
const eventObject = {
|
|
419
|
+
services: _.keys(this.service),
|
|
420
|
+
payload: null
|
|
421
|
+
};
|
|
422
|
+
if (errorMsg) {
|
|
423
|
+
eventObject.payload = this.encodeMsg({
|
|
424
|
+
error: errorMsg
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
eventObject.payload = this.encodeMsg({
|
|
436
429
|
status: 'Reset concluded successfully'
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
await this.commandTopic.emit('resetResponse', eventObject);
|
|
433
|
+
this.logger.info('reset process ended');
|
|
434
|
+
if (errorMsg) {
|
|
435
|
+
return {
|
|
436
|
+
error: errorMsg
|
|
437
437
|
};
|
|
438
|
-
}
|
|
438
|
+
}
|
|
439
|
+
return {
|
|
440
|
+
status: 'Reset concluded successfully'
|
|
441
|
+
};
|
|
439
442
|
}
|
|
440
443
|
/**
|
|
441
444
|
* Check the service status
|
|
442
445
|
* @param call List of services to be checked
|
|
443
446
|
* @param context
|
|
444
447
|
*/
|
|
445
|
-
check(payload) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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;
|
|
448
|
+
async check(payload) {
|
|
449
|
+
if (_.isNil(payload)) {
|
|
450
|
+
return {
|
|
451
|
+
error: {
|
|
452
|
+
code: 400,
|
|
453
|
+
message: 'Invalid payload for restore command'
|
|
483
454
|
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
}
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
const serviceName = payload.service;
|
|
458
|
+
if (_.isNil(serviceName) || _.size(serviceName) === 0) {
|
|
459
|
+
return {
|
|
460
|
+
status: this.health.status,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
const service = this.service[serviceName];
|
|
464
|
+
if (_.isNil(service)) {
|
|
465
|
+
const errorMsg = 'Service ' + serviceName + ' does not exist';
|
|
466
|
+
this.logger.warn(errorMsg);
|
|
491
467
|
return {
|
|
492
|
-
|
|
468
|
+
error: {
|
|
469
|
+
code: 404,
|
|
470
|
+
message: errorMsg
|
|
471
|
+
}
|
|
493
472
|
};
|
|
473
|
+
}
|
|
474
|
+
let status = ServingStatus.UNKNOWN;
|
|
475
|
+
// If one transports serves the service, set it to SERVING
|
|
476
|
+
_.forEach(service.transport, (transportStatus) => {
|
|
477
|
+
if (transportStatus === ServingStatus.SERVING) {
|
|
478
|
+
status = transportStatus;
|
|
479
|
+
}
|
|
494
480
|
});
|
|
481
|
+
return {
|
|
482
|
+
status,
|
|
483
|
+
};
|
|
495
484
|
}
|
|
496
485
|
/**
|
|
497
486
|
* Retrieve current NPM package and Node version of service
|
|
498
487
|
*/
|
|
499
|
-
version() {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
payload: this.encodeMsg(response)
|
|
508
|
-
});
|
|
509
|
-
return response;
|
|
488
|
+
async version() {
|
|
489
|
+
const response = {
|
|
490
|
+
nodejs: process.version,
|
|
491
|
+
version: process.env.npm_package_version,
|
|
492
|
+
};
|
|
493
|
+
await this.commandTopic.emit('versionResponse', {
|
|
494
|
+
services: _.keys(this.service),
|
|
495
|
+
payload: this.encodeMsg(response)
|
|
510
496
|
});
|
|
497
|
+
return response;
|
|
511
498
|
}
|
|
512
499
|
/**
|
|
513
500
|
* Update config for acs-client to disable it
|
|
514
501
|
* @param payload JSON object containing key value pairs for configuration
|
|
515
502
|
*/
|
|
516
|
-
configUpdate(payload) {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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]);
|
|
503
|
+
async configUpdate(payload) {
|
|
504
|
+
if (_.isNil(payload)) {
|
|
505
|
+
return {
|
|
506
|
+
error: {
|
|
507
|
+
code: 400,
|
|
508
|
+
message: 'Invalid payload for configUpdate command'
|
|
531
509
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
}
|
|
540
|
-
catch (error) {
|
|
541
|
-
this.logger.error('Error executing configUpdate Command', { message: error.message });
|
|
542
|
-
response = error.message;
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
let response;
|
|
513
|
+
try {
|
|
514
|
+
let configProperties = Object.keys(payload);
|
|
515
|
+
for (let key of configProperties) {
|
|
516
|
+
this.config.set(key, payload[key]);
|
|
543
517
|
}
|
|
544
|
-
|
|
545
|
-
|
|
518
|
+
response = {
|
|
519
|
+
status: 'Configuration updated successfully'
|
|
520
|
+
};
|
|
521
|
+
await this.commandTopic.emit('configUpdateResponse', {
|
|
522
|
+
services: _.keys(this.service),
|
|
523
|
+
payload: this.encodeMsg(response)
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
catch (error) {
|
|
527
|
+
this.logger.error('Error executing configUpdate Command', { message: error.message });
|
|
528
|
+
response = error.message;
|
|
529
|
+
}
|
|
530
|
+
return response;
|
|
546
531
|
}
|
|
547
532
|
/**
|
|
548
533
|
* Sets provided authentication apiKey on configuration
|
|
549
534
|
* @param payload JSON object containing key value pairs for authentication apiKey
|
|
550
535
|
*/
|
|
551
|
-
setApiKey(payload) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
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]);
|
|
536
|
+
async setApiKey(payload) {
|
|
537
|
+
if (_.isNil(payload)) {
|
|
538
|
+
return {
|
|
539
|
+
error: {
|
|
540
|
+
code: 400,
|
|
541
|
+
message: 'Invalid payload for setApiKey command'
|
|
566
542
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
}
|
|
575
|
-
catch (err) {
|
|
576
|
-
this.logger.error('Error executing setApiKey Command', { message: err.message });
|
|
577
|
-
response = err.message;
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
let response;
|
|
546
|
+
try {
|
|
547
|
+
let configProperties = Object.keys(payload);
|
|
548
|
+
for (let key of configProperties) {
|
|
549
|
+
this.config.set(key, payload[key]);
|
|
578
550
|
}
|
|
579
|
-
|
|
580
|
-
|
|
551
|
+
response = {
|
|
552
|
+
status: 'ApiKey set successfully'
|
|
553
|
+
};
|
|
554
|
+
await this.commandTopic.emit('setApiKeyResponse', {
|
|
555
|
+
services: _.keys(this.service),
|
|
556
|
+
payload: this.encodeMsg(response)
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
catch (err) {
|
|
560
|
+
this.logger.error('Error executing setApiKey Command', { message: err.message });
|
|
561
|
+
response = err.message;
|
|
562
|
+
}
|
|
563
|
+
return response;
|
|
581
564
|
}
|
|
582
565
|
/**
|
|
583
566
|
* Flush the cache based on DB index and prefix passed, if no dbIndex is passed
|
|
@@ -585,112 +568,69 @@ class CommandInterface {
|
|
|
585
568
|
*
|
|
586
569
|
* @param prefix An optional prefix to flush instead of entire cache
|
|
587
570
|
*/
|
|
588
|
-
flushCache(payload) {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
}
|
|
613
|
-
catch (err) {
|
|
614
|
-
this.logger.error('Error creating stream / pipeline in Redis', { message: err.message });
|
|
615
|
-
response = err.message;
|
|
616
|
-
}
|
|
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'); });
|
|
634
|
-
response = {
|
|
635
|
-
status: 'Successfully flushed cache pattern'
|
|
636
|
-
};
|
|
637
|
-
resolve(response);
|
|
638
|
-
});
|
|
639
|
-
stream.on('error', (err) => {
|
|
640
|
-
this.logger.error('error', err);
|
|
641
|
-
response = err.message;
|
|
642
|
-
resolve(err);
|
|
643
|
-
});
|
|
644
|
-
});
|
|
571
|
+
async flushCache(payload) {
|
|
572
|
+
let flushCachePayload;
|
|
573
|
+
if (payload && payload.data) {
|
|
574
|
+
flushCachePayload = payload.data;
|
|
575
|
+
}
|
|
576
|
+
let dbIndex, pattern, response;
|
|
577
|
+
if (flushCachePayload) {
|
|
578
|
+
dbIndex = flushCachePayload.db_index;
|
|
579
|
+
pattern = flushCachePayload.pattern;
|
|
580
|
+
}
|
|
581
|
+
if (dbIndex === undefined || !dbIndex) {
|
|
582
|
+
dbIndex = 0;
|
|
583
|
+
}
|
|
584
|
+
// select the particular dbIndex
|
|
585
|
+
await this.redisClient.select(dbIndex);
|
|
586
|
+
try {
|
|
587
|
+
if (pattern != undefined) {
|
|
588
|
+
let flushPattern = '*' + pattern + '*';
|
|
589
|
+
this.logger.debug('Flushing cache wiht pattern', { dbIndex, flushPattern });
|
|
590
|
+
let scanIterator;
|
|
591
|
+
try {
|
|
592
|
+
scanIterator = this.redisClient.scanIterator({ MATCH: flushPattern, COUNT: 100 });
|
|
593
|
+
for await (const key of scanIterator) {
|
|
594
|
+
await this.redisClient.del(key);
|
|
645
595
|
}
|
|
596
|
+
this.logger.debug(`Successfully flushed cache pattern ${flushPattern}`);
|
|
597
|
+
response = {
|
|
598
|
+
status: 'Successfully flushed cache pattern'
|
|
599
|
+
};
|
|
646
600
|
}
|
|
647
|
-
|
|
648
|
-
this.logger.
|
|
649
|
-
|
|
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
|
-
});
|
|
601
|
+
catch (err) {
|
|
602
|
+
this.logger.error('Error creating stream / pipeline in Redis', { message: err.message });
|
|
603
|
+
response = err.message;
|
|
683
604
|
}
|
|
684
605
|
}
|
|
685
|
-
|
|
686
|
-
|
|
606
|
+
else {
|
|
607
|
+
this.logger.debug('Flushing cache', { dbIndex });
|
|
608
|
+
if (dbIndex || dbIndex === 0) {
|
|
609
|
+
// Flush all keys in the given dbIndex (flushDB)
|
|
610
|
+
await this.redisClient.flushDb();
|
|
611
|
+
response = {
|
|
612
|
+
status: `Successfully flushed cache with DB index ${dbIndex}`
|
|
613
|
+
};
|
|
614
|
+
this.logger.debug('Successfully flushed cache with DB index', { dbIndex });
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
// Flush Complete Redis Cache (flushAll)
|
|
618
|
+
await this.redisClient.flushAll();
|
|
619
|
+
response = {
|
|
620
|
+
status: 'Successfully flushed complete cache'
|
|
621
|
+
};
|
|
622
|
+
this.logger.debug('Successfully flushed complete cache');
|
|
623
|
+
}
|
|
687
624
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
625
|
+
}
|
|
626
|
+
catch (err) {
|
|
627
|
+
response = err.message;
|
|
628
|
+
}
|
|
629
|
+
await this.commandTopic.emit('flushCacheResponse', {
|
|
630
|
+
services: _.keys(this.service),
|
|
631
|
+
payload: this.encodeMsg(response)
|
|
693
632
|
});
|
|
633
|
+
return response;
|
|
694
634
|
}
|
|
695
635
|
// Helper functions
|
|
696
636
|
/**
|
|
@@ -701,25 +641,19 @@ class CommandInterface {
|
|
|
701
641
|
makeResourcesRestoreSetup(db, resource) {
|
|
702
642
|
const that = this;
|
|
703
643
|
return {
|
|
704
|
-
[`${resource}Created`]: function restoreCreated(message, ctx, config, eventName) {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
return {};
|
|
709
|
-
});
|
|
644
|
+
[`${resource}Created`]: async function restoreCreated(message, ctx, config, eventName) {
|
|
645
|
+
that.decodeBufferField(message, resource);
|
|
646
|
+
await db.insert(`${resource}s`, message);
|
|
647
|
+
return {};
|
|
710
648
|
},
|
|
711
|
-
[`${resource}Modified`]: function restoreModified(message, ctx, config, eventName) {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
return {};
|
|
716
|
-
});
|
|
649
|
+
[`${resource}Modified`]: async function restoreModified(message, ctx, config, eventName) {
|
|
650
|
+
that.decodeBufferField(message, resource);
|
|
651
|
+
await db.update(`${resource}s`, { id: message.id }, _.omitBy(message, _.isNil));
|
|
652
|
+
return {};
|
|
717
653
|
},
|
|
718
|
-
[`${resource}Deleted`]: function restoreDeleted(message, ctx, config, eventName) {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
return {};
|
|
722
|
-
});
|
|
654
|
+
[`${resource}Deleted`]: async function restoreDeleted(message, ctx, config, eventName) {
|
|
655
|
+
await db.delete(`${resource}s`, { id: message.id });
|
|
656
|
+
return {};
|
|
723
657
|
}
|
|
724
658
|
};
|
|
725
659
|
}
|