elasticio-sailor-nodejs 3.0.0-dev4 → 3.0.0-dev6

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/config/local.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "LOG_LEVEL": "trace",
3
- "ELASTICIO_FLOW_ID": "697b56aca8e2e9b35e1952da",
4
- "ELASTICIO_EXEC_ID": "exec_67890",
3
+ "ELASTICIO_FLOW_ID": "692ee23d4ab5d34bb7321559",
4
+ "ELASTICIO_EXEC_ID": "69b26526c4796609ca0da12a",
5
5
  "ELASTICIO_STEP_ID": "step_1",
6
- "ELASTICIO_CONTAINER_ID": "container_abcde",
7
- "ELASTICIO_WORKSPACE_ID": "workspace_xyz",
8
- "ELASTICIO_USER_ID": "user_1122",
9
- "ELASTICIO_COMP_ID": "component_3344",
6
+ "ELASTICIO_CONTAINER_ID": "69b26526c4796609ca0da129",
7
+ "ELASTICIO_WORKSPACE_ID": "69b26526c4796609ca0da128",
8
+ "ELASTICIO_USER_ID": "69b26526c4796609ca0da127",
9
+ "ELASTICIO_COMP_ID": "69b26526c4796609ca0da126",
10
10
  "ELASTICIO_FUNCTION": "data_trigger",
11
11
  "ELASTICIO_API_URI": "http://localhost:9000",
12
- "ELASTICIO_API_USERNAME": "task-697b56aca8e2e9b35e1952da",
13
- "ELASTICIO_API_KEY": "9b2e0465-b00c-4b57-af91-bfc3a830306c",
12
+ "ELASTICIO_API_USERNAME": "task-692ee23d4ab5d34bb7321559",
13
+ "ELASTICIO_API_KEY": "976ffec8-455b-494e-9478-2d66761f4040",
14
14
  "ELASTICIO_MESSAGE_CRYPTO_IV": "0.03091345790184",
15
15
  "ELASTICIO_MESSAGE_CRYPTO_PASSWORD": "password",
16
16
  "ELASTICIO_SAILOR_PROXY_URI": "http://localhost:4001",
@@ -202,6 +202,7 @@ class ProxyClient {
202
202
  return resolve();
203
203
  }
204
204
 
205
+ // TODO: what if incoming message is received during graceful shutdown?
205
206
  this.clientSession.close(() => {
206
207
  log.debug('Successfully closed HTTP2 connection');
207
208
  resolve();
@@ -237,62 +238,16 @@ class ProxyClient {
237
238
  throw new Error('Connection lost and no reconnection in progress');
238
239
  }
239
240
 
240
- async fetchMessageBody(message, logger) {
241
- await this._ensureConnection();
242
-
243
- const { body, headers } = message;
244
-
245
- logger.info('Checking if incoming messages is lightweight...');
246
-
247
- if (!headers) {
248
- logger.info('Empty headers so not lightweight.');
249
- return body;
250
- }
251
-
252
- const { [OBJECT_ID_HEADER]: objectId } = headers;
253
-
254
- if (!objectId) {
255
- logger.trace('No object id header so not lightweight.');
256
- return body;
257
- }
258
-
259
- logger.info('Object id header found, message is lightweight.', { objectId });
260
-
261
- let object;
262
-
263
- logger.info('Going to fetch message body.', { objectId });
264
-
241
+ async _proxyRequestWithRetries(operationName, requestFn) {
265
242
  const maxRetries = this.settings.PROXY_OBJECT_REQUEST_RETRY_ATTEMPTS;
266
243
  const retryDelay = this.settings.PROXY_OBJECT_REQUEST_RETRY_DELAY;
267
244
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
268
245
  try {
269
- object = await new Promise((resolve, reject) => {
270
- const getObjectStream = this.clientSession.request({
271
- [HTTP2_HEADER_PATH]: `/object/${objectId}`,
272
- [HTTP2_HEADER_METHOD]: 'GET',
273
- [HTTP2_HEADER_AUTHORIZATION]: this.authHeader
274
- }).pipe(this._encryptor.createDecipher());
275
-
276
- const chunks = [];
277
- getObjectStream.on('data', chunk => {
278
- chunks.push(chunk);
279
- });
280
- getObjectStream.on('error', (err) => {
281
- logger.error(err, 'Error during fetching message body');
282
- reject(err);
283
- });
284
- getObjectStream.on('end', () => {
285
- logger.info('Message stream ended by server');
286
- const buffer = Buffer.concat(chunks);
287
- logger.info({ messageSize: buffer.length }, 'Received complete message from server');
288
- resolve({ data: JSON.parse(buffer.toString()) });
289
- });
290
- });
291
-
246
+ const result = await requestFn();
292
247
  if (attempt > 0) {
293
- log.info({ attempt, maxRetries }, 'Fetch message body succeeded after retry');
248
+ log.info({ attempt, maxRetries }, `${operationName} succeeded after retry`);
294
249
  }
295
- return objectId;
250
+ return result;
296
251
  } catch (error) {
297
252
  const isLastAttempt = attempt === maxRetries;
298
253
  const isRetryable = error.isNetworkError ||
@@ -306,7 +261,7 @@ class ProxyClient {
306
261
  maxRetries,
307
262
  error: error.message,
308
263
  isRetryable
309
- }, 'Fetch message body failed, no more retries');
264
+ }, `${operationName} failed, no more retries`);
310
265
  throw error;
311
266
  }
312
267
 
@@ -319,121 +274,120 @@ class ProxyClient {
319
274
  maxRetries,
320
275
  error: error.message,
321
276
  nextRetryIn: delay
322
- }, 'Fetch message body failed, retrying...');
277
+ }, `${operationName} failed, retrying...`);
323
278
 
