elasticio-sailor-nodejs 2.7.0-dev2 → 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/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`;
package/lib/settings.js CHANGED
@@ -1,39 +1,8 @@
1
1
  const _ = require('lodash');
2
2
 
3
-
4
3
  const PREFIX = 'ELASTICIO_';
5
4
 
6
- function readFrom(envVars) {
7
- const result = {};
8
-
9
- // required settings
10
- const requiredAlways = [
11
- 'FLOW_ID',
12
- 'EXEC_ID',
13
- 'STEP_ID',
14
- 'CONTAINER_ID',
15
- 'WORKSPACE_ID',
16
-
17
- 'USER_ID',
18
- 'COMP_ID',
19
- 'FUNCTION',
20
-
21
- 'API_URI',
22
- 'API_USERNAME',
23
- 'API_KEY'
24
- ];
25
-
26
- const requiredForMessageProcessing = [
27
- 'AMQP_URI',
28
- 'LISTEN_MESSAGES_ON',
29
- 'PUBLISH_MESSAGES_TO',
30
-
31
- 'DATA_ROUTING_KEY',
32
- 'ERROR_ROUTING_KEY',
33
- 'REBOUND_ROUTING_KEY',
34
- 'SNAPSHOT_ROUTING_KEY'
35
- ];
36
-
5
+ function getOptionalEnvVars(envVars) {
37
6
  const optional = {
38
7
  REBOUND_INITIAL_EXPIRATION: 15000,
39
8
  REBOUND_LIMIT: 20,
@@ -51,15 +20,40 @@ function readFrom(envVars) {
51
20
  AMQP_PUBLISH_RETRY_DELAY: 100, // 100ms
52
21
  AMQP_PUBLISH_RETRY_ATTEMPTS: Infinity,
53
22
  AMQP_PUBLISH_MAX_RETRY_DELAY: 5 * 60 * 1000, // 5 mins
23
+ AMQP_PERSISTENT_MESSAGES: false,
54
24
  OUTGOING_MESSAGE_SIZE_LIMIT: 10485760,
55
25
  NO_SELF_PASSTRHOUGH: false,
56
26
  PROTOCOL_VERSION: 1,
27
+ NO_ERROR_REPLIES: false,
28
+ INPUT_FORMAT: 'default',
57
29
  OBJECT_STORAGE_URI: null,
58
30
  OBJECT_STORAGE_TOKEN: null,
59
31
  OBJECT_STORAGE_SIZE_THRESHOLD: 1048576,
60
- EMIT_LIGHTWEIGHT_MESSAGE: false
32
+ EMIT_LIGHTWEIGHT_MESSAGE: false,
33
+ AMQP_RECONNECT_ATTEMPTS: 3,
34
+ AMQP_RECONNECT_TIMEOUT: 100,
35
+ WAIT_MESSAGES_TIMEOUT: 50
61
36
  };
62
37
 
38
+ const result = {};
39
+ _.forEach(optional, function readOptional(defaultValue, key) {
40
+ const envVarName = PREFIX + key;
41
+ if (typeof defaultValue === 'number' && envVars[envVarName]) {
42
+ result[key] = parseInt(envVars[envVarName]) || defaultValue;
43
+ } else if (typeof defaultValue === 'boolean') {
44
+ if (envVars[envVarName] === undefined) {
45
+ result[key] = defaultValue;
46
+ } else {
47
+ result[key] = (!envVars[envVarName] || envVars[envVarName] === 'false') ? false : true;
48
+ }
49
+ } else {
50
+ result[key] = envVars[envVarName] || defaultValue;
51
+ }
52
+ });
53
+ return result;
54
+ }
55
+
56
+ function getAdditionalVars(envVars) {
63
57
  if (envVars.ELASTICIO_ADDITIONAL_VARS_FOR_HEADERS) {
64
58
  const vars = {};
65
59
  envVars.ELASTICIO_ADDITIONAL_VARS_FOR_HEADERS
@@ -69,39 +63,64 @@ function readFrom(envVars) {
69
63
  const key = env.indexOf(PREFIX) === 0 ? env.slice(PREFIX.length) : env;
70
64
  vars[key] = envVars[env];
71
65
  });
72
- result.additionalVars = vars;
66
+
67
+ return vars;
73
68
  }
69
+ }
70
+
71
+ function getMandatoryEnvVars(envVars) {
72
+ // required settings
73
+ const requiredAlways = [
74
+ 'FLOW_ID',
75
+ 'EXEC_ID',
76
+ 'STEP_ID',
77
+ 'CONTAINER_ID',
78
+ 'WORKSPACE_ID',
79
+
80
+ 'USER_ID',
81
+ 'COMP_ID',
82
+ 'FUNCTION',
83
+
84
+ 'API_URI',
85
+ 'API_USERNAME',
86
+ 'API_KEY'
87
+ ];
88
+
89
+ const requiredForMessageProcessing = [
90
+ 'AMQP_URI',
91
+ 'LISTEN_MESSAGES_ON',
92
+ 'PUBLISH_MESSAGES_TO',
93
+
94
+ 'DATA_ROUTING_KEY',
95
+ 'ERROR_ROUTING_KEY',
96
+ 'REBOUND_ROUTING_KEY',
97
+ 'SNAPSHOT_ROUTING_KEY',
98
+ 'MESSAGE_CRYPTO_IV',
99
+ 'MESSAGE_CRYPTO_PASSWORD'
100
+ ];
101
+
74
102
  const envVarsList = requiredAlways.slice(0);
75
103
 
76
104
  if (!envVars.ELASTICIO_HOOK_SHUTDOWN) {
77
105
  envVarsList.push(...requiredForMessageProcessing);
78
106
  }
79
107
 
80
- envVarsList.forEach(key => {
108
+ return envVarsList.reduce((result, key) => {
81
109
  const envVarName = PREFIX + key;
82
110
  if (!envVars[envVarName]) {
83
111
  throw new Error(`${envVarName} is missing`);
84
112
  }
85
113
  result[key] = envVars[envVarName];
86
- });
87
-
88
- _.forEach(optional, function readOptional(defaultValue, key) {
89
- const envVarName = PREFIX + key;
90
- const val = envVars[envVarName];
91
- if (typeof defaultValue === 'number' && val) {
92
- result[key] = parseInt(val) || defaultValue;
93
- } else if (typeof defaultValue === 'boolean') {
94
- if (typeof val === 'undefined') {
95
- result[key] = defaultValue;
96
- } else {
97
- result[key] = val === 'false' ? false : Boolean(val);
98
- }
99
- } else {
100
- result[key] = val || defaultValue;
101
- }
102
- });
114
+ return result;
115
+ }, {});
116
+ }
103
117
 
104
- return result;
118
+ function readFrom(envVars) {
119
+ return {
120
+ ...getMandatoryEnvVars(envVars),
121
+ ...getOptionalEnvVars(envVars),
122
+ additionalVars: getAdditionalVars(envVars)
123
+ };
105
124
  }
106
125
 
107
126
  exports.readFrom = readFrom;
package/package.json CHANGED
@@ -1,9 +1,10 @@
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": "2.7.0-dev2",
4
+ "version": "2.7.0",
5
5
  "main": "run.js",
6
6
  "scripts": {
7
+ "audit": "better-npm-audit audit --level high --production",
7
8
  "lint": "./node_modules/.bin/eslint lib spec mocha_spec lib run.js runService.js",
8
9
  "pretest": "npm run lint",
9
10
  "test": "npm run test:jasmine && npm run test:mocha",
@@ -15,34 +16,36 @@
15
16
  "node": ">=12.13.0"
16
17
  },
17
18
  "dependencies": {
18
- "@elastic.io/object-storage-client": "0.0.1-dev.12",
19
- "amqplib": "0.5.1",
19
+ "@elastic.io/object-storage-client": "2.1.0",
20
+ "amqplib": "0.8.0",
20
21
  "bunyan": "1.8.10",
21
22
  "co": "4.6.0",
22
23
  "debug": "3.1.0",
23
- "elasticio-rest-node": "1.2.5",
24
+ "elasticio-rest-node": "1.2.7",
24
25
  "event-to-promise": "0.8.0",
25
- "lodash": "4.17.15",
26
+ "lodash": "4.17.21",
26
27
  "p-throttle": "2.1.0",
27
- "q": "1.4.1",
28
+ "q": "1.5.1",
28
29
  "request-promise-native": "1.0.5",
29
- "requestretry": "3.1.0",
30
+ "requestretry": "7.0.2",
30
31
  "uuid": "3.0.1"
31
32
  },
32
33
  "devDependencies": {
34
+ "better-npm-audit": "3.7.3",
33
35
  "chai": "4.2.0",
34
36
  "del": "2.2.2",
35
37
  "eslint": "4.19.1",
36
38
  "eslint-plugin-mocha": "4.12.1",
37
39
  "express": "4.16.4",
38
40
  "gulp": "3.9.1",
39
- "gulp-istanbul": "1.1.1",
41
+ "gulp-istanbul": "1.1.3",
40
42
  "gulp-jasmine": "0.2.0",
41
43
  "jasmine-node": "3.0.0",
42
44
  "mocha": "7.1.2",
43
- "nock": "^12.0.3",
45
+ "nock": "12.0.3",
46
+ "rabbitmq-stats": "1.2.4",
44
47
  "request": "2.88.0",
45
- "sinon": "^9.0.2",
48
+ "sinon": "9.0.2",
46
49
  "sinon-chai": "3.5.0"
47
50
  },
48
51
  "repository": "elasticio/sailor-nodejs",
package/run.js CHANGED
@@ -1,12 +1,32 @@
1
+ /**
2
+ * Entrypoint for starting task step.
3
+ */
1
4
  const logger = require('./lib/logging.js');
2
5
  const Sailor = require('./lib/sailor.js').Sailor;
3
6
  const settings = require('./lib/settings.js');
4
- const co = require('co');
7
+ const { IPC } = require('./lib/ipc.js');
8
+ const Q = require('q');
9
+ const http = require('http');
10
+ const https = require('https');
5
11
 
6
12
  let sailor;
13
+ let sailorInit;
7
14
  let disconnectRequired;
8
15
 
9
- async function putOutToSea(settings) {
16
+ function prepareSandbox() {
17
+ // enable keep alive by default to handle issues like https://github.com/elasticio/elasticio/issues/4874
18
+ http.globalAgent = new http.Agent({
19
+ keepAlive: true
20
+ });
21
+ https.globalAgent = new https.Agent({
22
+ keepAlive: true
23
+ });
24
+ }
25
+
26
+ async function putOutToSea(settings, ipc) {
27
+ ipc.send('init:started');
28
+ const deferred = Q.defer();
29
+ sailorInit = deferred.promise;
10
30
  sailor = new Sailor(settings);
11
31
 
12
32
  //eslint-disable-next-line no-extra-boolean-cast
@@ -31,32 +51,28 @@ async function putOutToSea(settings) {
31
51
 
32
52
  await sailor.runHookInit();
33
53
  await sailor.run();
54
+ deferred.resolve();
55
+ ipc.send('init:ended');
34
56
  }
35
57
 
36
- function disconnectAndExit() {
58
+ async function disconnectAndExit() {
37
59
  if (!disconnectRequired) {
38
60
  return;
39
61
  }
40
62
  disconnectRequired = false;
41
- co(function* putIn() {
63
+
64
+ try {
42
65
  logger.info('Disconnecting...');
43
- yield sailor.disconnect();
66
+ await sailor.disconnect();
44
67
  logger.info('Successfully disconnected');
45
68
  process.exit();
46
- }).catch((err) => {
47
- logger.error('Unable to disconnect', err.stack);
69
+ } catch (err) {
70
+ logger.error(err, 'Unable to disconnect');
48
71
  process.exit(-1);
49
- });
50
- }
51
-
52
- function _disconnectOnly() {
53
- if (!disconnectRequired) {
54
- return Promise.resolve();
55
72
  }
56
- return sailor.disconnect();
57
73
  }
58
74
 
59
- function gracefulShutdown() {
75
+ async function gracefulShutdown() {
60
76
  if (!disconnectRequired) {
61
77
  return;
62
78
  }
@@ -66,22 +82,42 @@ function gracefulShutdown() {
66
82
  return;
67
83
  }
68
84
 
69
- sailor.scheduleShutdown().then(disconnectAndExit);
85
+ // we connect to amqp, create channels, start listen a queue on init and interrupting this process with 'disconnect'
86
+ // will lead to undefined behaviour
87
+ logger.trace('Checking/waiting for init before graceful shutdown');
88
+ await sailorInit;
89
+ logger.trace('Waited an init before graceful shutdown');
90
+
91
+ await sailor.scheduleShutdown();
92
+ await disconnectAndExit();
70
93
  }
71
94
 
72
- async function run(settings) {
95
+ async function run(settings, ipc) {
96
+ prepareSandbox();
73
97
  try {
74
- await putOutToSea(settings);
98
+ await putOutToSea(settings, ipc);
99
+ logger.info('Fully initialized and waiting for messages');
75
100
  } catch (e) {
76
- if (sailor) {
101
+ if (sailor && !sailor.amqpConnection.closed) {
77
102
  await sailor.reportError(e);
78
103
  }
79
104
  logger.criticalErrorAndExit('putOutToSea.catch', e);
80
105
  }
81
106
  }
82
107
 
83
- exports._disconnectOnly = _disconnectOnly;
108
+ exports.__test__ = {
109
+ disconnectOnly: function disconnectOnly() {
110
+ if (!disconnectRequired) {
111
+ return Promise.resolve();
112
+ }
113
+ return sailor.disconnect();
114
+ },
115
+ closeConsumerChannel: function closeConsumerChannel() {
116
+ return sailor.amqpConnection.consumerChannel.close();
117
+ }
118
+ };
84
119
  exports.run = run;
120
+ exports.putOutToSea = putOutToSea;
85
121
 
86
122
  if (require.main === module || process.mainModule.filename === __filename) {
87
123
  process.on('SIGTERM', function onSigterm() {
@@ -95,6 +131,9 @@ if (require.main === module || process.mainModule.filename === __filename) {
95
131
  });
96
132
 
97
133
  process.on('uncaughtException', logger.criticalErrorAndExit.bind(logger, 'process.uncaughtException'));
134
+ process.on('unhandledRejection', (err) => logger.error(err, 'process.unhandledRejection'));
135
+
136
+ const ipc = new IPC();
98
137
 
99
- run(settings.readFrom(process.env));
138
+ run(settings.readFrom(process.env), ipc);
100
139
  }
package/runService.js CHANGED
@@ -1,3 +1,6 @@
1
+ /**
2
+ * Entrypoint for starting service which will handle verifyCredentials and selectModel requests.
3
+ */
1
4
  const logger = require('./lib/logging');
2
5
  const service = require('./lib/service');
3
6
  const debug = require('debug')('sailor');
@@ -12,4 +15,5 @@ service.processService(serviceMethod, process.env)
12
15
  process.exit(0);
13
16
  });
14
17
 
15
- process.on('uncaughtException', logger.criticalErrorAndExit.bind(logger, 'runService.uncaughtException'));
18
+ process.on('uncaughtException', logger.criticalErrorAndExit.bind(logger, 'process.uncaughtException'));
19
+ process.on('unhandledRejection', (err) => logger.error(err, 'process.unhandledRejection'));
package/.travis.yml DELETED
@@ -1,8 +0,0 @@
1
- language: node_js
2
- node_js:
3
- - "8"
4
- - "9"
5
- - "10"
6
- - "11"
7
- script:
8
- - npm run test:jasmine
package/Procfile DELETED
@@ -1,2 +0,0 @@
1
- sail: node run.js
2
- createQueues: node createQueues.js
package/createQueues.js DELETED
@@ -1,148 +0,0 @@
1
- var amqp = require('./lib/amqp.js');
2
- var settings = require('./lib/settings.js').readFrom(process.env);
3
- var Q = require('q');
4
- var util = require('util');
5
-
6
- var execId = "1432205514864";
7
-
8
- var task = {
9
- "id" : "5559edd38968ec0736000003",
10
- "user" : "5527f0ea43238e5d5f000001",
11
- "data" : {
12
- "step_2" : {
13
- "_account" : "554b53aed5178d6540000001"
14
- },
15
- "step_3" : {
16
- "mapper" : {
17
- "data" : {
18
- "qty" : "2"
19
- },
20
- "product" : "Btestsku"
21
- }
22
- },
23
- "step_1" : {
24
- "interval" : "minute",
25
- "_account" : "5559ed6b8968ec0736000002"
26
- }
27
- },
28
- "recipe" : {
29
- "nodes" : [
30
- {
31
- "first" : true,
32
- "id" : "step_1",
33
- "function" : "getProducts",
34
- "compId" : "shopware"
35
- },
36
- {
37
- "id" : "step_3",
38
- "function" : "map",
39
- "compId" : "mapper"
40
- },
41
- {
42
- "id" : "step_2",
43
- "function" : "updateInventory",
44
- "compId" : "magento"
45
- }
46
- ],
47
- "connections" : [
48
- {
49
- "to" : "step_3",
50
- "from" : "step_1"
51
- },
52
- {
53
- "to" : "step_2",
54
- "from" : "step_3"
55
- }
56
- ]
57
- }
58
- };
59
-
60
-
61
- var amqpConnection = new amqp.AMQPConnection(settings);
62
- amqpConnection.connect(settings.AMQP_URI).then(function(){
63
- сreateQueuesAndExchanges(execId, task, "step_1", "step_2");
64
- });
65
-
66
- function сreateQueuesAndExchanges(execId, task, stepId, nextStepId){
67
-
68
- var EXCHANGE_NAME = 'userexchange:' + task.user;
69
-
70
- var MESSAGE_TAG = util.format('%s:%s:%s:message', task.id, stepId, execId);
71
- var ERROR_TAG = util.format('%s:%s:%s:error', task.id, stepId, execId);
72
- var REBOUND_TAG = util.format('%s:%s:%s:rebound', task.id, stepId, execId);
73
-
74
- var MESSAGES_QUEUE = util.format('%s:%s:%s:messages', task.id, stepId, execId);
75
- var MESSAGES_LISTENING_QUEUE = util.format('%s:%s:%s:messages', task.id, nextStepId, execId);
76
- var REBOUNDS_QUEUE = util.format('%s:%s:%s:rebounds', task.id, stepId, execId);
77
-
78
- console.log('INCOMING_MESSAGES_QUEUE=%s', MESSAGES_QUEUE);
79
- console.log('EXCHANGE_NAME=%s', EXCHANGE_NAME);
80
- console.log('MESSAGE_TAG=%s', MESSAGE_TAG);
81
- console.log('ERROR_TAG=%s', ERROR_TAG);
82
- console.log('REBOUND_TAG=%s', REBOUND_TAG);
83
- console.log('MESSAGES_QUEUE=%s', MESSAGES_QUEUE);
84
- console.log('REBOUNDS_QUEUE=%s', REBOUNDS_QUEUE);
85
-
86
- var userExchange = {
87
- name: EXCHANGE_NAME,
88
- type: 'direct',
89
- options: {
90
- durable: true,
91
- autoDelete: false
92
- }
93
- };
94
-
95
- var messagesQueue = {
96
- name: MESSAGES_QUEUE,
97
- options: {
98
- durable: true,
99
- autoDelete: false
100
- }
101
- };
102
-
103
- var messagesListeningQueue = {
104
- name: MESSAGES_LISTENING_QUEUE,
105
- options: {
106
- durable: true,
107
- autoDelete: false
108
- }
109
- };
110
-
111
- var REBOUND_QUEUE_TTL = 10 * 60 * 1000; // 10 min
112
-
113
- var reboundsQueue = {
114
- name: REBOUNDS_QUEUE,
115
- options: {
116
- durable: true,
117
- autoDelete: false,
118
- arguments: {
119
- 'x-message-ttl': REBOUND_QUEUE_TTL,
120
- 'x-dead-letter-exchange': EXCHANGE_NAME, // send dead rebounded queues back to exchange
121
- 'x-dead-letter-routing-key': MESSAGE_TAG // with tag as message
122
- }
123
- }
124
- };
125
-
126
- return Q.all([
127
- assertExchange(amqpConnection.publishChannel, userExchange), // check that exchange exists
128
- assertQueue(amqpConnection.publishChannel, messagesQueue), // create messages queue
129
- assertQueue(amqpConnection.publishChannel, messagesListeningQueue), // create messages queue
130
- amqpConnection.publishChannel.bindQueue(messagesListeningQueue.name, userExchange.name, MESSAGE_TAG),
131
- assertQueue(amqpConnection.publishChannel, reboundsQueue), // create rebounds queue
132
- amqpConnection.publishChannel.bindQueue(reboundsQueue.name, userExchange.name, REBOUND_TAG)
133
- ]).then(function(){
134
- console.log('Successfully asserted all queues');
135
- }).done();
136
-
137
- function assertQueue(channel, queue) {
138
- return channel.assertQueue(queue.name, queue.options).then(function assertQueueSuccess() {
139
- console.log('Succesfully asserted queue: ' + queue.name);
140
- });
141
- }
142
-
143
- function assertExchange(channel, exchange) {
144
- return channel.assertExchange(exchange.name, exchange.type, exchange.options).then(function assertExchangeSuccess() {
145
- console.log('Succesfully asserted exchange: ' + exchange.name);
146
- });
147
- }
148
- }
package/gulpfile.js DELETED
@@ -1,31 +0,0 @@
1
- var gulp = require('gulp');
2
- var jasmine = require('gulp-jasmine');
3
- var istanbul = require('gulp-istanbul');
4
- var del = require('del');
5
-
6
- var paths = {
7
- code: ['./lib/*.js'],
8
- spec: ['./spec/**/*.spec.js'],
9
- coverageReport: './coverage/lcov.info',
10
- coverage: './coverage'
11
- };
12
-
13
- gulp.task('clean:coverage', function (cb) {
14
- del([paths.coverage], cb);
15
- });
16
-
17
- gulp.task('istanbul', function (cb) {
18
- gulp
19
- .src(paths.code)
20
- .pipe(istanbul()) // Covering files
21
- .pipe(istanbul.hookRequire()) // Force `require` to return covered files
22
- .on('finish', function () {
23
- gulp
24
- .src(paths.spec)
25
- .pipe(jasmine())
26
- .pipe(istanbul.writeReports()) // Creating the reports after tests runned
27
- .on('end', cb);
28
- });
29
- });
30
-
31
- gulp.task('coverage', ['clean:coverage', 'istanbul']);