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 +4 -4
- package/dist/consumer.d.ts +3 -1
- package/dist/consumer.js +20 -10
- package/package.json +4 -4
- package/src/consumer.ts +21 -10
- package/test/consumer.ts +77 -0
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
|
|
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:
|
|
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).
|
package/dist/consumer.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
73
|
-
this.authenticationErrorTimeout = options.authenticationErrorTimeout
|
|
74
|
-
this.pollingWaitTimeMs = options.pollingWaitTimeMs
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
53
|
+
"aws-sdk": "^2.1114.0"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"debug": "^4.
|
|
56
|
+
"debug": "^4.3.1"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"aws-sdk": "^2.
|
|
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
|
|
138
|
-
this.authenticationErrorTimeout = options.authenticationErrorTimeout
|
|
139
|
-
this.pollingWaitTimeMs = options.pollingWaitTimeMs
|
|
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.
|
|
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.
|
|
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
|
|
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
|
});
|