324
279
  await new Promise(resolve => setTimeout(resolve, delay));
325
280
 
326
- // Ensure connection is still valid before retry
327
281
  if (!this.isConnected()) {
328
282
  log.info('Reconnecting before retry...');
329
283
  await this.connect();
330
284
  }
331
285
  }
332
286
  }
333
- logger.info('Successfully obtained message body.', { objectId });
334
- logger.trace('Message body object received');
335
-
336
- return object.data;
337
287
  }
338
288
 
339
- async uploadMessageBody(bodyBuf) {
289
+ async fetchMessageBody(message, logger) {
340
290
  await this._ensureConnection();
341
291
 
342
- const maxRetries = this.settings.PROXY_OBJECT_REQUEST_RETRY_ATTEMPTS;
343
- const retryDelay = this.settings.PROXY_OBJECT_REQUEST_RETRY_DELAY;
344
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
345
- try {
346
- const objectId = await new Promise((resolve, reject) => {
347
- const postMessageStream = this.clientSession.request({
348
- [HTTP2_HEADER_PATH]: '/object',
349
- [HTTP2_HEADER_METHOD]: 'POST',
350
- [HTTP2_HEADER_AUTHORIZATION]: this.authHeader
351
- });
292
+ const { body, headers } = message;
352
293
 
353
- let responseData = '';
354
- let statusCode = null;
294
+ logger.info('Checking if incoming messages is lightweight...');
355
295
 
356
- postMessageStream.on('response', (headers, flags) => {
357
- statusCode = headers[http2.constants.HTTP2_HEADER_STATUS];
358
- if (statusCode !== 200) {
359
- const error = new Error(`Failed to upload message body, status code: ${statusCode}`);
360
- error.statusCode = statusCode;
361
- return reject(error);
362
- }
363
- });
296
+ if (!headers) {
297
+ logger.info('Empty headers so not lightweight.');
298
+ return body;
299
+ }
364
300
 
365
- postMessageStream.on('data', chunk => {
366
- responseData += chunk;
367
- });
301
+ const { [OBJECT_ID_HEADER]: objectId } = headers;
368
302
 
369
- postMessageStream.on('error', (err) => {
370
- log.error(err, 'Error during upload message body');
371
- err.isNetworkError = true;
372
- reject(err);
373
- });
303
+ if (!objectId) {
304
+ logger.trace('No object id header so not lightweight.');
305
+ return body;
306
+ }
374
307
 
375
- postMessageStream.on('end', () => {
376
- if (!responseData) {
377
- return
378
- }
379
- try {
380
- const responseJson = JSON.parse(responseData);
381
- resolve(responseJson.objectId);
382
- } catch (e) {
383
- log.error(e, 'Failed to parse upload message body response');
384
- reject(e);
385
- }
386
- });
308
+ logger.info('Object id header found, message is lightweight.', { objectId });
387
309
 
388
- const cipher = this._encryptor.createCipher();
389
- cipher.pipe(postMessageStream);
390
- cipher.write(bodyBuf);
391
- cipher.end();
392
- });
310
+ logger.info('Going to fetch message body.', { objectId });
393
311
 
394
- // Success - return the objectId
395
- if (attempt > 0) {
396
- log.info({ attempt, maxRetries }, 'Upload message body succeeded after retry');
397
- }
398
- return objectId;
312
+ await this._proxyRequestWithRetries('Fetch message body', () => new Promise((resolve, reject) => {
313
+ const getObjectStream = this.clientSession.request({
314
+ [HTTP2_HEADER_PATH]: `/object/${objectId}`,
315
+ [HTTP2_HEADER_METHOD]: 'GET',
316
+ [HTTP2_HEADER_AUTHORIZATION]: this.authHeader
317
+ }).pipe(this._encryptor.createDecipher());
399
318
 
400
- } catch (error) {
401
- const isLastAttempt = attempt === maxRetries;
402
- const isRetryable = error.isNetworkError ||
403
- (error.statusCode && error.statusCode >= 500) ||
404
- error.code === 'ECONNRESET' ||
405
- error.code === 'ETIMEDOUT';
319
+ const chunks = [];
320
+ getObjectStream.on('data', chunk => {
321
+ chunks.push(chunk);
322
+ });
323
+ getObjectStream.on('error', (err) => {
324
+ logger.error(err, 'Error during fetching message body');
325
+ reject(err);
326
+ });
327
+ getObjectStream.on('end', () => {
328
+ logger.info('Message stream ended by server');
329
+ const buffer = Buffer.concat(chunks);
330
+ logger.info({ messageSize: buffer.length }, 'Received complete message from server');
331
+ resolve({ data: JSON.parse(buffer.toString()) });
332
+ });
333
+ }));
406
334
 
407
- if (!isRetryable || isLastAttempt) {
408
- log.error({
409
- attempt,
410
- maxRetries,
411
- error: error.message,
412
- isRetryable
413
- }, 'Upload message body failed, no more retries');
414
- throw error;
335
+ logger.info('Successfully obtained message body.', { objectId });
336
+ logger.trace('Message body object received');
337
+
338
+ return objectId;
339
+ }
340
+
341
+ async uploadMessageBody(bodyBuf) {
342
+ await this._ensureConnection();
343
+
344
+ return this._proxyRequestWithRetries('Upload message body', () => new Promise((resolve, reject) => {
345
+ const postMessageStream = this.clientSession.request({
346
+ [HTTP2_HEADER_PATH]: '/object',
347
+ [HTTP2_HEADER_METHOD]: 'POST',
348
+ [HTTP2_HEADER_AUTHORIZATION]: this.authHeader
349
+ });
350
+
351
+ let responseData = '';
352
+ let statusCode = null;
353
+
354
+ postMessageStream.on('response', (headers, flags) => {
355
+ statusCode = headers[http2.constants.HTTP2_HEADER_STATUS];
356
+ if (statusCode !== 200) {
357
+ const error = new Error(`Failed to upload message body, status code: ${statusCode}`);
358
+ error.statusCode = statusCode;
359
+ return reject(error);
415
360
  }
361
+ });
416
362
 
417
- const delay = Math.min(
418
- retryDelay * Math.pow(2, attempt),
419
- this.settings.PROXY_OBJECT_REQUEST_MAX_RETRY_DELAY
420
- );
421
- log.warn({
422
- attempt,
423
- maxRetries,
424
- error: error.message,
425
- nextRetryIn: delay
426
- }, 'Upload message body failed, retrying...');
363
+ postMessageStream.on('data', chunk => {
364
+ responseData += chunk;
365
+ });
427
366
 
428
- await new Promise(resolve => setTimeout(resolve, delay));
367
+ postMessageStream.on('error', (err) => {
368
+ log.error(err, 'Error during upload message body');
369
+ err.isNetworkError = true;
370
+ reject(err);
371
+ });
429
372
 
430
- // Ensure connection is still valid before retry
431
- if (!this.isConnected()) {
432
- log.info('Reconnecting before retry...');
433
- await this.connect();
373
+ postMessageStream.on('end', () => {
374
+ if (!responseData) {
375
+ return
434
376
  }
435
- }
436
- }
377
+ try {
378
+ const responseJson = JSON.parse(responseData);
379
+ resolve(responseJson.objectId);
380
+ } catch (e) {
381
+ log.error(e, 'Failed to parse upload message body response');
382
+ reject(e);
383
+ }
384
+ });
385
+
386
+ const cipher = this._encryptor.createCipher();
387
+ cipher.pipe(postMessageStream);
388
+ cipher.write(bodyBuf);
389
+ cipher.end();
390
+ }));
437
391
  }
438
392
 
439
393
  async listenForMessages(messageHandler) {
@@ -542,33 +496,36 @@ class ProxyClient {
542
496
  type,
543
497
  ...(customRoutingKey ? { customRoutingKey } : {})
544
498
  }).toString();
545
- // TODO: Add retries
546
- const postMessageStream = this.clientSession.request({
547
- ...proxyHeaders,
548
- [HTTP2_HEADER_PATH]: `/message?${queryParams}`,
549
- [HTTP2_HEADER_METHOD]: 'POST',
550
- [HTTP2_HEADER_AUTHORIZATION]: this.authHeader
551
- });
552
- postMessageStream.write(encryptedData);
553
- postMessageStream.end();
554
499
 
555
- return new Promise((resolve, reject) => {
500
+ await this._proxyRequestWithRetries('Send message', () => new Promise((resolve, reject) => {
501
+ const postMessageStream = this.clientSession.request({
502
+ ...proxyHeaders,
503
+ [HTTP2_HEADER_PATH]: `/message?${queryParams}`,
504
+ [HTTP2_HEADER_METHOD]: 'POST',
505
+ [HTTP2_HEADER_AUTHORIZATION]: this.authHeader
506
+ });
507
+ postMessageStream.write(encryptedData);
508
+ postMessageStream.end();
509
+
556
510
  postMessageStream.on('response', (headers) => {
557
511
  log.debug({ status: headers[HTTP2_HEADER_STATUS] }, 'Send message response');
558
512
  if (headers[HTTP2_HEADER_STATUS] !== 200) {
559
513
  log.error({ headers }, 'Failed to send message');
560
- return reject(new Error(`Failed to send message, status code: ${headers[HTTP2_HEADER_STATUS]}`));
514
+ const error = new Error(`Failed to send message, status code: ${headers[HTTP2_HEADER_STATUS]}`);
515
+ error.statusCode = headers[HTTP2_HEADER_STATUS];
516
+ return reject(error);
561
517
  }
562
518
  });
563
519
  postMessageStream.on('error', (err) => {
564
520
  log.error(err, 'Error during sending message');
521
+ err.isNetworkError = true;
565
522
  reject(err);
566
523
  });
567
524
  postMessageStream.on('end', () => {
568
525
  log.debug('Send message end event');
569
526
  resolve();
570
527
  });
571
- });
528
+ }));
572
529
  }
573
530
 
574
531
  _decodeMessage(originalMessage, headers) {
@@ -620,30 +577,33 @@ class ProxyClient {
620
577
  incomingMessageId,
621
578
  status
622
579
  }).toString();
623
- // TODO: Add retries
624
- const postMessageStream = this.clientSession.request({
625
- [HTTP2_HEADER_PATH]: `/finish-processing?${queryParams}`,
626
- [HTTP2_HEADER_METHOD]: 'POST',
627
- [HTTP2_HEADER_AUTHORIZATION]: this.authHeader
628
- });
629
- postMessageStream.end();
630
580
 
631
- return new Promise((resolve, reject) => {
581
+ await this._proxyRequestWithRetries('Finish processing', () => new Promise((resolve, reject) => {
582
+ const postMessageStream = this.clientSession.request({
583
+ [HTTP2_HEADER_PATH]: `/finish-processing?${queryParams}`,
584
+ [HTTP2_HEADER_METHOD]: 'POST',
585
+ [HTTP2_HEADER_AUTHORIZATION]: this.authHeader
586
+ });
587
+ postMessageStream.end();
588
+
632
589
  postMessageStream.on('response', (headers) => {
633
590
  log.debug({ status: headers[HTTP2_HEADER_STATUS] }, 'Finish processing response event');
634
591
  if (headers[HTTP2_HEADER_STATUS] !== 200) {
635
592
  log.error({ headers }, 'Failed to finish processing message');
636
- return reject(new Error(`Failed to finish processing message, status code: ${headers[HTTP2_HEADER_STATUS]}`));
593
+ const error = new Error(`Failed to finish processing message, status code: ${headers[HTTP2_HEADER_STATUS]}`);
594
+ error.statusCode = headers[HTTP2_HEADER_STATUS];
595
+ return reject(error);
637
596
  }
638
597
  });
639
-
640
598
  postMessageStream.on('end', () => {
641
599
  log.debug('Finish processing end event');
642
600
  resolve();
643
601
  });
644
-
645
- postMessageStream.on('error', reject);
646
- });
602
+ postMessageStream.on('error', (err) => {
603
+ err.isNetworkError = true;
604
+ reject(err);
605
+ });
606
+ }));
647
607
  }
