sqs-consumer 5.7.0 → 5.8.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/.github/CODEOWNERS +2 -2
- package/.github/CODE_OF_CONDUCT.md +74 -0
- package/.github/CONTRIBUTING.md +14 -12
- package/.github/ISSUE_TEMPLATE/bug-report.md +2 -2
- package/.github/ISSUE_TEMPLATE/feature-request.md +0 -1
- package/.github/ISSUE_TEMPLATE/technical-question.md +0 -1
- package/.github/SECURITY.md +9 -0
- package/.github/pull_request_template.md +20 -11
- package/.github/workflows/coverage.yml +35 -0
- package/.github/workflows/stale.yml +20 -0
- package/.github/workflows/test.yml +34 -0
- package/.prettierignore +4 -0
- package/.prettierrc.js +5 -0
- package/README.md +41 -39
- package/dist/consumer.d.ts +10 -10
- package/dist/consumer.js +27 -29
- package/dist/index.js +1 -0
- package/package.json +47 -29
- package/src/consumer.ts +90 -50
- package/src/errors.ts +1 -4
- package/test/{consumer.ts → consumer.test.ts} +74 -36
- package/tsconfig.json +3 -8
- package/.travis.yml +0 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sqs-consumer",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.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",
|
|
@@ -11,11 +11,13 @@
|
|
|
11
11
|
"prepublish": "npm run build",
|
|
12
12
|
"pretest": "npm run build",
|
|
13
13
|
"test": "mocha --recursive --full-trace --exit",
|
|
14
|
-
"lint": "eslint . --ext .ts",
|
|
15
|
-
"lint:fix": "eslint . --fix",
|
|
16
14
|
"coverage": "nyc mocha && nyc report --reporter=html && nyc report --reporter=json-summary",
|
|
17
15
|
"lcov": "nyc mocha && nyc report --reporter=lcov",
|
|
18
|
-
"
|
|
16
|
+
"lint": "eslint . --ext .ts",
|
|
17
|
+
"lint:fix": "eslint . --fix",
|
|
18
|
+
"format": "prettier --loglevel warn --write \"**/*.{js,json,jsx,md,ts,tsx,html}\"",
|
|
19
|
+
"format:check": "prettier --check \"**/*.{js,json,jsx,md,ts,tsx,html}\"",
|
|
20
|
+
"posttest": "npm run lint && npm run format:check"
|
|
19
21
|
},
|
|
20
22
|
"repository": {
|
|
21
23
|
"type": "git",
|
|
@@ -32,31 +34,33 @@
|
|
|
32
34
|
],
|
|
33
35
|
"license": "Apache-2.0",
|
|
34
36
|
"devDependencies": {
|
|
35
|
-
"@types/chai": "^4.
|
|
36
|
-
"@types/debug": "^4.1.
|
|
37
|
-
"@types/mocha": "^
|
|
38
|
-
"@types/node": "^
|
|
39
|
-
"@types/sinon": "^
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"eslint": "^
|
|
44
|
-
"
|
|
45
|
-
"mocha": "^8.0.1",
|
|
37
|
+
"@types/chai": "^4.3.4",
|
|
38
|
+
"@types/debug": "^4.1.7",
|
|
39
|
+
"@types/mocha": "^10.0.1",
|
|
40
|
+
"@types/node": "^16.18.7",
|
|
41
|
+
"@types/sinon": "^10.0.13",
|
|
42
|
+
"chai": "^4.3.7",
|
|
43
|
+
"eslint": "^8.29.0",
|
|
44
|
+
"eslint-config-iplayer-ts": "^4.1.0",
|
|
45
|
+
"eslint-config-prettier": "^4.3.0",
|
|
46
|
+
"mocha": "^10.1.0",
|
|
46
47
|
"nyc": "^15.1.0",
|
|
47
48
|
"p-event": "^4.2.0",
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"typescript": "^3.9.5",
|
|
53
|
-
"aws-sdk": "^2.1114.0"
|
|
49
|
+
"prettier": "^2.8.1",
|
|
50
|
+
"sinon": "^15.0.0",
|
|
51
|
+
"ts-node": "^10.9.1",
|
|
52
|
+
"typescript": "^4.9.4"
|
|
54
53
|
},
|
|
55
54
|
"dependencies": {
|
|
56
|
-
"
|
|
55
|
+
"aws-sdk": "^2.1271.0",
|
|
56
|
+
"debug": "^4.3.4"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"aws-sdk": "^2.
|
|
59
|
+
"aws-sdk": "^2.1271.0"
|
|
60
|
+
},
|
|
61
|
+
"mocha": {
|
|
62
|
+
"spec": "test/**/**/*.test.ts",
|
|
63
|
+
"require": "ts-node/register"
|
|
60
64
|
},
|
|
61
65
|
"nyc": {
|
|
62
66
|
"include": [
|
|
@@ -72,14 +76,28 @@
|
|
|
72
76
|
"instrument": true
|
|
73
77
|
},
|
|
74
78
|
"eslintConfig": {
|
|
75
|
-
"extends":
|
|
79
|
+
"extends": [
|
|
80
|
+
"iplayer-ts",
|
|
81
|
+
"prettier",
|
|
82
|
+
"prettier/react",
|
|
83
|
+
"prettier/@typescript-eslint"
|
|
84
|
+
],
|
|
76
85
|
"parserOptions": {
|
|
77
|
-
"ecmaVersion": 2017,
|
|
78
86
|
"sourceType": "module"
|
|
87
|
+
},
|
|
88
|
+
"rules": {
|
|
89
|
+
"@typescript-eslint/naming-convention": [
|
|
90
|
+
"error",
|
|
91
|
+
{
|
|
92
|
+
"selector": "variable",
|
|
93
|
+
"format": [
|
|
94
|
+
"camelCase",
|
|
95
|
+
"UPPER_CASE",
|
|
96
|
+
"PascalCase"
|
|
97
|
+
],
|
|
98
|
+
"leadingUnderscore": "allow"
|
|
99
|
+
}
|
|
100
|
+
]
|
|
79
101
|
}
|
|
80
|
-
},
|
|
81
|
-
"mocha": {
|
|
82
|
-
"spec": "test/**/**/*.ts",
|
|
83
|
-
"require": "ts-node/register"
|
|
84
102
|
}
|
|
85
103
|
}
|
package/src/consumer.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { AWSError } from 'aws-sdk';
|
|
2
|
-
import * as SQS from 'aws-sdk/clients/sqs';
|
|
1
|
+
import { AWSError, SQS } from 'aws-sdk';
|
|
3
2
|
import { PromiseResult } from 'aws-sdk/lib/request';
|
|
4
|
-
import
|
|
3
|
+
import Debug from 'debug';
|
|
5
4
|
import { EventEmitter } from 'events';
|
|
6
5
|
import { autoBind } from './bind';
|
|
7
6
|
import { SQSError, TimeoutError } from './errors';
|
|
8
7
|
|
|
9
8
|
const debug = Debug('sqs-consumer');
|
|
10
9
|
|
|
11
|
-
type ReceieveMessageResponse = PromiseResult<
|
|
10
|
+
type ReceieveMessageResponse = PromiseResult<
|
|
11
|
+
SQS.Types.ReceiveMessageResult,
|
|
12
|
+
AWSError
|
|
13
|
+
>;
|
|
12
14
|
type ReceiveMessageRequest = SQS.Types.ReceiveMessageRequest;
|
|
13
15
|
export type SQSMessage = SQS.Types.Message;
|
|
14
16
|
|
|
@@ -37,7 +39,9 @@ function assertOptions(options: ConsumerOptions): void {
|
|
|
37
39
|
requiredOptions.forEach((option) => {
|
|
38
40
|
const possibilities = option.split('|');
|
|
39
41
|
if (!possibilities.find((p) => options[p])) {
|
|
40
|
-
throw new Error(
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Missing SQS consumer option [ ${possibilities.join(' or ')} ].`
|
|
44
|
+
);
|
|
41
45
|
}
|
|
42
46
|
});
|
|
43
47
|
|
|
@@ -45,14 +49,21 @@ function assertOptions(options: ConsumerOptions): void {
|
|
|
45
49
|
throw new Error('SQS batchSize option must be between 1 and 10.');
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
if (
|
|
52
|
+
if (
|
|
53
|
+
options.heartbeatInterval &&
|
|
54
|
+
!(options.heartbeatInterval < options.visibilityTimeout)
|
|
55
|
+
) {
|
|
49
56
|
throw new Error('heartbeatInterval must be less than visibilityTimeout.');
|
|
50
57
|
}
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
function isConnectionError(err: Error): boolean {
|
|
54
61
|
if (err instanceof SQSError) {
|
|
55
|
-
return (
|
|
62
|
+
return (
|
|
63
|
+
err.statusCode === 403 ||
|
|
64
|
+
err.code === 'CredentialsError' ||
|
|
65
|
+
err.code === 'UnknownEndpoint'
|
|
66
|
+
);
|
|
56
67
|
}
|
|
57
68
|
return false;
|
|
58
69
|
}
|
|
@@ -94,14 +105,14 @@ export interface ConsumerOptions {
|
|
|
94
105
|
}
|
|
95
106
|
|
|
96
107
|
interface Events {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
response_processed: [];
|
|
109
|
+
empty: [];
|
|
110
|
+
message_received: [SQSMessage];
|
|
111
|
+
message_processed: [SQSMessage];
|
|
112
|
+
error: [Error, void | SQSMessage | SQSMessage[]];
|
|
113
|
+
timeout_error: [Error, SQSMessage];
|
|
114
|
+
processing_error: [Error, SQSMessage];
|
|
115
|
+
stopped: [];
|
|
105
116
|
}
|
|
106
117
|
|
|
107
118
|
export class Consumer extends EventEmitter {
|
|
@@ -134,16 +145,20 @@ export class Consumer extends EventEmitter {
|
|
|
134
145
|
this.stopped = true;
|
|
135
146
|
this.batchSize = options.batchSize || 1;
|
|
136
147
|
this.visibilityTimeout = options.visibilityTimeout;
|
|
137
|
-
this.terminateVisibilityTimeout =
|
|
148
|
+
this.terminateVisibilityTimeout =
|
|
149
|
+
options.terminateVisibilityTimeout || false;
|
|
138
150
|
this.heartbeatInterval = options.heartbeatInterval;
|
|
139
151
|
this.waitTimeSeconds = options.waitTimeSeconds ?? 20;
|
|
140
|
-
this.authenticationErrorTimeout =
|
|
152
|
+
this.authenticationErrorTimeout =
|
|
153
|
+
options.authenticationErrorTimeout ?? 10000;
|
|
141
154
|
this.pollingWaitTimeMs = options.pollingWaitTimeMs ?? 0;
|
|
142
155
|
this.shouldDeleteMessages = options.shouldDeleteMessages ?? true;
|
|
143
156
|
|
|
144
|
-
this.sqs =
|
|
145
|
-
|
|
146
|
-
|
|
157
|
+
this.sqs =
|
|
158
|
+
options.sqs ||
|
|
159
|
+
new SQS({
|
|
160
|
+
region: options.region || process.env.AWS_REGION || 'eu-west-1'
|
|
161
|
+
});
|
|
147
162
|
|
|
148
163
|
autoBind(this);
|
|
149
164
|
}
|
|
@@ -152,11 +167,17 @@ export class Consumer extends EventEmitter {
|
|
|
152
167
|
return super.emit(event, ...args);
|
|
153
168
|
}
|
|
154
169
|
|
|
155
|
-
on<T extends keyof Events>(
|
|
170
|
+
on<T extends keyof Events>(
|
|
171
|
+
event: T,
|
|
172
|
+
listener: (...args: Events[T]) => void
|
|
173
|
+
): this {
|
|
156
174
|
return super.on(event, listener);
|
|
157
175
|
}
|
|
158
176
|
|
|
159
|
-
once<T extends keyof Events>(
|
|
177
|
+
once<T extends keyof Events>(
|
|
178
|
+
event: T,
|
|
179
|
+
listener: (...args: Events[T]) => void
|
|
180
|
+
): this {
|
|
160
181
|
return super.once(event, listener);
|
|
161
182
|
}
|
|
162
183
|
|
|
@@ -181,7 +202,9 @@ export class Consumer extends EventEmitter {
|
|
|
181
202
|
this.stopped = true;
|
|
182
203
|
}
|
|
183
204
|
|
|
184
|
-
private async handleSqsResponse(
|
|
205
|
+
private async handleSqsResponse(
|
|
206
|
+
response: ReceieveMessageResponse
|
|
207
|
+
): Promise<void> {
|
|
185
208
|
debug('Received SQS response');
|
|
186
209
|
debug(response);
|
|
187
210
|
|
|
@@ -224,11 +247,11 @@ export class Consumer extends EventEmitter {
|
|
|
224
247
|
}
|
|
225
248
|
}
|
|
226
249
|
|
|
227
|
-
private async receiveMessage(
|
|
250
|
+
private async receiveMessage(
|
|
251
|
+
params: ReceiveMessageRequest
|
|
252
|
+
): Promise<ReceieveMessageResponse> {
|
|
228
253
|
try {
|
|
229
|
-
return await this.sqs
|
|
230
|
-
.receiveMessage(params)
|
|
231
|
-
.promise();
|
|
254
|
+
return await this.sqs.receiveMessage(params).promise();
|
|
232
255
|
} catch (err) {
|
|
233
256
|
throw toSQSError(err, `SQS receive message failed: ${err.message}`);
|
|
234
257
|
}
|
|
@@ -236,7 +259,9 @@ export class Consumer extends EventEmitter {
|
|
|
236
259
|
|
|
237
260
|
private async deleteMessage(message: SQSMessage): Promise<void> {
|
|
238
261
|
if (!this.shouldDeleteMessages) {
|
|
239
|
-
debug(
|
|
262
|
+
debug(
|
|
263
|
+
'Skipping message delete since shouldDeleteMessages is set to false'
|
|
264
|
+
);
|
|
240
265
|
return;
|
|
241
266
|
}
|
|
242
267
|
debug('Deleting message %s', message.MessageId);
|
|
@@ -247,9 +272,7 @@ export class Consumer extends EventEmitter {
|
|
|
247
272
|
};
|
|
248
273
|
|
|
249
274
|
try {
|
|
250
|
-
await this.sqs
|
|
251
|
-
.deleteMessage(deleteParams)
|
|
252
|
-
.promise();
|
|
275
|
+
await this.sqs.deleteMessage(deleteParams).promise();
|
|
253
276
|
} catch (err) {
|
|
254
277
|
throw toSQSError(err, `SQS delete message failed: ${err.message}`);
|
|
255
278
|
}
|
|
@@ -261,10 +284,7 @@ export class Consumer extends EventEmitter {
|
|
|
261
284
|
try {
|
|
262
285
|
if (this.handleMessageTimeout) {
|
|
263
286
|
[timeout, pending] = createTimeout(this.handleMessageTimeout);
|
|
264
|
-
await Promise.race([
|
|
265
|
-
this.handleMessage(message),
|
|
266
|
-
pending
|
|
267
|
-
]);
|
|
287
|
+
await Promise.race([this.handleMessage(message), pending]);
|
|
268
288
|
} else {
|
|
269
289
|
await this.handleMessage(message);
|
|
270
290
|
}
|
|
@@ -280,7 +300,10 @@ export class Consumer extends EventEmitter {
|
|
|
280
300
|
}
|
|
281
301
|
}
|
|
282
302
|
|
|
283
|
-
private async changeVisibilityTimeout(
|
|
303
|
+
private async changeVisibilityTimeout(
|
|
304
|
+
message: SQSMessage,
|
|
305
|
+
timeout: number
|
|
306
|
+
): Promise<PromiseResult<any, AWSError>> {
|
|
284
307
|
try {
|
|
285
308
|
return await this.sqs
|
|
286
309
|
.changeMessageVisibility({
|
|
@@ -290,7 +313,11 @@ export class Consumer extends EventEmitter {
|
|
|
290
313
|
})
|
|
291
314
|
.promise();
|
|
292
315
|
} catch (err) {
|
|
293
|
-
this.emit(
|
|
316
|
+
this.emit(
|
|
317
|
+
'error',
|
|
318
|
+
toSQSError(err, `Error changing visibility timeout: ${err.message}`),
|
|
319
|
+
message
|
|
320
|
+
);
|
|
294
321
|
}
|
|
295
322
|
}
|
|
296
323
|
|
|
@@ -330,9 +357,11 @@ export class Consumer extends EventEmitter {
|
|
|
330
357
|
currentPollingTimeout = this.authenticationErrorTimeout;
|
|
331
358
|
}
|
|
332
359
|
return;
|
|
333
|
-
})
|
|
360
|
+
})
|
|
361
|
+
.then(() => {
|
|
334
362
|
setTimeout(this.poll, currentPollingTimeout);
|
|
335
|
-
})
|
|
363
|
+
})
|
|
364
|
+
.catch((err) => {
|
|
336
365
|
this.emit('error', err);
|
|
337
366
|
});
|
|
338
367
|
}
|
|
@@ -346,7 +375,10 @@ export class Consumer extends EventEmitter {
|
|
|
346
375
|
try {
|
|
347
376
|
if (this.heartbeatInterval) {
|
|
348
377
|
heartbeat = this.startHeartbeat(async () => {
|
|
349
|
-
return this.changeVisabilityTimeoutBatch(
|
|
378
|
+
return this.changeVisabilityTimeoutBatch(
|
|
379
|
+
messages,
|
|
380
|
+
this.visibilityTimeout
|
|
381
|
+
);
|
|
350
382
|
});
|
|
351
383
|
}
|
|
352
384
|
await this.executeBatchHandler(messages);
|
|
@@ -367,10 +399,15 @@ export class Consumer extends EventEmitter {
|
|
|
367
399
|
|
|
368
400
|
private async deleteMessageBatch(messages: SQSMessage[]): Promise<void> {
|
|
369
401
|
if (!this.shouldDeleteMessages) {
|
|
370
|
-
debug(
|
|
402
|
+
debug(
|
|
403
|
+
'Skipping message delete since shouldDeleteMessages is set to false'
|
|
404
|
+
);
|
|
371
405
|
return;
|
|
372
406
|
}
|
|
373
|
-
debug(
|
|
407
|
+
debug(
|
|
408
|
+
'Deleting messages %s',
|
|
409
|
+
messages.map((msg) => msg.MessageId).join(' ,')
|
|
410
|
+
);
|
|
374
411
|
|
|
375
412
|
const deleteParams = {
|
|
376
413
|
QueueUrl: this.queueUrl,
|
|
@@ -381,9 +418,7 @@ export class Consumer extends EventEmitter {
|
|
|
381
418
|
};
|
|
382
419
|
|
|
383
420
|
try {
|
|
384
|
-
await this.sqs
|
|
385
|
-
.deleteMessageBatch(deleteParams)
|
|
386
|
-
.promise();
|
|
421
|
+
await this.sqs.deleteMessageBatch(deleteParams).promise();
|
|
387
422
|
} catch (err) {
|
|
388
423
|
throw toSQSError(err, `SQS delete message failed: ${err.message}`);
|
|
389
424
|
}
|
|
@@ -398,7 +433,10 @@ export class Consumer extends EventEmitter {
|
|
|
398
433
|
}
|
|
399
434
|
}
|
|
400
435
|
|
|
401
|
-
private async changeVisabilityTimeoutBatch(
|
|
436
|
+
private async changeVisabilityTimeoutBatch(
|
|
437
|
+
messages: SQSMessage[],
|
|
438
|
+
timeout: number
|
|
439
|
+
): Promise<PromiseResult<any, AWSError>> {
|
|
402
440
|
const params = {
|
|
403
441
|
QueueUrl: this.queueUrl,
|
|
404
442
|
Entries: messages.map((message) => ({
|
|
@@ -408,11 +446,13 @@ export class Consumer extends EventEmitter {
|
|
|
408
446
|
}))
|
|
409
447
|
};
|
|
410
448
|
try {
|
|
411
|
-
return await this.sqs
|
|
412
|
-
.changeMessageVisibilityBatch(params)
|
|
413
|
-
.promise();
|
|
449
|
+
return await this.sqs.changeMessageVisibilityBatch(params).promise();
|
|
414
450
|
} catch (err) {
|
|
415
|
-
this.emit(
|
|
451
|
+
this.emit(
|
|
452
|
+
'error',
|
|
453
|
+
toSQSError(err, `Error changing visibility timeout: ${err.message}`),
|
|
454
|
+
messages
|
|
455
|
+
);
|
|
416
456
|
}
|
|
417
457
|
}
|
|
418
458
|
|
package/src/errors.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { assert } from 'chai';
|
|
2
|
-
import * as pEvent from 'p-event';
|
|
3
|
-
|
|
4
2
|
import * as sinon from 'sinon';
|
|
5
|
-
import
|
|
3
|
+
import * as pEvent from 'p-event';
|
|
4
|
+
import { Consumer } from '../src/consumer';
|
|
6
5
|
|
|
7
6
|
const sandbox = sinon.createSandbox();
|
|
8
7
|
|
|
@@ -10,15 +9,11 @@ const AUTHENTICATION_ERROR_TIMEOUT = 20;
|
|
|
10
9
|
const POLLING_TIMEOUT = 100;
|
|
11
10
|
|
|
12
11
|
function stubResolve(value?: any): any {
|
|
13
|
-
return sandbox
|
|
14
|
-
.stub()
|
|
15
|
-
.returns({ promise: sandbox.stub().resolves(value) });
|
|
12
|
+
return sandbox.stub().returns({ promise: sandbox.stub().resolves(value) });
|
|
16
13
|
}
|
|
17
14
|
|
|
18
15
|
function stubReject(value?: any): any {
|
|
19
|
-
return sandbox
|
|
20
|
-
.stub()
|
|
21
|
-
.returns({ promise: sandbox.stub().rejects(value) });
|
|
16
|
+
return sandbox.stub().returns({ promise: sandbox.stub().rejects(value) });
|
|
22
17
|
}
|
|
23
18
|
|
|
24
19
|
class MockSQSError extends Error {
|
|
@@ -35,7 +30,6 @@ class MockSQSError extends Error {
|
|
|
35
30
|
}
|
|
36
31
|
}
|
|
37
32
|
|
|
38
|
-
// tslint:disable:no-unused-expression
|
|
39
33
|
describe('Consumer', () => {
|
|
40
34
|
let consumer;
|
|
41
35
|
let clock;
|
|
@@ -43,11 +37,13 @@ describe('Consumer', () => {
|
|
|
43
37
|
let handleMessageBatch;
|
|
44
38
|
let sqs;
|
|
45
39
|
const response = {
|
|
46
|
-
Messages: [
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
Messages: [
|
|
41
|
+
{
|
|
42
|
+
ReceiptHandle: 'receipt-handle',
|
|
43
|
+
MessageId: '123',
|
|
44
|
+
Body: 'body'
|
|
45
|
+
}
|
|
46
|
+
]
|
|
51
47
|
};
|
|
52
48
|
|
|
53
49
|
beforeEach(() => {
|
|
@@ -71,6 +67,7 @@ describe('Consumer', () => {
|
|
|
71
67
|
});
|
|
72
68
|
|
|
73
69
|
afterEach(() => {
|
|
70
|
+
clock.restore();
|
|
74
71
|
sandbox.restore();
|
|
75
72
|
});
|
|
76
73
|
|
|
@@ -198,18 +195,25 @@ describe('Consumer', () => {
|
|
|
198
195
|
consumer = new Consumer({
|
|
199
196
|
queueUrl: 'some-queue-url',
|
|
200
197
|
region: 'some-region',
|
|
201
|
-
handleMessage: () =>
|
|
198
|
+
handleMessage: () =>
|
|
199
|
+
new Promise((resolve) => setTimeout(resolve, 1000)),
|
|
202
200
|
handleMessageTimeout,
|
|
203
201
|
sqs,
|
|
204
202
|
authenticationErrorTimeout: 20
|
|
205
203
|
});
|
|
206
204
|
|
|
207
205
|
consumer.start();
|
|
208
|
-
const [err]: any = await Promise.all([
|
|
206
|
+
const [err]: any = await Promise.all([
|
|
207
|
+
pEvent(consumer, 'timeout_error'),
|
|
208
|
+
clock.tickAsync(handleMessageTimeout)
|
|
209
|
+
]);
|
|
209
210
|
consumer.stop();
|
|
210
211
|
|
|
211
212
|
assert.ok(err);
|
|
212
|
-
assert.equal(
|
|
213
|
+
assert.equal(
|
|
214
|
+
err.message,
|
|
215
|
+
`Message handler timed out after ${handleMessageTimeout}ms: Operation timed out.`
|
|
216
|
+
);
|
|
213
217
|
});
|
|
214
218
|
|
|
215
219
|
it('handles unexpected exceptions thrown by the handler function', async () => {
|
|
@@ -228,7 +232,10 @@ describe('Consumer', () => {
|
|
|
228
232
|
consumer.stop();
|
|
229
233
|
|
|
230
234
|
assert.ok(err);
|
|
231
|
-
assert.equal(
|
|
235
|
+
assert.equal(
|
|
236
|
+
err.message,
|
|
237
|
+
'Unexpected message handler failure: unexpected parsing error'
|
|
238
|
+
);
|
|
232
239
|
});
|
|
233
240
|
|
|
234
241
|
it('fires an error event when an error occurs deleting a message', async () => {
|
|
@@ -251,10 +258,18 @@ describe('Consumer', () => {
|
|
|
251
258
|
handleMessage.rejects(processingErr);
|
|
252
259
|
|
|
253
260
|
consumer.start();
|
|
254
|
-
const [err, message] = await pEvent
|
|
261
|
+
const [err, message] = await pEvent<
|
|
262
|
+
string | symbol,
|
|
263
|
+
{ [key: string]: string }[]
|
|
264
|
+
>(consumer, 'processing_error', {
|
|
265
|
+
multiArgs: true
|
|
266
|
+
});
|
|
255
267
|
consumer.stop();
|
|
256
268
|
|
|
257
|
-
assert.equal(
|
|
269
|
+
assert.equal(
|
|
270
|
+
err instanceof Error ? err.message : '',
|
|
271
|
+
'Unexpected message handler failure: Processing error'
|
|
272
|
+
);
|
|
258
273
|
assert.equal(message.MessageId, '123');
|
|
259
274
|
});
|
|
260
275
|
|
|
@@ -266,7 +281,12 @@ describe('Consumer', () => {
|
|
|
266
281
|
sqs.deleteMessage = stubReject(sqsError);
|
|
267
282
|
|
|
268
283
|
consumer.start();
|
|
269
|
-
const [err, message] = await pEvent
|
|
284
|
+
const [err, message] = await pEvent<
|
|
285
|
+
string | symbol,
|
|
286
|
+
{ [key: string]: string }[]
|
|
287
|
+
>(consumer, 'error', {
|
|
288
|
+
multiArgs: true
|
|
289
|
+
});
|
|
270
290
|
consumer.stop();
|
|
271
291
|
|
|
272
292
|
assert.equal(err.message, 'SQS delete message failed: Processing error');
|
|
@@ -310,7 +330,8 @@ describe('Consumer', () => {
|
|
|
310
330
|
it('waits before repolling when a UnknownEndpoint error occurs', async () => {
|
|
311
331
|
const unknownEndpointErr = {
|
|
312
332
|
code: 'UnknownEndpoint',
|
|
313
|
-
message:
|
|
333
|
+
message:
|
|
334
|
+
'Inaccessible host: `sqs.eu-west-1.amazonaws.com`. This service may not be available in the `eu-west-1` region.'
|
|
314
335
|
};
|
|
315
336
|
sqs.receiveMessage = stubReject(unknownEndpointErr);
|
|
316
337
|
const errorListener = sandbox.stub();
|
|
@@ -380,7 +401,7 @@ describe('Consumer', () => {
|
|
|
380
401
|
});
|
|
381
402
|
});
|
|
382
403
|
|
|
383
|
-
it(
|
|
404
|
+
it("doesn't delete the message when a processing error is reported", async () => {
|
|
384
405
|
handleMessage.rejects(new Error('Processing error'));
|
|
385
406
|
|
|
386
407
|
consumer.start();
|
|
@@ -400,8 +421,10 @@ describe('Consumer', () => {
|
|
|
400
421
|
sandbox.assert.calledTwice(handleMessage);
|
|
401
422
|
});
|
|
402
423
|
|
|
403
|
-
it(
|
|
404
|
-
sqs.receiveMessage = stubResolve(
|
|
424
|
+
it("doesn't consume more messages when called multiple times", () => {
|
|
425
|
+
sqs.receiveMessage = stubResolve(
|
|
426
|
+
new Promise((res) => setTimeout(res, 100))
|
|
427
|
+
);
|
|
405
428
|
consumer.start();
|
|
406
429
|
consumer.start();
|
|
407
430
|
consumer.start();
|
|
@@ -461,7 +484,7 @@ describe('Consumer', () => {
|
|
|
461
484
|
});
|
|
462
485
|
});
|
|
463
486
|
|
|
464
|
-
it(
|
|
487
|
+
it("consumes messages with message attribute 'ApproximateReceiveCount'", async () => {
|
|
465
488
|
const messageWithAttr = {
|
|
466
489
|
ReceiptHandle: 'receipt-handle-1',
|
|
467
490
|
MessageId: '1',
|
|
@@ -620,14 +643,14 @@ describe('Consumer', () => {
|
|
|
620
643
|
|
|
621
644
|
sandbox.assert.callCount(handleMessageBatch, 1);
|
|
622
645
|
sandbox.assert.callCount(handleMessage, 0);
|
|
623
|
-
|
|
624
646
|
});
|
|
625
647
|
|
|
626
648
|
it('uses the correct visibility timeout for long running handler functions', async () => {
|
|
627
649
|
consumer = new Consumer({
|
|
628
650
|
queueUrl: 'some-queue-url',
|
|
629
651
|
region: 'some-region',
|
|
630
|
-
handleMessage: () =>
|
|
652
|
+
handleMessage: () =>
|
|
653
|
+
new Promise((resolve) => setTimeout(resolve, 75000)),
|
|
631
654
|
sqs,
|
|
632
655
|
visibilityTimeout: 40,
|
|
633
656
|
heartbeatInterval: 30
|
|
@@ -635,7 +658,10 @@ describe('Consumer', () => {
|
|
|
635
658
|
const clearIntervalSpy = sinon.spy(global, 'clearInterval');
|
|
636
659
|
|
|
637
660
|
consumer.start();
|
|
638
|
-
await Promise.all([
|
|
661
|
+
await Promise.all([
|
|
662
|
+
pEvent(consumer, 'response_processed'),
|
|
663
|
+
clock.tickAsync(75000)
|
|
664
|
+
]);
|
|
639
665
|
consumer.stop();
|
|
640
666
|
|
|
641
667
|
sandbox.assert.calledWith(sqs.changeMessageVisibility, {
|
|
@@ -662,7 +688,8 @@ describe('Consumer', () => {
|
|
|
662
688
|
consumer = new Consumer({
|
|
663
689
|
queueUrl: 'some-queue-url',
|
|
664
690
|
region: 'some-region',
|
|
665
|
-
handleMessageBatch: () =>
|
|
691
|
+
handleMessageBatch: () =>
|
|
692
|
+
new Promise((resolve) => setTimeout(resolve, 75000)),
|
|
666
693
|
batchSize: 3,
|
|
667
694
|
sqs,
|
|
668
695
|
visibilityTimeout: 40,
|
|
@@ -671,7 +698,10 @@ describe('Consumer', () => {
|
|
|
671
698
|
const clearIntervalSpy = sinon.spy(global, 'clearInterval');
|
|
672
699
|
|
|
673
700
|
consumer.start();
|
|
674
|
-
await Promise.all([
|
|
701
|
+
await Promise.all([
|
|
702
|
+
pEvent(consumer, 'response_processed'),
|
|
703
|
+
clock.tickAsync(75000)
|
|
704
|
+
]);
|
|
675
705
|
consumer.stop();
|
|
676
706
|
|
|
677
707
|
sandbox.assert.calledWith(sqs.changeMessageVisibilityBatch, {
|
|
@@ -702,7 +732,8 @@ describe('Consumer', () => {
|
|
|
702
732
|
consumer = new Consumer({
|
|
703
733
|
queueUrl: 'some-queue-url',
|
|
704
734
|
region: 'some-region',
|
|
705
|
-
handleMessage: () =>
|
|
735
|
+
handleMessage: () =>
|
|
736
|
+
new Promise((resolve) => setTimeout(resolve, 75000)),
|
|
706
737
|
sqs,
|
|
707
738
|
visibilityTimeout: 40,
|
|
708
739
|
heartbeatInterval: 30
|
|
@@ -712,7 +743,10 @@ describe('Consumer', () => {
|
|
|
712
743
|
sqs.changeMessageVisibility = stubReject(receiveErr);
|
|
713
744
|
|
|
714
745
|
consumer.start();
|
|
715
|
-
const [err]: any[] = await Promise.all([
|
|
746
|
+
const [err]: any[] = await Promise.all([
|
|
747
|
+
pEvent(consumer, 'error'),
|
|
748
|
+
clock.tickAsync(75000)
|
|
749
|
+
]);
|
|
716
750
|
consumer.stop();
|
|
717
751
|
|
|
718
752
|
assert.ok(err);
|
|
@@ -729,7 +763,8 @@ describe('Consumer', () => {
|
|
|
729
763
|
consumer = new Consumer({
|
|
730
764
|
queueUrl: 'some-queue-url',
|
|
731
765
|
region: 'some-region',
|
|
732
|
-
handleMessageBatch: () =>
|
|
766
|
+
handleMessageBatch: () =>
|
|
767
|
+
new Promise((resolve) => setTimeout(resolve, 75000)),
|
|
733
768
|
sqs,
|
|
734
769
|
batchSize: 2,
|
|
735
770
|
visibilityTimeout: 40,
|
|
@@ -740,7 +775,10 @@ describe('Consumer', () => {
|
|
|
740
775
|
sqs.changeMessageVisibilityBatch = stubReject(receiveErr);
|
|
741
776
|
|
|
742
777
|
consumer.start();
|
|
743
|
-
const [err]: any[] = await Promise.all([
|
|
778
|
+
const [err]: any[] = await Promise.all([
|
|
779
|
+
pEvent(consumer, 'error'),
|
|
780
|
+
clock.tickAsync(75000)
|
|
781
|
+
]);
|
|
744
782
|
consumer.stop();
|
|
745
783
|
|
|
746
784
|
assert.ok(err);
|