sqs-consumer 5.6.0 → 5.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/README.md CHANGED
@@ -44,6 +44,7 @@ app.start();
44
44
  ```js
45
45
  const { Consumer } = require('sqs-consumer');
46
46
  const AWS = require('aws-sdk');
47
+ const https = require('https');
47
48
 
48
49
  const app = Consumer.create({
49
50
  queueUrl: 'https://sqs.eu-west-1.amazonaws.com/account-id/queue-name',
@@ -138,7 +139,7 @@ Creates a new SQS consumer.
138
139
  * `authenticationErrorTimeout` - _Number_ - The duration (in milliseconds) to wait before retrying after an authentication error (defaults to `10000`).
139
140
  * `pollingWaitTimeMs` - _Number_ - The duration (in milliseconds) to wait before repolling the queue (defaults to `0`).
140
141
  * `sqs` - _Object_ - An optional [AWS SQS](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SQS.html) object to use if you need to configure the client manually
141
-
142
+ * `shouldDeleteMessages` - _Boolean_ - Default to `true`, if you don't want the package to delete messages from sqs set this to `false`
142
143
  ### `consumer.start()`
143
144
 
144
145
  Start polling the queue for messages.
@@ -157,7 +158,7 @@ Each consumer is an [`EventEmitter`](http://nodejs.org/api/events.html) and emit
157
158
 
158
159
  |Event|Params|Description|
159
160
  |-----|------|-----------|
160
- |`error`|`err`, `[message]`|Fired when an error occurs interacting with the queue. If the error correlates to a message, that error is included in Params|
161
+ |`error`|`err`, `[message]`|Fired when an error occurs interacting with the queue. If the error correlates to a message, that message is included in Params|
161
162
  |`processing_error`|`err`, `message`|Fired when an error occurs processing the message.|
162
163
  |`timeout_error`|`err`, `message`|Fired when `handleMessageTimeout` is supplied as an option and if `handleMessage` times out.|
163
164
  |`message_received`|`message`|Fired when a message is received.|
@@ -168,8 +169,7 @@ Each consumer is an [`EventEmitter`](http://nodejs.org/api/events.html) and emit
168
169
 
169
170
  ### AWS IAM Permissions
170
171
 
171
- Consumer will receive and delete messages from the SQS queue. Ensure `sqs:ReceiveMessage` and `sqs:DeleteMessage` access is granted on the queue being consumed.
172
-
172
+ Consumer will receive and delete messages from the SQS queue. Ensure `sqs:ReceiveMessage`, `sqs:DeleteMessage`, `sqs:DeleteMessageBatch`, `sqs:ChangeMessageVisibility` and `sqs:ChangeMessageVisibilityBatch` access is granted on the queue being consumed.
173
173
 
174
174
  ### Contributing
175
175
  See contributing [guidelines](https://github.com/bbc/sqs-consumer/blob/master/.github/CONTRIBUTING.md).
@@ -17,6 +17,7 @@ export interface ConsumerOptions {
17
17
  sqs?: SQS;
18
18
  region?: string;
19
19
  handleMessageTimeout?: number;
20
+ shouldDeleteMessages?: boolean;
20
21
  handleMessage?(message: SQSMessage): Promise<void>;
21
22
  handleMessageBatch?(messages: SQSMessage[]): Promise<void>;
22
23
  }
@@ -46,6 +47,7 @@ export declare class Consumer extends EventEmitter {
46
47
  private terminateVisibilityTimeout;
47
48
  private heartbeatInterval;
48
49
  private sqs;
50
+ private shouldDeleteMessages;
49
51
  constructor(options: ConsumerOptions);
50
52
  emit<T extends keyof Events>(event: T, ...args: Events[T]): boolean;
51
53
  on<T extends keyof Events>(event: T, listener: (...args: Events[T]) => void): this;
@@ -59,7 +61,7 @@ export declare class Consumer extends EventEmitter {
59
61
  private receiveMessage;
60
62
  private deleteMessage;
61
63
  private executeHandler;
62
- private changeVisabilityTimeout;
64
+ private changeVisibilityTimeout;
63
65
  private emitError;
64
66
  private poll;
65
67
  private processMessageBatch;
package/dist/consumer.js CHANGED
@@ -56,6 +56,7 @@ function hasMessages(response) {
56
56
  }
57
57
  class Consumer extends events_1.EventEmitter {
58
58
  constructor(options) {
59
+ var _a, _b, _c, _d;
59
60
  super();
60
61
  assertOptions(options);
61
62
  this.queueUrl = options.queueUrl;
@@ -69,9 +70,10 @@ class Consumer extends events_1.EventEmitter {
69
70
  this.visibilityTimeout = options.visibilityTimeout;
70
71
  this.terminateVisibilityTimeout = options.terminateVisibilityTimeout || false;
71
72
  this.heartbeatInterval = options.heartbeatInterval;
72
- this.waitTimeSeconds = options.waitTimeSeconds || 20;
73
- this.authenticationErrorTimeout = options.authenticationErrorTimeout || 10000;
74
- this.pollingWaitTimeMs = options.pollingWaitTimeMs || 0;
73
+ this.waitTimeSeconds = (_a = options.waitTimeSeconds) !== null && _a !== void 0 ? _a : 20;
74
+ this.authenticationErrorTimeout = (_b = options.authenticationErrorTimeout) !== null && _b !== void 0 ? _b : 10000;
75
+ this.pollingWaitTimeMs = (_c = options.pollingWaitTimeMs) !== null && _c !== void 0 ? _c : 0;
76
+ this.shouldDeleteMessages = (_d = options.shouldDeleteMessages) !== null && _d !== void 0 ? _d : true;
75
77
  this.sqs = options.sqs || new SQS({
76
78
  region: options.region || process.env.AWS_REGION || 'eu-west-1'
77
79
  });
@@ -128,7 +130,7 @@ class Consumer extends events_1.EventEmitter {
128
130
  try {
129
131
  if (this.heartbeatInterval) {
130
132
  heartbeat = this.startHeartbeat(async () => {
131
- return this.changeVisabilityTimeout(message, this.visibilityTimeout);
133
+ return this.changeVisibilityTimeout(message, this.visibilityTimeout);
132
134
  });
133
135
  }
134
136
  await this.executeHandler(message);
@@ -138,7 +140,7 @@ class Consumer extends events_1.EventEmitter {
138
140
  catch (err) {
139
141
  this.emitError(err, message);
140
142
  if (this.terminateVisibilityTimeout) {
141
- await this.changeVisabilityTimeout(message, 0);
143
+ await this.changeVisibilityTimeout(message, 0);
142
144
  }
143
145
  }
144
146
  finally {
@@ -156,6 +158,10 @@ class Consumer extends events_1.EventEmitter {
156
158
  }
157
159
  }
158
160
  async deleteMessage(message) {
161
+ if (!this.shouldDeleteMessages) {
162
+ debug('Skipping message delete since shouldDeleteMessages is set to false');
163
+ return;
164
+ }
159
165
  debug('Deleting message %s', message.MessageId);
160
166
  const deleteParams = {
161
167
  QueueUrl: this.queueUrl,
@@ -198,9 +204,9 @@ class Consumer extends events_1.EventEmitter {
198
204
  clearTimeout(timeout);
199
205
  }
200
206
  }
201
- async changeVisabilityTimeout(message, timeout) {
207
+ async changeVisibilityTimeout(message, timeout) {
202
208
  try {
203
- return this.sqs
209
+ return await this.sqs
204
210
  .changeMessageVisibility({
205
211
  QueueUrl: this.queueUrl,
206
212
  ReceiptHandle: message.ReceiptHandle,
@@ -209,7 +215,7 @@ class Consumer extends events_1.EventEmitter {
209
215
  .promise();
210
216
  }
211
217
  catch (err) {
212
- this.emit('error', err, message);
218
+ this.emit('error', toSQSError(err, `Error changing visibility timeout: ${err.message}`), message);
213
219
  }
214
220
  }
215
221
  emitError(err, message) {
@@ -281,6 +287,10 @@ class Consumer extends events_1.EventEmitter {
281
287
  }
282
288
  }
283
289
  async deleteMessageBatch(messages) {
290
+ if (!this.shouldDeleteMessages) {
291
+ debug('Skipping message delete since shouldDeleteMessages is set to false');
292
+ return;
293
+ }
284
294
  debug('Deleting messages %s', messages.map((msg) => msg.MessageId).join(' ,'));
285
295
  const deleteParams = {
286
296
  QueueUrl: this.queueUrl,
@@ -317,12 +327,12 @@ class Consumer extends events_1.EventEmitter {
317
327
  }))
318
328
  };
319
329
  try {
320
- return this.sqs
330
+ return await this.sqs
321
331
  .changeMessageVisibilityBatch(params)
322
332
  .promise();
323
333
  }
324
334
  catch (err) {
325
- this.emit('error', err, messages);
335
+ this.emit('error', toSQSError(err, `Error changing visibility timeout: ${err.message}`), messages);
326
336
  }
327
337
  }
328
338
  startHeartbeat(heartbeatFn) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sqs-consumer",
3
- "version": "5.6.0",
3
+ "version": "5.7.0",
4
4
  "description": "Build SQS-based Node applications without the boilerplate",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -50,13 +50,13 @@
50
50
  "tslint-config-airbnb": "^5.11.2",
51
51
  "tslint-microsoft-contrib": "^6.2.0",
52
52
  "typescript": "^3.9.5",
53
- "aws-sdk": "^2.699.0"
53
+ "aws-sdk": "^2.1114.0"
54
54
  },
55
55
  "dependencies": {
56
- "debug": "^4.1.1"
56
+ "debug": "^4.3.1"
57
57
  },
58
58
  "peerDependencies": {
59
- "aws-sdk": "^2.699.0"
59
+ "aws-sdk": "^2.1114.0"
60
60
  },
61
61
  "nyc": {
62
62
  "include": [
package/src/consumer.ts CHANGED
@@ -88,6 +88,7 @@ export interface ConsumerOptions {
88
88
  sqs?: SQS;
89
89
  region?: string;
90
90
  handleMessageTimeout?: number;
91
+ shouldDeleteMessages?: boolean;
91
92
  handleMessage?(message: SQSMessage): Promise<void>;
92
93
  handleMessageBatch?(messages: SQSMessage[]): Promise<void>;
93
94
  }
@@ -119,6 +120,7 @@ export class Consumer extends EventEmitter {
119
120
  private terminateVisibilityTimeout: boolean;
120
121
  private heartbeatInterval: number;
121
122
  private sqs: SQS;
123
+ private shouldDeleteMessages: boolean;
122
124
 
123
125
  constructor(options: ConsumerOptions) {
124
126
  super();
@@ -134,9 +136,10 @@ export class Consumer extends EventEmitter {
134
136
  this.visibilityTimeout = options.visibilityTimeout;
135
137
  this.terminateVisibilityTimeout = options.terminateVisibilityTimeout || false;
136
138
  this.heartbeatInterval = options.heartbeatInterval;
137
- this.waitTimeSeconds = options.waitTimeSeconds || 20;
138
- this.authenticationErrorTimeout = options.authenticationErrorTimeout || 10000;
139
- this.pollingWaitTimeMs = options.pollingWaitTimeMs || 0;
139
+ this.waitTimeSeconds = options.waitTimeSeconds ?? 20;
140
+ this.authenticationErrorTimeout = options.authenticationErrorTimeout ?? 10000;
141
+ this.pollingWaitTimeMs = options.pollingWaitTimeMs ?? 0;
142
+ this.shouldDeleteMessages = options.shouldDeleteMessages ?? true;
140
143
 
141
144
  this.sqs = options.sqs || new SQS({
142
145
  region: options.region || process.env.AWS_REGION || 'eu-west-1'
@@ -204,7 +207,7 @@ export class Consumer extends EventEmitter {
204
207
  try {
205
208
  if (this.heartbeatInterval) {
206
209
  heartbeat = this.startHeartbeat(async () => {
207
- return this.changeVisabilityTimeout(message, this.visibilityTimeout);
210
+ return this.changeVisibilityTimeout(message, this.visibilityTimeout);
208
211
  });
209
212
  }
210
213
  await this.executeHandler(message);
@@ -214,7 +217,7 @@ export class Consumer extends EventEmitter {
214
217
  this.emitError(err, message);
215
218
 
216
219
  if (this.terminateVisibilityTimeout) {
217
- await this.changeVisabilityTimeout(message, 0);
220
+ await this.changeVisibilityTimeout(message, 0);
218
221
  }
219
222
  } finally {
220
223
  clearInterval(heartbeat);
@@ -232,6 +235,10 @@ export class Consumer extends EventEmitter {
232
235
  }
233
236
 
234
237
  private async deleteMessage(message: SQSMessage): Promise<void> {
238
+ if (!this.shouldDeleteMessages) {
239
+ debug('Skipping message delete since shouldDeleteMessages is set to false');
240
+ return;
241
+ }
235
242
  debug('Deleting message %s', message.MessageId);
236
243
 
237
244
  const deleteParams = {
@@ -273,9 +280,9 @@ export class Consumer extends EventEmitter {
273
280
  }
274
281
  }
275
282
 
276
- private async changeVisabilityTimeout(message: SQSMessage, timeout: number): Promise<PromiseResult<any, AWSError>> {
283
+ private async changeVisibilityTimeout(message: SQSMessage, timeout: number): Promise<PromiseResult<any, AWSError>> {
277
284
  try {
278
- return this.sqs
285
+ return await this.sqs
279
286
  .changeMessageVisibility({
280
287
  QueueUrl: this.queueUrl,
281
288
  ReceiptHandle: message.ReceiptHandle,
@@ -283,7 +290,7 @@ export class Consumer extends EventEmitter {
283
290
  })
284
291
  .promise();
285
292
  } catch (err) {
286
- this.emit('error', err, message);
293
+ this.emit('error', toSQSError(err, `Error changing visibility timeout: ${err.message}`), message);
287
294
  }
288
295
  }
289
296
 
@@ -359,6 +366,10 @@ export class Consumer extends EventEmitter {
359
366
  }
360
367
 
361
368
  private async deleteMessageBatch(messages: SQSMessage[]): Promise<void> {
369
+ if (!this.shouldDeleteMessages) {
370
+ debug('Skipping message delete since shouldDeleteMessages is set to false');
371
+ return;
372
+ }
362
373
  debug('Deleting messages %s', messages.map((msg) => msg.MessageId).join(' ,'));
363
374
 
364
375
  const deleteParams = {
@@ -397,11 +408,11 @@ export class Consumer extends EventEmitter {
397
408
  }))
398
409
  };
399
410
  try {
400
- return this.sqs
411
+ return await this.sqs
401
412
  .changeMessageVisibilityBatch(params)
402
413
  .promise();
403
414
  } catch (err) {
404
- this.emit('error', err, messages);
415
+ this.emit('error', toSQSError(err, `Error changing visibility timeout: ${err.message}`), messages);
405
416
  }
406
417
  }
407
418
 
package/test/consumer.ts CHANGED
@@ -692,6 +692,60 @@ describe('Consumer', () => {
692
692
  });
693
693
  sandbox.assert.calledOnce(clearIntervalSpy);
694
694
  });
695
+
696
+ it('emit error when changing visibility timeout fails', async () => {
697
+ sqs.receiveMessage = stubResolve({
698
+ Messages: [
699
+ { MessageId: '1', ReceiptHandle: 'receipt-handle-1', Body: 'body-1' }
700
+ ]
701
+ });
702
+ consumer = new Consumer({
703
+ queueUrl: 'some-queue-url',
704
+ region: 'some-region',
705
+ handleMessage: () => new Promise((resolve) => setTimeout(resolve, 75000)),
706
+ sqs,
707
+ visibilityTimeout: 40,
708
+ heartbeatInterval: 30
709
+ });
710
+
711
+ const receiveErr = new MockSQSError('failed');
712
+ sqs.changeMessageVisibility = stubReject(receiveErr);
713
+
714
+ consumer.start();
715
+ const [err]: any[] = await Promise.all([pEvent(consumer, 'error'), clock.tickAsync(75000)]);
716
+ consumer.stop();
717
+
718
+ assert.ok(err);
719
+ assert.equal(err.message, 'Error changing visibility timeout: failed');
720
+ });
721
+
722
+ it('emit error when changing visibility timeout fails for batch handler functions', async () => {
723
+ sqs.receiveMessage = stubResolve({
724
+ Messages: [
725
+ { MessageId: '1', ReceiptHandle: 'receipt-handle-1', Body: 'body-1' },
726
+ { MessageId: '2', ReceiptHandle: 'receipt-handle-2', Body: 'body-2' }
727
+ ]
728
+ });
729
+ consumer = new Consumer({
730
+ queueUrl: 'some-queue-url',
731
+ region: 'some-region',
732
+ handleMessageBatch: () => new Promise((resolve) => setTimeout(resolve, 75000)),
733
+ sqs,
734
+ batchSize: 2,
735
+ visibilityTimeout: 40,
736
+ heartbeatInterval: 30
737
+ });
738
+
739
+ const receiveErr = new MockSQSError('failed');
740
+ sqs.changeMessageVisibilityBatch = stubReject(receiveErr);
741
+
742
+ consumer.start();
743
+ const [err]: any[] = await Promise.all([pEvent(consumer, 'error'), clock.tickAsync(75000)]);
744
+ consumer.stop();
745
+
746
+ assert.ok(err);
747
+ assert.equal(err.message, 'Error changing visibility timeout: failed');
748
+ });
695
749
  });
696
750
 
697
751
  describe('.stop', () => {
@@ -752,4 +806,27 @@ describe('Consumer', () => {
752
806
  assert.isFalse(consumer.isRunning);
753
807
  });
754
808
  });
809
+
810
+ describe('delete messages property', () => {
811
+ beforeEach(() => {
812
+ consumer = new Consumer({
813
+ queueUrl: 'some-queue-url',
814
+ region: 'some-region',
815
+ handleMessage,
816
+ sqs,
817
+ authenticationErrorTimeout: 20,
818
+ shouldDeleteMessages: false
819
+ });
820
+ });
821
+
822
+ it('dont deletes the message when the handleMessage function is called', async () => {
823
+ handleMessage.resolves();
824
+
825
+ consumer.start();
826
+ await pEvent(consumer, 'message_processed');
827
+ consumer.stop();
828
+
829
+ sandbox.assert.notCalled(sqs.deleteMessage);
830
+ });
831
+ });
755
832
  });