648
608
 
649
609
  encryptMessageContent(body, protocolVersion = 1) {
package/lib/sailor.js CHANGED
@@ -80,6 +80,7 @@ class Sailor {
80
80
  }
81
81
 
82
82
  async disconnect() {
83
+ // TODO: delete if not needed (currently used only in old tests)
83
84
  log.debug('Disconnecting, %s messages in processing', this.messagesCount);
84
85
  return this.proxyClient.disconnect();
85
86
  }
@@ -194,7 +195,6 @@ class Sailor {
194
195
  return new Promise(resolve => this.shutdownCallback = resolve);
195
196
  }
196
197
 
197
- // TODO: remove duplicate disconnect call (also in run.js)
198
198
  await this.proxyClient.disconnect();
199
199
  if (this.messagesCount === 0) {
200
200
  // there is no unfinished processMessage invocation, let's just resolve scheduleShutdown now
package/mise.toml ADDED
@@ -0,0 +1,2 @@
1
+ [tools]
2
+ node = "18.16.1"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "elasticio-sailor-nodejs",
3
3
  "description": "The official elastic.io library for bootstrapping and executing for Node.js connectors",
4
- "version": "3.0.0-dev4",
4
+ "version": "3.0.0-dev6",
5
5
  "main": "run.js",
6
6
  "scripts": {
7
7
  "build": "tsc",
package/run.js CHANGED
@@ -57,23 +57,6 @@ async function putOutToSea(settings, ipc) {
57
57
  ipc.send('init:ended');
58
58
  }
59
59
 
60
- async function disconnectAndExit() {
61
- if (!disconnectRequired) {
62
- return;
63
- }
64
- disconnectRequired = false;
65
-
66
- try {
67
- logger.info('Disconnecting...');
68
- await sailor.disconnect();
69
- logger.info('Successfully disconnected');
70
- process.exit();
71
- } catch (err) {
72
- logger.error(err, 'Unable to disconnect');
73
- process.exit(-1);
74
- }
75
- }
76
-
77
60
  async function gracefulShutdown() {
78
61
  if (!disconnectRequired) {
79
62
  return;
@@ -90,8 +73,15 @@ async function gracefulShutdown() {
90
73
  await sailorInit;
91
74
  logger.trace('Waited an init before graceful shutdown');
92
75
 
93
- await sailor.scheduleShutdown();
94
- await disconnectAndExit();
76
+ try {
77
+ logger.info('Disconnecting...');
78
+ await sailor.scheduleShutdown();
79
+ logger.info('Successfully disconnected');
80
+ process.exit();
81
+ } catch (err) {
82
+ logger.error(err, 'Unable to disconnect');
83
+ process.exit(-1);
84
+ }
95
85
  }
96
86
 
97
87
  async function run(settings, ipc) {