elasticio-sailor-nodejs 2.7.0-dev3 → 2.7.0
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/.nsprc +6 -0
- package/CHANGELOG.md +144 -0
- package/lib/amqp.js +272 -80
- package/lib/cipher.js +0 -82
- package/lib/component_reader.js +14 -7
- package/lib/encryptor.js +107 -14
- package/lib/executor.js +1 -1
- package/lib/ipc.js +13 -0
- package/lib/sailor.js +112 -104
- package/lib/service.js +1 -2
- package/lib/settings.js +72 -53
- package/package.json +13 -10
- package/run.js +60 -21
- package/runService.js +5 -1
- package/.travis.yml +0 -8
- package/Procfile +0 -2
- package/createQueues.js +0 -148
- package/gulpfile.js +0 -31
package/lib/cipher.js
CHANGED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const crypto = require('crypto');
|
|
3
|
-
const debug = require('debug')('sailor:cipher');
|
|
4
|
-
const { PassThrough } = require('stream');
|
|
5
|
-
|
|
6
|
-
const ALGORYTHM = 'aes-256-cbc';
|
|
7
|
-
|
|
8
|
-
exports.id = 1;
|
|
9
|
-
exports.encrypt = encryptIV;
|
|
10
|
-
exports.encryptStream = createCypher;
|
|
11
|
-
exports.decrypt = decryptIV;
|
|
12
|
-
exports.decryptStream = createDecipher;
|
|
13
|
-
|
|
14
|
-
function createCypher() {
|
|
15
|
-
const PASSWORD = process.env.ELASTICIO_MESSAGE_CRYPTO_PASSWORD;
|
|
16
|
-
const VECTOR = process.env.ELASTICIO_MESSAGE_CRYPTO_IV;
|
|
17
|
-
|
|
18
|
-
if (!PASSWORD) {
|
|
19
|
-
//mimic cypher
|
|
20
|
-
return new class extends PassThrough {
|
|
21
|
-
update(data) {
|
|
22
|
-
return data;
|
|
23
|
-
}
|
|
24
|
-
final() {
|
|
25
|
-
return '';
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!VECTOR) {
|
|
31
|
-
throw new Error('process.env.ELASTICIO_MESSAGE_CRYPTO_IV is not set');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const encodeKey = crypto.createHash('sha256').update(PASSWORD, 'utf-8').digest();
|
|
35
|
-
return crypto.createCipheriv(ALGORYTHM, encodeKey, VECTOR);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function createDecipher() {
|
|
39
|
-
const PASSWORD = process.env.ELASTICIO_MESSAGE_CRYPTO_PASSWORD;
|
|
40
|
-
const VECTOR = process.env.ELASTICIO_MESSAGE_CRYPTO_IV;
|
|
41
|
-
|
|
42
|
-
if (!PASSWORD) {
|
|
43
|
-
//mimic cypher
|
|
44
|
-
return new class extends PassThrough {
|
|
45
|
-
update(data) {
|
|
46
|
-
return data;
|
|
47
|
-
}
|
|
48
|
-
final() {
|
|
49
|
-
return '';
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!VECTOR) {
|
|
55
|
-
throw new Error('process.env.ELASTICIO_MESSAGE_CRYPTO_IV is not set');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const decodeKey = crypto.createHash('sha256').update(PASSWORD, 'utf-8').digest();
|
|
59
|
-
return crypto.createDecipheriv(ALGORYTHM, decodeKey, VECTOR);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function encryptIV(rawData, outputEncoding) {
|
|
63
|
-
debug('About to encrypt:', rawData);
|
|
64
|
-
|
|
65
|
-
if (!_.isString(rawData)) {
|
|
66
|
-
throw new Error('RabbitMQ message cipher.encryptIV() accepts only string as parameter.');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const cipher = createCypher();
|
|
70
|
-
return Buffer.concat([
|
|
71
|
-
Buffer.from(cipher.update(rawData, 'utf8', outputEncoding)),
|
|
72
|
-
Buffer.from(cipher.final(outputEncoding))
|
|
73
|
-
]);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function decryptIV(encData, inputEncoding) {
|
|
77
|
-
const decipher = createDecipher();
|
|
78
|
-
|
|
79
|
-
const data = inputEncoding ? encData.toString() : encData;
|
|
80
|
-
|
|
81
|
-
return decipher.update(data, inputEncoding, 'utf8') + decipher.final('utf8');
|
|
82
|
-
}
|
package/lib/component_reader.js
CHANGED
|
@@ -24,10 +24,7 @@ ComponentReader.prototype.init = function init(componentPath) {
|
|
|
24
24
|
|
|
25
25
|
ComponentReader.prototype.promiseLoadJson = function promiseLoadJson(jsonFilePath) {
|
|
26
26
|
try {
|
|
27
|
-
this.componentJson =
|
|
28
|
-
autoResolveObjectReferences: true,
|
|
29
|
-
...require(jsonFilePath)
|
|
30
|
-
};
|
|
27
|
+
this.componentJson = require(jsonFilePath);
|
|
31
28
|
log.debug('Successfully loaded %s', jsonFilePath);
|
|
32
29
|
log.debug('Triggers: %j', _.keys(this.componentJson.triggers));
|
|
33
30
|
log.debug('Actions: %j', _.keys(this.componentJson.actions));
|
|
@@ -37,18 +34,28 @@ ComponentReader.prototype.promiseLoadJson = function promiseLoadJson(jsonFilePat
|
|
|
37
34
|
}
|
|
38
35
|
};
|
|
39
36
|
|
|
40
|
-
ComponentReader.prototype.
|
|
37
|
+
ComponentReader.prototype.findTriggerOrActionDefinition = function findTriggerOrActionDefinition(name) {
|
|
41
38
|
if (this.componentJson === null) {
|
|
42
39
|
throw new Error('Component.json was not loaded');
|
|
43
40
|
}
|
|
44
41
|
|
|
42
|
+
let def;
|
|
45
43
|
if (this.componentJson.triggers && this.componentJson.triggers[name]) {
|
|
46
|
-
|
|
44
|
+
def = this.componentJson.triggers[name];
|
|
47
45
|
} else if (this.componentJson.actions && this.componentJson.actions[name]) {
|
|
48
|
-
|
|
46
|
+
def = this.componentJson.actions[name];
|
|
49
47
|
} else {
|
|
50
48
|
throw new Error('Trigger or action "' + name + '" is not found in component.json!');
|
|
51
49
|
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
autoResolveObjectReferences: true,
|
|
53
|
+
...def
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
ComponentReader.prototype.findTriggerOrAction = function findTriggerOrAction(name) {
|
|
58
|
+
return this.findTriggerOrActionDefinition(name).main;
|
|
52
59
|
};
|
|
53
60
|
|
|
54
61
|
ComponentReader.prototype.loadTriggerOrAction = async function loadTriggerOrAction(name) {
|
package/lib/encryptor.js
CHANGED
|
@@ -1,21 +1,114 @@
|
|
|
1
|
-
const
|
|
1
|
+
const crypto = require('crypto');
|
|
2
2
|
const log = require('./logging.js');
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
class Encryptor {
|
|
5
|
+
constructor(password, iv) {
|
|
6
|
+
this._cryptoPassword = password;
|
|
7
|
+
this._cryptoIV = iv;
|
|
8
|
+
if (this._cryptoPassword) {
|
|
9
|
+
this._encryptionKey = crypto
|
|
10
|
+
.createHash('sha256')
|
|
11
|
+
.update(this._cryptoPassword, 'utf-8')
|
|
12
|
+
.digest();
|
|
13
|
+
if (!this._cryptoIV) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`missing crypt initialiazation vector,
|
|
16
|
+
most likely ELASTICIO_MESSAGE_CRYPTO_PASSWORD env var is not set
|
|
17
|
+
`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
this._algorithm = 'aes-256-cbc';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
createCipher() {
|
|
25
|
+
return crypto.createCipheriv(
|
|
26
|
+
this._algorithm,
|
|
27
|
+
this._encryptionKey,
|
|
28
|
+
this._cryptoIV
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
createDecipher() {
|
|
33
|
+
return crypto.createDecipheriv(
|
|
34
|
+
this._algorithm,
|
|
35
|
+
this._encryptionKey,
|
|
36
|
+
this._cryptoIV
|
|
37
|
+
);
|
|
38
|
+
}
|
|
6
39
|
|
|
7
|
-
function encryptMessageContent(messagePayload, outputEncoding) {
|
|
8
|
-
return cipher.encrypt(JSON.stringify(messagePayload), outputEncoding);
|
|
9
|
-
}
|
|
10
40
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Encrypt message to proper format
|
|
43
|
+
* @param {*} messagePayload anything json-stringifiable
|
|
44
|
+
* @param {'hex'|'base64'|'utf-8',undefined} outputEncoding
|
|
45
|
+
* @returns {Buffer}
|
|
46
|
+
*/
|
|
47
|
+
encryptMessageContent(messagePayload, outputEncoding) {
|
|
48
|
+
const encryptedBuffer = this._encryptToBuffer(JSON.stringify(messagePayload));
|
|
49
|
+
if (outputEncoding) {
|
|
50
|
+
return Buffer.from(encryptedBuffer.toString(outputEncoding));
|
|
51
|
+
} else {
|
|
52
|
+
return encryptedBuffer;
|
|
53
|
+
}
|
|
14
54
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Encrypt message to proper format
|
|
58
|
+
* @param {Buffer} messagePayload
|
|
59
|
+
* @param {'hex'|'base64'|'utf-8',undefined} inputEncoding
|
|
60
|
+
* @returns {*} anything, what have been encrypted
|
|
61
|
+
*/
|
|
62
|
+
decryptMessageContent(messagePayload, inputEncoding) {
|
|
63
|
+
if (!messagePayload || messagePayload.length === 0) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
let encryptedBuffer;
|
|
67
|
+
if (inputEncoding) {
|
|
68
|
+
encryptedBuffer = Buffer.from(messagePayload.toString(), inputEncoding);
|
|
69
|
+
} else {
|
|
70
|
+
encryptedBuffer = messagePayload;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const decryptedMessage = this._decryptFromBuffer(encryptedBuffer);
|
|
74
|
+
return JSON.parse(decryptedMessage);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
log.error(err, 'Failed to decrypt message');
|
|
77
|
+
throw Error('Failed to decrypt message: ' + err.message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Encrypt payload.
|
|
83
|
+
* @param {Buffer|String} message
|
|
84
|
+
* @returns {Buffer}
|
|
85
|
+
*/
|
|
86
|
+
_encryptToBuffer(message) {
|
|
87
|
+
if (!this._encryptionKey) {
|
|
88
|
+
return Buffer.from(message);
|
|
89
|
+
}
|
|
90
|
+
const cipher = this.createCipher();
|
|
91
|
+
return Buffer.concat([
|
|
92
|
+
cipher.update(message),
|
|
93
|
+
cipher.final()
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Decrypt payload.
|
|
99
|
+
* @param {Buffer} message
|
|
100
|
+
* @returns {String}
|
|
101
|
+
*/
|
|
102
|
+
_decryptFromBuffer(message) {
|
|
103
|
+
if (!this._encryptionKey) {
|
|
104
|
+
return message.toString();
|
|
105
|
+
}
|
|
106
|
+
const decipher = this.createDecipher();
|
|
107
|
+
|
|
108
|
+
return Buffer.concat([
|
|
109
|
+
decipher.update(message),
|
|
110
|
+
decipher.final()
|
|
111
|
+
]).toString('utf8');
|
|
20
112
|
}
|
|
21
113
|
}
|
|
114
|
+
module.exports = Encryptor;
|
package/lib/executor.js
CHANGED
|
@@ -20,7 +20,7 @@ class TaskExec extends EventEmitter {
|
|
|
20
20
|
|
|
21
21
|
process(triggerOrAction, payload, cfg, snapshot) {
|
|
22
22
|
const onError = async (err) => {
|
|
23
|
-
this.logger.error(
|
|
23
|
+
this.logger.error('Error occurred during trigger or action processing');
|
|
24
24
|
await this.emit('error', err);
|
|
25
25
|
await this.emit('end');
|
|
26
26
|
};
|
package/lib/ipc.js
ADDED
package/lib/sailor.js
CHANGED
|
@@ -4,8 +4,8 @@ const amqp = require('./amqp.js');
|
|
|
4
4
|
const TaskExec = require('./executor.js').TaskExec;
|
|
5
5
|
const log = require('./logging.js');
|
|
6
6
|
const _ = require('lodash');
|
|
7
|
-
const cipher = require('./cipher.js');
|
|
8
7
|
const hooksData = require('./hooksData');
|
|
8
|
+
const Encryptor = require('../lib/encryptor');
|
|
9
9
|
const RestApiClient = require('elasticio-rest-node');
|
|
10
10
|
const assert = require('assert');
|
|
11
11
|
const co = require('co');
|
|
@@ -50,7 +50,11 @@ class Sailor {
|
|
|
50
50
|
jwtSecret: settings.OBJECT_STORAGE_TOKEN
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
const encryptor = new Encryptor(settings.MESSAGE_CRYPTO_PASSWORD, settings.MESSAGE_CRYPTO_IV);
|
|
54
|
+
this.objectStorage = objectStorage.use(
|
|
55
|
+
() => encryptor.createCipher(),
|
|
56
|
+
() => encryptor.createDecipher()
|
|
57
|
+
);
|
|
54
58
|
|
|
55
59
|
this.throttles = {
|
|
56
60
|
// 100 Messages per Second
|
|
@@ -82,7 +86,7 @@ class Sailor {
|
|
|
82
86
|
} = this;
|
|
83
87
|
|
|
84
88
|
const stepData = await apiClient.tasks.retrieveStep(flowId, stepId);
|
|
85
|
-
log.debug('Received step data
|
|
89
|
+
log.debug('Received step data');
|
|
86
90
|
assert(stepData);
|
|
87
91
|
|
|
88
92
|
Object.assign(this, {
|
|
@@ -118,7 +122,7 @@ class Sailor {
|
|
|
118
122
|
return co(function* doStartup() {
|
|
119
123
|
log.debug('Starting up component');
|
|
120
124
|
const result = yield this.invokeModuleFunction('startup');
|
|
121
|
-
log.trace('Startup data'
|
|
125
|
+
log.trace('Startup data received');
|
|
122
126
|
const handle = hooksData.startup(this.settings);
|
|
123
127
|
try {
|
|
124
128
|
const state = _.isEmpty(result) ? {} : result;
|
|
@@ -189,7 +193,7 @@ class Sailor {
|
|
|
189
193
|
try {
|
|
190
194
|
return await this.processMessage(payload, message);
|
|
191
195
|
} catch (e) {
|
|
192
|
-
log.error(
|
|
196
|
+
log.error('Something very bad happened during message processing');
|
|
193
197
|
} finally {
|
|
194
198
|
if (this.shutdownCallback) {
|
|
195
199
|
if (this.messagesCount === 0) {
|
|
@@ -205,24 +209,22 @@ class Sailor {
|
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
211
|
|
|
208
|
-
scheduleShutdown() {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
log.debug('scheduleShutdown – shutdown is already scheduled, do nothing');
|
|
212
|
-
return new Promise(resolve => this.shutdownCallback = resolve);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
yield this.amqpConnection.listenQueueCancel();
|
|
216
|
-
if (this.messagesCount === 0) {
|
|
217
|
-
// there is no unfinished processMessage invocation, let's just resolve scheduleShutdown now
|
|
218
|
-
log.debug('scheduleShutdown – about to shutdown immediately');
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
// at least one processMessage invocation is not finished yet
|
|
222
|
-
// let's return a Promise, which will be resolved by processMessageAndMaybeShutdownCallback
|
|
223
|
-
log.debug('scheduleShutdown – shutdown is scheduled');
|
|
212
|
+
async scheduleShutdown() {
|
|
213
|
+
if (this.shutdownCallback) {
|
|
214
|
+
log.debug('scheduleShutdown – shutdown is already scheduled, do nothing');
|
|
224
215
|
return new Promise(resolve => this.shutdownCallback = resolve);
|
|
225
|
-
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
await this.amqpConnection.stopConsume();
|
|
219
|
+
if (this.messagesCount === 0) {
|
|
220
|
+
// there is no unfinished processMessage invocation, let's just resolve scheduleShutdown now
|
|
221
|
+
log.debug('scheduleShutdown – about to shutdown immediately');
|
|
222
|
+
return Promise.resolve();
|
|
223
|
+
}
|
|
224
|
+
// at least one processMessage invocation is not finished yet
|
|
225
|
+
// let's return a Promise, which will be resolved by processMessageAndMaybeShutdownCallback
|
|
226
|
+
log.debug('scheduleShutdown – shutdown is scheduled');
|
|
227
|
+
return new Promise(resolve => this.shutdownCallback = resolve);
|
|
226
228
|
}
|
|
227
229
|
|
|
228
230
|
|
|
@@ -257,17 +259,17 @@ class Sailor {
|
|
|
257
259
|
async fetchMessageBody(message, logger) {
|
|
258
260
|
const { body, headers } = message;
|
|
259
261
|
|
|
260
|
-
logger.info('Checking if incoming messages is lightweight...'
|
|
262
|
+
logger.info('Checking if incoming messages is lightweight...');
|
|
261
263
|
|
|
262
264
|
if (!headers) {
|
|
263
|
-
logger.info('Empty headers so not lightweight.'
|
|
265
|
+
logger.info('Empty headers so not lightweight.');
|
|
264
266
|
return body;
|
|
265
267
|
}
|
|
266
268
|
|
|
267
269
|
const { [OBJECT_ID_HEADER]: objectId } = headers;
|
|
268
270
|
|
|
269
271
|
if (!objectId) {
|
|
270
|
-
logger.
|
|
272
|
+
logger.trace('No object id header so not lightweight.');
|
|
271
273
|
return body;
|
|
272
274
|
}
|
|
273
275
|
|
|
@@ -278,43 +280,27 @@ class Sailor {
|
|
|
278
280
|
logger.info('Going to fetch message body.', { objectId });
|
|
279
281
|
|
|
280
282
|
try {
|
|
281
|
-
object = await this.objectStorage.getAsJSON(
|
|
283
|
+
object = await this.objectStorage.getAsJSON(
|
|
284
|
+
objectId,
|
|
285
|
+
{ jwtPayloadOrToken: this.settings.OBJECT_STORAGE_TOKEN }
|
|
286
|
+
);
|
|
282
287
|
} catch (e) {
|
|
283
288
|
log.error(e);
|
|
284
289
|
throw new Error(`Failed to get message body with id=${objectId}`);
|
|
285
290
|
}
|
|
286
291
|
|
|
287
292
|
logger.info('Successfully obtained message body.', { objectId });
|
|
288
|
-
logger.trace('Message body object'
|
|
293
|
+
logger.trace('Message body object received');
|
|
289
294
|
|
|
290
295
|
return object;
|
|
291
296
|
}
|
|
292
297
|
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
logger.info('Message size is below threshold', { size, OBJECT_STORAGE_SIZE_THRESHOLD });
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const readable = Readable.from(buf.values());
|
|
304
|
-
|
|
305
|
-
let id;
|
|
306
|
-
|
|
307
|
-
logger.info('Message size is above threshold, going to upload', { size, OBJECT_STORAGE_SIZE_THRESHOLD });
|
|
308
|
-
|
|
309
|
-
try {
|
|
310
|
-
id = await this.objectStorage.addAsStream(readable);
|
|
311
|
-
} catch (e) {
|
|
312
|
-
log.error(e);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
logger.info('Message uploaded', { id });
|
|
316
|
-
|
|
317
|
-
return id;
|
|
298
|
+
uploadMessageBody(bodyBuf) {
|
|
299
|
+
const stream = () => Readable.from(bodyBuf);
|
|
300
|
+
return this.objectStorage.addAsStream(
|
|
301
|
+
stream,
|
|
302
|
+
{ jwtPayloadOrToken: this.settings.OBJECT_STORAGE_TOKEN }
|
|
303
|
+
);
|
|
318
304
|
}
|
|
319
305
|
|
|
320
306
|
async runExec(module, payload, message, outgoingMessageHeaders, stepData, timeStart, logger) {
|
|
@@ -376,57 +362,79 @@ class Sailor {
|
|
|
376
362
|
}
|
|
377
363
|
|
|
378
364
|
data.headers = data.headers || {};
|
|
379
|
-
const { body, passthrough } = data;
|
|
365
|
+
const { body, passthrough = {} } = data;
|
|
380
366
|
|
|
381
367
|
if (settings.EMIT_LIGHTWEIGHT_MESSAGE) {
|
|
382
|
-
logger.
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
368
|
+
logger.trace('Outgoing lightweight is enabled, going to check size.');
|
|
369
|
+
const bodyBuf = Buffer.from(JSON.stringify(body), 'utf-8');
|
|
370
|
+
const passthroughBufs = Object.keys(passthrough).map(stepId => ({
|
|
371
|
+
stepId,
|
|
372
|
+
body: Buffer.from(JSON.stringify(passthrough[stepId].body), 'utf-8'),
|
|
373
|
+
id: passthrough[stepId].headers && passthrough[stepId].headers[OBJECT_ID_HEADER]
|
|
374
|
+
}));
|
|
375
|
+
|
|
376
|
+
const totalLength = passthroughBufs.reduce((len, { body }) =>
|
|
377
|
+
len + body.length, bodyBuf.length);
|
|
378
|
+
|
|
379
|
+
if (totalLength > settings.OBJECT_STORAGE_SIZE_THRESHOLD) {
|
|
380
|
+
logger.info(
|
|
381
|
+
'Message size is above threshold, going to upload',
|
|
382
|
+
{
|
|
383
|
+
totalLength,
|
|
384
|
+
OBJECT_STORAGE_SIZE_THRESHOLD: settings.OBJECT_STORAGE_SIZE_THRESHOLD
|
|
394
385
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
'Passthrough is lightweight already',
|
|
406
|
-
{ stepId, headers }
|
|
407
|
-
);
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
const id = await that.uploadMessageBody(data.passthrough[stepId].body, logger);
|
|
411
|
-
if (id) {
|
|
412
|
-
data.passthrough[stepId].body = {};
|
|
413
|
-
data.passthrough[stepId].headers = {
|
|
414
|
-
...(headers || {}),
|
|
415
|
-
[OBJECT_ID_HEADER]: id
|
|
416
|
-
};
|
|
417
|
-
}
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
let bodyId;
|
|
389
|
+
let passthroughIds;
|
|
390
|
+
try {
|
|
391
|
+
[bodyId, ...passthroughIds] = await Promise.all([
|
|
392
|
+
that.uploadMessageBody(bodyBuf),
|
|
393
|
+
...passthroughBufs.map(async ({ stepId, body, id }) => {
|
|
394
|
+
const bodyId = id || await that.uploadMessageBody(body);
|
|
395
|
+
return { stepId, bodyId };
|
|
418
396
|
})
|
|
419
|
-
|
|
420
|
-
)
|
|
421
|
-
|
|
397
|
+
]);
|
|
398
|
+
} catch (e) {
|
|
399
|
+
logger.error(e, 'Error during message/passthrough body upload');
|
|
400
|
+
return onError(new Error('Lightweight message/passthrough body upload error'));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
logger.info('Message body uploaded', { id: bodyId });
|
|
404
|
+
const { headers } = data;
|
|
405
|
+
data.body = {};
|
|
406
|
+
data.headers = {
|
|
407
|
+
...(headers || {}),
|
|
408
|
+
[OBJECT_ID_HEADER]: bodyId
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
for (const { stepId, bodyId } of passthroughIds) {
|
|
412
|
+
logger.info('Passthrough Message body uploaded', { stepId, id: bodyId });
|
|
413
|
+
const { [stepId]: { headers } } = passthrough;
|
|
414
|
+
data.passthrough[stepId].body = {};
|
|
415
|
+
data.passthrough[stepId].headers = {
|
|
416
|
+
...(headers || {}),
|
|
417
|
+
[OBJECT_ID_HEADER]: bodyId
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
} else {
|
|
422
|
+
logger.trace(
|
|
423
|
+
'Message size is below threshold.',
|
|
424
|
+
{
|
|
425
|
+
totalLength,
|
|
426
|
+
OBJECT_STORAGE_SIZE_THRESHOLD: settings.OBJECT_STORAGE_SIZE_THRESHOLD
|
|
427
|
+
}
|
|
428
|
+
);
|
|
429
|
+
}
|
|
422
430
|
} else if (passthrough) {
|
|
423
|
-
logger.
|
|
431
|
+
logger.trace('Outgoing lightweight is disabled, going to download all bodies.');
|
|
424
432
|
try {
|
|
425
433
|
await Promise.all(Object.keys(passthrough).map(async stepId => {
|
|
426
|
-
logger.
|
|
434
|
+
logger.trace('Going to check if passthrough for step is lightweight.', { stepId });
|
|
427
435
|
// if body is not empty then we've downloaded before processing, no need to redownload
|
|
428
436
|
if (!_.isEmpty(data.passthrough[stepId].body)) {
|
|
429
|
-
logger.
|
|
437
|
+
logger.trace('Body is not empty.', { stepId });
|
|
430
438
|
return;
|
|
431
439
|
}
|
|
432
440
|
data.passthrough[stepId].body = await that.fetchMessageBody(
|
|
@@ -445,8 +453,11 @@ class Sailor {
|
|
|
445
453
|
});
|
|
446
454
|
}
|
|
447
455
|
|
|
456
|
+
log.trace('Going to send outgoing message');
|
|
457
|
+
|
|
448
458
|
try {
|
|
449
459
|
await that.amqpConnection.sendData(data, headers, that.throttles.data);
|
|
460
|
+
log.trace('Outgoing message sent');
|
|
450
461
|
} catch (err) {
|
|
451
462
|
return onError(err);
|
|
452
463
|
}
|
|
@@ -475,16 +486,13 @@ class Sailor {
|
|
|
475
486
|
}
|
|
476
487
|
|
|
477
488
|
async function onRebound(err) {
|
|
478
|
-
const headers = _.clone(outgoingMessageHeaders);
|
|
479
489
|
err = formatError(err);
|
|
480
490
|
logger.trace({
|
|
481
491
|
err,
|
|
482
492
|
messagesCount: that.messagesCount,
|
|
483
493
|
messageProcessingTime: Date.now() - timeStart
|
|
484
494
|
}, 'processMessage emit rebound');
|
|
485
|
-
|
|
486
|
-
headers.reboundReason = err.message;
|
|
487
|
-
return that.amqpConnection.sendRebound(err, message, headers);
|
|
495
|
+
return that.amqpConnection.sendRebound(err, message);
|
|
488
496
|
}
|
|
489
497
|
|
|
490
498
|
async function onSnapshot(data) {
|
|
@@ -546,7 +554,6 @@ class Sailor {
|
|
|
546
554
|
errorCount: taskExec.errorCount,
|
|
547
555
|
messageProcessingTime: Date.now() - timeStart
|
|
548
556
|
}, 'processMessage emit end');
|
|
549
|
-
|
|
550
557
|
resolve();
|
|
551
558
|
}
|
|
552
559
|
});
|
|
@@ -607,8 +614,7 @@ class Sailor {
|
|
|
607
614
|
stepId: settings.STEP_ID,
|
|
608
615
|
compId: settings.COMP_ID,
|
|
609
616
|
function: settings.FUNCTION,
|
|
610
|
-
start: new Date().getTime()
|
|
611
|
-
cid: cipher.id
|
|
617
|
+
start: new Date().getTime()
|
|
612
618
|
};
|
|
613
619
|
let module;
|
|
614
620
|
try {
|
|
@@ -621,18 +627,20 @@ class Sailor {
|
|
|
621
627
|
return;
|
|
622
628
|
}
|
|
623
629
|
|
|
624
|
-
|
|
630
|
+
const method = this.componentReader.findTriggerOrActionDefinition(settings.FUNCTION);
|
|
631
|
+
|
|
632
|
+
if (method.autoResolveObjectReferences) {
|
|
625
633
|
const { passthrough } = payload;
|
|
626
634
|
|
|
627
635
|
try {
|
|
628
636
|
await Promise.all([
|
|
629
637
|
(async () => {
|
|
630
|
-
logger.
|
|
638
|
+
logger.trace('Going to check if incoming message body is lightweight.');
|
|
631
639
|
payload.body = await this.fetchMessageBody(payload, logger);
|
|
632
640
|
})(),
|
|
633
641
|
...(passthrough
|
|
634
642
|
? Object.keys(passthrough).map(async stepId => {
|
|
635
|
-
logger.
|
|
643
|
+
logger.trace('Going to check if passthrough for step is lightweight.', { stepId });
|
|
636
644
|
payload.passthrough[stepId].body = await this.fetchMessageBody(
|
|
637
645
|
payload.passthrough[stepId],
|
|
638
646
|
logger
|
package/lib/service.js
CHANGED
|
@@ -245,8 +245,7 @@ function processService(serviceMethod, env) {
|
|
|
245
245
|
.then(validateMethod)
|
|
246
246
|
.then(executeMethod)
|
|
247
247
|
.then(onExecutionSuccess)
|
|
248
|
-
.catch(onExecutionFail)
|
|
249
|
-
.done();
|
|
248
|
+
.catch(onExecutionFail);
|
|
250
249
|
|
|
251
250
|
function validateMethod(module) {
|
|
252
251
|
var errorMsg = `Method "${method}" is not found in "${triggerOrAction}" action or trigger`;
|