elasticio-sailor-nodejs 2.6.24 → 2.6.26-dev1

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 CHANGED
@@ -1,3 +1,12 @@
1
+ ## 2.6.26 (June 24, 2021)
2
+
3
+ * Replaced exit on "unhandledRejection" with error logging
4
+
5
+ ## 2.6.25 (June 17, 2021)
6
+
7
+ * Improve consume performance by switching back to pushing instead of polling, keeping reconnections mechanism
8
+ ([#5432](https://github.com/elasticio/elasticio/issues/5432))
9
+
1
10
  ## 2.6.24 (January 27, 2021)
2
11
 
3
12
  * Fix "Big messages processing is not stable" errors ([#5051](https://github.com/elasticio/elasticio/issues/5051))
@@ -23,7 +32,7 @@
23
32
  * Separate connections for consuming and publishing
24
33
  * Consuming is done with polling instead of pushing
25
34
  * Reconnects on connection errors
26
- * Hadling consumer cancel notification
35
+ * Handling consumer cancel notification
27
36
  * Lowered log levels of some developers' log messages
28
37
  * Addded env vars:
29
38
  * AMQP_RECONNECT_ATTEMPTS - number of retries on connection close
package/lib/amqp.js CHANGED
@@ -5,18 +5,17 @@ const Encryptor = require('./encryptor.js');
5
5
  const _ = require('lodash');
6
6
  const eventToPromise = require('event-to-promise');
7
7
  const uuid = require('uuid');
8
+ const os = require('os');
8
9
 
9
10
  const HEADER_ROUTING_KEY = 'x-eio-routing-key';
10
11
  const HEADER_ERROR_RESPONSE = 'x-eio-error-response';
11
12
 
12
-
13
13
  class Amqp {
14
14
  constructor(settings) {
15
- this.reconectCount = 0;
16
15
  this.settings = settings;
17
16
  this._encryptor = new Encryptor(this.settings.MESSAGE_CRYPTO_PASSWORD, this.settings.MESSAGE_CRYPTO_IV);
18
17
  this.closed = true;
19
- this.consume = false;
18
+ this.consume = undefined;
20
19
  }
21
20
 
22
21
  async connect() {
@@ -25,7 +24,7 @@ class Amqp {
25
24
  }
26
25
 
27
26
  async disconnect() {
28
- this.stopConsume();
27
+ await this.stopConsume();
29
28
  this.closed = true;
30
29
  log.trace('Close AMQP connections');
31
30
  if (this._readConnection) {
@@ -53,7 +52,7 @@ class Amqp {
53
52
  try {
54
53
  await this._readConnection.close();
55
54
  } catch (alreadyClosed) {
56
- log.debug('AMQP raed connection is closed already');
55
+ log.debug('AMQP read connection is closed already');
57
56
  }
58
57
  try {
59
58
  await this._writeConnection.close();
@@ -73,12 +72,14 @@ class Amqp {
73
72
  } while (!this._readConnection);
74
73
  return this._readConnection;
75
74
  }
75
+ log.debug('Creating new read connection');
76
76
  this._creatingReadConnection = true;
77
- this._readConnection = await this._createConnection();
77
+ this._readConnection = await this._createConnection('read');
78
78
  this._creatingReadConnection = false;
79
+ log.debug('Read connection created');
79
80
  this._readConnection.on('error', err => log.error({ err }, 'AMQP read Connection error'));
80
81
  this._readConnection.once('close', err => {
81
- log.error({ err }, 'Unexepected connection close.');
82
+ log.error({ err }, 'Unexpected connection close.');
82
83
  delete this._readConnection;
83
84
  });
84
85
  return this._readConnection;
@@ -94,12 +95,14 @@ class Amqp {
94
95
  } while (!this._writeConnection);
95
96
  return this._writeConnection;
96
97
  }
98
+ log.debug('Creating new write connection');
97
99
  this._creatingWriteConnection = true;
98
- this._writeConnection = await this._createConnection();
100
+ this._writeConnection = await this._createConnection('write');
99
101
  this._creatingWriteConnection = false;
102
+ log.debug('Write connection created');
100
103
  this._writeConnection.on('error', err => log.error({ err }, 'AMQP write Connection error'));
101
104
  this._writeConnection.once('close', err => {
102
- log.error({ err }, 'Unexepected connection close.');
105
+ log.error({ err }, 'Unexpected connection close.');
103
106
  delete this._writeConnection;
104
107
  });
105
108
  return this._writeConnection;
@@ -115,14 +118,24 @@ class Amqp {
115
118
  } while (!this.consumerChannel);
116
119
  return this.consumerChannel;
117
120
  }
121
+ log.debug({ prefetch: this.settings.RABBITMQ_PREFETCH_SAILOR }, 'Creating new consume channel');
118
122
  this._creatingConsumerChannel = true;
119
123
  const amqp = await this._ensureReadConnection();
120
124
  this.consumerChannel = await amqp.createChannel();
125
+ log.debug('Consume channel created');
121
126
  this._creatingConsumerChannel = false;
127
+ this.consumerChannel.prefetch(this.settings.RABBITMQ_PREFETCH_SAILOR);
122
128
  this.consumerChannel.on('error', err => log.error({ err }, 'Consumer channel error'));
123
129
  this.consumerChannel.once('close', () => {
124
- this.consumerChannel.closed = true;
125
130
  delete this.consumerChannel;
131
+ if (this.consume) {
132
+ log.warn('Channel unexpectedly closed, but we were listening. Reconnecting and re-listening queue');
133
+ this.consume.consumerTag = undefined;
134
+ // when RabbitMQ closes connection, amqplib will first emit 'close' for channel and then for connection
135
+ // we use setImmediate to wait for connection 'close' event which will unset connection property
136
+ // otherwise listenQueue will try to create channel on closing connection and fail hard
137
+ setImmediate(() => this.listenQueue(this.consume.queue, this.consume.messageHandler));
138
+ }
126
139
  });
127
140
  return this.consumerChannel;
128
141
  }
@@ -137,11 +150,12 @@ class Amqp {
137
150
  } while (!this.publishChannel);
138
151
  return this.publishChannel;
139
152
  }
153
+ log.debug('Creating new publish connection and channel');
140
154
  this._creatingPublishChannel = true;
141
155
  const amqp = await this._ensureWriteConnection();
142
156
  this.publishChannel = await amqp.createConfirmChannel();
157
+ log.debug('Publish connection and channel created');
143
158
  this._creatingPublishChannel = false;
144
- this.publishChannel.prefetch(this.settings.RABBITMQ_PREFETCH_SAILOR);
145
159
  this.publishChannel.on('error', err => log.error({ err }, 'Publish channel error'));
146
160
  this.publishChannel.once('close', () => {
147
161
  delete this.publishChannel;
@@ -149,104 +163,68 @@ class Amqp {
149
163
  return this.publishChannel;
150
164
  }
151
165
 
152
- async *_qReader(queue) {
153
- let channel;
154
-
155
- while (!this.closed) {
156
- channel = await this._ensureConsumerChannel();
157
- let msg;
158
-
159
- try {
160
- msg = await channel.get(queue);
161
- } catch (e) {
162
- if (this.closed) {
163
- break;
164
- }
165
- if (channel.closed) {
166
- if (e.isOperational && e.fields && e.fields.replyCode === 404) { // deleted queue
167
- throw e;
168
- }
169
- delete this.consumerChannel;
170
- } else {
171
- throw e;
172
- }
173
- }
174
- if (msg) {
175
- yield msg;
176
- } else {
177
- // need it here since if there's no message channel.get seems to be not true async
178
- // and event loop is being blocked without this
179
- // also to avoid CPU load if no queue is empty
180
- await new Promise(resolve => setTimeout(resolve, parseInt(this.settings.WAIT_MESSAGES_TIMEOUT)));
181
- }
182
- }
183
- }
184
-
185
- async _createConnection() {
166
+ async _createConnection(name) {
186
167
  const uri = this.settings.AMQP_URI;
187
- let connection;
188
- const reconnectAttempts = parseInt(this.settings.AMQP_RECONNECT_ATTEMPTS);
189
- for (let i = 0; i < reconnectAttempts; i++) {
168
+ const allowedAttempts = parseInt(this.settings.AMQP_RECONNECT_ATTEMPTS);
169
+ let lastErr;
170
+ let attempts = 0;
171
+ while (attempts <= allowedAttempts) {
172
+ if (attempts > 0) {
173
+ await new Promise(resolve => setTimeout(resolve, parseInt(this.settings.AMQP_RECONNECT_TIMEOUT)));
174
+ log.debug(
175
+ {
176
+ reconnectAttempt: attempts,
177
+ AMQP_RECONNECT_ATTEMPTS: this.settings.AMQP_RECONNECT_ATTEMPTS
178
+ },
179
+ 'AMQP Reconnecting'
180
+ );
181
+ }
190
182
  try {
191
- connection = await amqplib.connect(uri);
192
- } catch (e) {
193
- log.error({ err: e }, 'AMQP Connection error');
194
- await new Promise(resolve =>
195
- setTimeout(() => resolve(), parseInt(this.settings.AMQP_RECONNECT_TIMEOUT)));
196
- if (this.reconnectCount >= reconnectAttempts) {
197
- log.debug(
198
- {
199
- reconnectCount: this.reconectCount,
200
- AMQP_RECONNECT_ATTEMPTS: this.settings.AMQP_RECONNECT_ATTEMPTS
201
- },
202
- 'reconnecting'
203
- );
204
- continue;
205
- }
206
- throw e;
183
+ return await amqplib.connect(uri,
184
+ { clientProperties: { connection_name: `${os.hostname()}-${name}` } });
185
+ } catch (err) {
186
+ lastErr = err;
187
+ log.error(err, 'AMQP Connection error');
188
+ attempts++;
207
189
  }
208
- break;
209
190
  }
210
- return connection;
191
+ throw lastErr;
211
192
  }
212
193
 
213
- stopConsume() {
214
- this.consume = false;
194
+ async stopConsume() {
195
+ if (!this.consume) {
196
+ return;
197
+ }
198
+ const consume = this.consume;
199
+ this.consume = undefined;
200
+ await this.consumerChannel.cancel(consume.consumerTag);
201
+ log.debug({ queue: consume.queue }, 'Stopped listening for messages');
215
202
  }
216
203
 
217
204
  async listenQueue(queue, messageHandler) {
218
- const prefetch = parseInt(this.settings.RABBITMQ_PREFETCH_SAILOR);
219
- this.consume = true;
205
+ await this._ensureConsumerChannel();
220
206
 
221
- const readAndProcess = async () => {
222
- for await (const amqpMessage of this._qReader(queue)) {
223
- if (!this.consume) {
224
- break;
225
- }
226
- let message;
227
- try {
228
- message = this._decodeMessage(amqpMessage);
229
- } catch (e) {
230
- log.error(e,
231
- 'Error occurred while parsing message #%j payload',
232
- amqpMessage.fields.deliveryTag
233
- );
234
- this.reject(amqpMessage);
235
- continue;
236
- }
237
- try {
238
- await messageHandler(message, amqpMessage);
239
- } catch (e) {
240
- log.error(e, 'Failed to process message #%j, reject', amqpMessage.fields.deliveryTag);
241
- this.reject(amqpMessage);
242
- }
207
+ const { consumerTag } = await this.consumerChannel.consume(queue, async (amqpMessage) => {
208
+ let message;
209
+ try {
210
+ message = this._decodeMessage(amqpMessage);
211
+ } catch (e) {
212
+ log.error(e,
213
+ 'Error occurred while parsing message #%j payload',
214
+ amqpMessage.fields.deliveryTag
215
+ );
216
+ this.reject(amqpMessage);
217
+ return;
243
218
  }
244
- };
245
-
246
- // here we start readers count = configured prefetch
247
- // if one of them fails then it's irrecoverable error (see _qReader)
248
- // that's why Promise.all here - to fail if one is failed
249
- await Promise.all((new Array(prefetch)).fill().map(() => readAndProcess()));
219
+ try {
220
+ await messageHandler(message, amqpMessage);
221
+ } catch (e) {
222
+ log.error(e, 'Failed to process message #%j, reject', amqpMessage.fields.deliveryTag);
223
+ this.reject(amqpMessage);
224
+ }
225
+ });
226
+ log.debug({ queue }, 'Started listening for messages');
227
+ this.consume = { queue, messageHandler, consumerTag };
250
228
  }
251
229
 
252
230
  _decodeMessage(amqpMessage) {
@@ -337,7 +315,7 @@ class Amqp {
337
315
 
338
316
  async sendToExchange(exchangeName, routingKey, payload, options, throttle) {
339
317
  if (throttle) {
340
- log.debug('Throttlig outgoing message');
318
+ log.debug('Throttling outgoing message');
341
319
  await throttle();
342
320
  }
343
321
  const buffer = Buffer.from(payload);
package/lib/ipc.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Used to communicate with parent processes.
3
+ */
4
+ class IPC {
5
+ send(event, data) {
6
+ if (!process.send) {
7
+ return;
8
+ }
9
+ process.send({ event, data });
10
+ }
11
+ }
12
+
13
+ exports.IPC = IPC;
package/lib/sailor.js CHANGED
@@ -209,13 +209,13 @@ class Sailor {
209
209
  }
210
210
  }
211
211
 
212
- scheduleShutdown() {
212
+ async scheduleShutdown() {
213
213
  if (this.shutdownCallback) {
214
214
  log.debug('scheduleShutdown – shutdown is already scheduled, do nothing');
215
215
  return new Promise(resolve => this.shutdownCallback = resolve);
216
216
  }
217
217
 
218
- this.amqpConnection.stopConsume();
218
+ await this.amqpConnection.stopConsume();
219
219
  if (this.messagesCount === 0) {
220
220
  // there is no unfinished processMessage invocation, let's just resolve scheduleShutdown now
221
221
  log.debug('scheduleShutdown – about to shutdown immediately');
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": "2.6.24",
4
+ "version": "2.6.26-dev1",
5
5
  "main": "run.js",
6
6
  "scripts": {
7
7
  "lint": "./node_modules/.bin/eslint lib spec mocha_spec lib run.js runService.js",
@@ -15,21 +15,22 @@
15
15
  "node": ">=12.13.0"
16
16
  },
17
17
  "dependencies": {
18
- "@elastic.io/object-storage-client": "1.0.0",
19
- "amqplib": "0.6.0",
18
+ "@elastic.io/object-storage-client": "1.0.1",
19
+ "amqplib": "0.8.0",
20
20
  "bunyan": "1.8.10",
21
21
  "co": "4.6.0",
22
22
  "debug": "3.1.0",
23
- "elasticio-rest-node": "1.2.5",
23
+ "elasticio-rest-node": "1.2.7",
24
24
  "event-to-promise": "0.8.0",
25
- "lodash": "4.17.15",
25
+ "lodash": "4.17.21",
26
26
  "p-throttle": "2.1.0",
27
- "q": "1.4.1",
27
+ "q": "1.5.1",
28
28
  "request-promise-native": "1.0.5",
29
- "requestretry": "3.1.0",
29
+ "requestretry": "7.0.2",
30
30
  "uuid": "3.0.1"
31
31
  },
32
32
  "devDependencies": {
33
+ "rabbitmq-stats": "1.2.3",
33
34
  "chai": "4.2.0",
34
35
  "del": "2.2.2",
35
36
  "eslint": "4.19.1",
package/run.js CHANGED
@@ -1,10 +1,15 @@
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');
5
9
  const http = require('http');
6
10
 
7
11
  let sailor;
12
+ let sailorInit;
8
13
  let disconnectRequired;
9
14
 
10
15
  // miserable try to workaround issue described in https://github.com/elasticio/elasticio/issues/4874
@@ -13,7 +18,10 @@ http.globalAgent = new Agent({
13
18
  keepAlive: true
14
19
  });
15
20
 
16
- async function putOutToSea(settings) {
21
+ async function putOutToSea(settings, ipc) {
22
+ ipc.send('init:started');
23
+ const deferred = Q.defer();
24
+ sailorInit = deferred.promise;
17
25
  sailor = new Sailor(settings);
18
26
 
19
27
  //eslint-disable-next-line no-extra-boolean-cast
@@ -38,36 +46,28 @@ async function putOutToSea(settings) {
38
46
 
39
47
  await sailor.runHookInit();
40
48
  await sailor.run();
49
+ deferred.resolve();
50
+ ipc.send('init:ended');
41
51
  }
42
52
 
43
- function disconnectAndExit() {
53
+ async function disconnectAndExit() {
44
54
  if (!disconnectRequired) {
45
55
  return;
46
56
  }
47
57
  disconnectRequired = false;
48
- co(function* putIn() {
58
+
59
+ try {
49
60
  logger.info('Disconnecting...');
50
- yield sailor.disconnect();
61
+ await sailor.disconnect();
51
62
  logger.info('Successfully disconnected');
52
63
  process.exit();
53
- }).catch((err) => {
54
- logger.error('Unable to disconnect', err.stack);
64
+ } catch (err) {
65
+ logger.error(err, 'Unable to disconnect');
55
66
  process.exit(-1);
56
- });
57
- }
58
-
59
- function _disconnectOnly() {
60
- if (!disconnectRequired) {
61
- return Promise.resolve();
62
67
  }
63
- return sailor.disconnect();
64
68
  }
65
69
 
66
- function _closeConsumerChannel() {
67
- return sailor.amqpConnection.consumerChannel.close();
68
- }
69
-
70
- function gracefulShutdown() {
70
+ async function gracefulShutdown() {
71
71
  if (!disconnectRequired) {
72
72
  return;
73
73
  }
@@ -77,12 +77,20 @@ function gracefulShutdown() {
77
77
  return;
78
78
  }
79
79
 
80
- sailor.scheduleShutdown().then(disconnectAndExit);
80
+ // we connect to amqp, create channels, start listen a queue on init and interrupting this process with 'disconnect'
81
+ // will lead to undefined behaviour
82
+ logger.trace('Checking/waiting for init before graceful shutdown');
83
+ await sailorInit;
84
+ logger.trace('Waited an init before graceful shutdown');
85
+
86
+ await sailor.scheduleShutdown();
87
+ await disconnectAndExit();
81
88
  }
82
89
 
83
- async function run(settings) {
90
+ async function run(settings, ipc) {
84
91
  try {
85
- await putOutToSea(settings);
92
+ await putOutToSea(settings, ipc);
93
+ logger.info('Fully initialized and waiting for messages');
86
94
  } catch (e) {
87
95
  if (sailor && !sailor.amqpConnection.closed) {
88
96
  await sailor.reportError(e);
@@ -91,8 +99,17 @@ async function run(settings) {
91
99
  }
92
100
  }
93
101
 
94
- exports._disconnectOnly = _disconnectOnly;
95
- exports._closeConsumerChannel = _closeConsumerChannel;
102
+ exports.__test__ = {
103
+ disconnectOnly: function disconnectOnly() {
104
+ if (!disconnectRequired) {
105
+ return Promise.resolve();
106
+ }
107
+ return sailor.disconnect();
108
+ },
109
+ closeConsumerChannel: function closeConsumerChannel() {
110
+ return sailor.amqpConnection.consumerChannel.close();
111
+ }
112
+ };
96
113
  exports.run = run;
97
114
  exports.putOutToSea = putOutToSea;
98
115
 
@@ -108,6 +125,9 @@ if (require.main === module || process.mainModule.filename === __filename) {
108
125
  });
109
126
 
110
127
  process.on('uncaughtException', logger.criticalErrorAndExit.bind(logger, 'process.uncaughtException'));
128
+ process.on('unhandledRejection', (err) => logger.error(err, 'process.unhandledRejection'));
129
+
130
+ const ipc = new IPC();
111
131
 
112
- run(settings.readFrom(process.env));
132
+ run(settings.readFrom(process.env), ipc);
113
133
  }
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']);