sqs-consumer 5.8.0 → 6.0.0-alpha.2
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 +19 -43
- package/dist/consumer.d.ts +10 -11
- package/dist/consumer.js +19 -19
- package/dist/errors.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +59 -0
- package/dist/types.js +2 -0
- package/package.json +3 -3
- package/src/consumer.ts +71 -60
- package/src/errors.ts +2 -2
- package/src/index.ts +1 -1
- package/src/types.ts +70 -0
- package/.github/CODEOWNERS +0 -2
- package/.github/CODE_OF_CONDUCT.md +0 -74
- package/.github/CONTRIBUTING.md +0 -41
- package/.github/ISSUE_TEMPLATE/bug-report.md +0 -27
- package/.github/ISSUE_TEMPLATE/feature-request.md +0 -19
- package/.github/ISSUE_TEMPLATE/technical-question.md +0 -16
- package/.github/SECURITY.md +0 -9
- package/.github/pull_request_template.md +0 -29
- package/.github/workflows/coverage.yml +0 -35
- package/.github/workflows/stale.yml +0 -20
- package/.github/workflows/test.yml +0 -34
- package/test/consumer.test.ts +0 -870
package/README.md
CHANGED
|
@@ -13,10 +13,17 @@ Build SQS-based applications without the boilerplate. Just define an async funct
|
|
|
13
13
|
npm install sqs-consumer --save-dev
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
> **Note**
|
|
17
|
+
> This library assumes you are using [AWS SDK v3](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-sqs/index.html). If you are using v2, please install v5.8.0:
|
|
18
|
+
>
|
|
19
|
+
> ```bash
|
|
20
|
+
> npm install sqs-consumer@5.8.0 --save-dev
|
|
21
|
+
> ```
|
|
22
|
+
|
|
16
23
|
## Usage
|
|
17
24
|
|
|
18
25
|
```js
|
|
19
|
-
|
|
26
|
+
import { Consumer } from 'sqs-consumer';
|
|
20
27
|
|
|
21
28
|
const app = Consumer.create({
|
|
22
29
|
queueUrl: 'https://sqs.eu-west-1.amazonaws.com/account-id/queue-name',
|
|
@@ -40,37 +47,6 @@ app.start();
|
|
|
40
47
|
- Messages are deleted from the queue once the handler function has completed successfully.
|
|
41
48
|
- Throwing an error (or returning a rejected promise) from the handler function will cause the message to be left on the queue. An [SQS redrive policy](http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/SQSDeadLetterQueue.html) can be used to move messages that cannot be processed to a dead letter queue.
|
|
42
49
|
- By default messages are processed one at a time – a new message won't be received until the first one has been processed. To process messages in parallel, use the `batchSize` option [detailed below](#options).
|
|
43
|
-
- By default, the default Node.js HTTP/HTTPS SQS agent creates a new TCP connection for every new request ([AWS SQS documentation](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-reusing-connections.html)). To avoid the cost of establishing a new connection, you can reuse an existing connection by passing a new SQS instance with `keepAlive: true`.
|
|
44
|
-
|
|
45
|
-
```js
|
|
46
|
-
const { Consumer } = require('sqs-consumer');
|
|
47
|
-
const AWS = require('aws-sdk');
|
|
48
|
-
const https = require('https');
|
|
49
|
-
|
|
50
|
-
const app = Consumer.create({
|
|
51
|
-
queueUrl: 'https://sqs.eu-west-1.amazonaws.com/account-id/queue-name',
|
|
52
|
-
handleMessage: async (message) => {
|
|
53
|
-
// do some work with `message`
|
|
54
|
-
},
|
|
55
|
-
sqs: new AWS.SQS({
|
|
56
|
-
httpOptions: {
|
|
57
|
-
agent: new https.Agent({
|
|
58
|
-
keepAlive: true
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
})
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
app.on('error', (err) => {
|
|
65
|
-
console.error(err.message);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
app.on('processing_error', (err) => {
|
|
69
|
-
console.error(err.message);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
app.start();
|
|
73
|
-
```
|
|
74
50
|
|
|
75
51
|
### Credentials
|
|
76
52
|
|
|
@@ -81,24 +57,24 @@ export AWS_SECRET_ACCESS_KEY=...
|
|
|
81
57
|
export AWS_ACCESS_KEY_ID=...
|
|
82
58
|
```
|
|
83
59
|
|
|
84
|
-
If you need to specify your credentials manually, you can use a pre-configured instance of the [
|
|
60
|
+
If you need to specify your credentials manually, you can use a pre-configured instance of the [SQS Client](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-sqs/classes/sqsclient.html) client.
|
|
85
61
|
|
|
86
62
|
```js
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
AWS.config.update({
|
|
91
|
-
region: 'eu-west-1',
|
|
92
|
-
accessKeyId: '...',
|
|
93
|
-
secretAccessKey: '...'
|
|
94
|
-
});
|
|
63
|
+
import { Consumer } from 'sqs-consumer';
|
|
64
|
+
import { SQSClient } from '@aws-sdk/client-sqs';
|
|
95
65
|
|
|
96
66
|
const app = Consumer.create({
|
|
97
67
|
queueUrl: 'https://sqs.eu-west-1.amazonaws.com/account-id/queue-name',
|
|
98
68
|
handleMessage: async (message) => {
|
|
99
69
|
// ...
|
|
100
70
|
},
|
|
101
|
-
sqs: new
|
|
71
|
+
sqs: new SQSClient({
|
|
72
|
+
region: 'my-region',
|
|
73
|
+
credentials: {
|
|
74
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
75
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
|
|
76
|
+
}
|
|
77
|
+
})
|
|
102
78
|
});
|
|
103
79
|
|
|
104
80
|
app.on('error', (err) => {
|
|
@@ -138,7 +114,7 @@ Creates a new SQS consumer.
|
|
|
138
114
|
- `waitTimeSeconds` - _Number_ - The duration (in seconds) for which the call will wait for a message to arrive in the queue before returning (defaults to `20`).
|
|
139
115
|
- `authenticationErrorTimeout` - _Number_ - The duration (in milliseconds) to wait before retrying after an authentication error (defaults to `10000`).
|
|
140
116
|
- `pollingWaitTimeMs` - _Number_ - The duration (in milliseconds) to wait before repolling the queue (defaults to `0`).
|
|
141
|
-
- `sqs` - _Object_ - An optional [
|
|
117
|
+
- `sqs` - _Object_ - An optional [SQS Client](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-sqs/classes/sqsclient.html) object to use if you need to configure the client manually
|
|
142
118
|
- `shouldDeleteMessages` - _Boolean_ - Default to `true`, if you don't want the package to delete messages from sqs set this to `false`
|
|
143
119
|
|
|
144
120
|
### `consumer.start()`
|
package/dist/consumer.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import {
|
|
2
|
+
import { SQSClient, Message } from '@aws-sdk/client-sqs';
|
|
3
3
|
import { EventEmitter } from 'events';
|
|
4
|
-
export type SQSMessage = SQS.Types.Message;
|
|
5
4
|
export interface ConsumerOptions {
|
|
6
5
|
queueUrl?: string;
|
|
7
6
|
attributeNames?: string[];
|
|
@@ -14,21 +13,21 @@ export interface ConsumerOptions {
|
|
|
14
13
|
pollingWaitTimeMs?: number;
|
|
15
14
|
terminateVisibilityTimeout?: boolean;
|
|
16
15
|
heartbeatInterval?: number;
|
|
17
|
-
sqs?:
|
|
16
|
+
sqs?: SQSClient;
|
|
18
17
|
region?: string;
|
|
19
18
|
handleMessageTimeout?: number;
|
|
20
19
|
shouldDeleteMessages?: boolean;
|
|
21
|
-
handleMessage?(message:
|
|
22
|
-
handleMessageBatch?(messages:
|
|
20
|
+
handleMessage?(message: Message): Promise<void>;
|
|
21
|
+
handleMessageBatch?(messages: Message[]): Promise<void>;
|
|
23
22
|
}
|
|
24
23
|
interface Events {
|
|
25
24
|
response_processed: [];
|
|
26
25
|
empty: [];
|
|
27
|
-
message_received: [
|
|
28
|
-
message_processed: [
|
|
29
|
-
error: [Error, void |
|
|
30
|
-
timeout_error: [Error,
|
|
31
|
-
processing_error: [Error,
|
|
26
|
+
message_received: [Message];
|
|
27
|
+
message_processed: [Message];
|
|
28
|
+
error: [Error, void | Message | Message[]];
|
|
29
|
+
timeout_error: [Error, Message];
|
|
30
|
+
processing_error: [Error, Message];
|
|
32
31
|
stopped: [];
|
|
33
32
|
}
|
|
34
33
|
export declare class Consumer extends EventEmitter {
|
|
@@ -67,7 +66,7 @@ export declare class Consumer extends EventEmitter {
|
|
|
67
66
|
private processMessageBatch;
|
|
68
67
|
private deleteMessageBatch;
|
|
69
68
|
private executeBatchHandler;
|
|
70
|
-
private
|
|
69
|
+
private changeVisibilityTimeoutBatch;
|
|
71
70
|
private startHeartbeat;
|
|
72
71
|
}
|
|
73
72
|
export {};
|
package/dist/consumer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Consumer = void 0;
|
|
4
|
-
const
|
|
4
|
+
const client_sqs_1 = require("@aws-sdk/client-sqs");
|
|
5
5
|
const debug_1 = require("debug");
|
|
6
6
|
const events_1 = require("events");
|
|
7
7
|
const bind_1 = require("./bind");
|
|
@@ -45,13 +45,14 @@ function isConnectionError(err) {
|
|
|
45
45
|
return false;
|
|
46
46
|
}
|
|
47
47
|
function toSQSError(err, message) {
|
|
48
|
+
var _a, _b;
|
|
48
49
|
const sqsError = new errors_1.SQSError(message);
|
|
49
|
-
sqsError.code = err.
|
|
50
|
-
sqsError.statusCode = err.
|
|
51
|
-
sqsError.
|
|
52
|
-
sqsError.
|
|
53
|
-
sqsError.
|
|
54
|
-
sqsError.time =
|
|
50
|
+
sqsError.code = err.name;
|
|
51
|
+
sqsError.statusCode = (_a = err.$metadata) === null || _a === void 0 ? void 0 : _a.httpStatusCode;
|
|
52
|
+
sqsError.retryable = (_b = err.$retryable) === null || _b === void 0 ? void 0 : _b.throttling;
|
|
53
|
+
sqsError.service = err.$service;
|
|
54
|
+
sqsError.fault = err.$fault;
|
|
55
|
+
sqsError.time = new Date();
|
|
55
56
|
return sqsError;
|
|
56
57
|
}
|
|
57
58
|
function hasMessages(response) {
|
|
@@ -81,7 +82,7 @@ class Consumer extends events_1.EventEmitter {
|
|
|
81
82
|
this.shouldDeleteMessages = (_d = options.shouldDeleteMessages) !== null && _d !== void 0 ? _d : true;
|
|
82
83
|
this.sqs =
|
|
83
84
|
options.sqs ||
|
|
84
|
-
new
|
|
85
|
+
new client_sqs_1.SQSClient({
|
|
85
86
|
region: options.region || process.env.AWS_REGION || 'eu-west-1'
|
|
86
87
|
});
|
|
87
88
|
(0, bind_1.autoBind)(this);
|
|
@@ -156,7 +157,7 @@ class Consumer extends events_1.EventEmitter {
|
|
|
156
157
|
}
|
|
157
158
|
async receiveMessage(params) {
|
|
158
159
|
try {
|
|
159
|
-
return await this.sqs.
|
|
160
|
+
return await this.sqs.send(new client_sqs_1.ReceiveMessageCommand(params));
|
|
160
161
|
}
|
|
161
162
|
catch (err) {
|
|
162
163
|
throw toSQSError(err, `SQS receive message failed: ${err.message}`);
|
|
@@ -173,7 +174,7 @@ class Consumer extends events_1.EventEmitter {
|
|
|
173
174
|
ReceiptHandle: message.ReceiptHandle
|
|
174
175
|
};
|
|
175
176
|
try {
|
|
176
|
-
await this.sqs.
|
|
177
|
+
await this.sqs.send(new client_sqs_1.DeleteMessageCommand(deleteParams));
|
|
177
178
|
}
|
|
178
179
|
catch (err) {
|
|
179
180
|
throw toSQSError(err, `SQS delete message failed: ${err.message}`);
|
|
@@ -206,13 +207,12 @@ class Consumer extends events_1.EventEmitter {
|
|
|
206
207
|
}
|
|
207
208
|
async changeVisibilityTimeout(message, timeout) {
|
|
208
209
|
try {
|
|
209
|
-
|
|
210
|
-
.changeMessageVisibility({
|
|
210
|
+
const input = {
|
|
211
211
|
QueueUrl: this.queueUrl,
|
|
212
212
|
ReceiptHandle: message.ReceiptHandle,
|
|
213
213
|
VisibilityTimeout: timeout
|
|
214
|
-
}
|
|
215
|
-
|
|
214
|
+
};
|
|
215
|
+
return await this.sqs.send(new client_sqs_1.ChangeMessageVisibilityCommand(input));
|
|
216
216
|
}
|
|
217
217
|
catch (err) {
|
|
218
218
|
this.emit('error', toSQSError(err, `Error changing visibility timeout: ${err.message}`), message);
|
|
@@ -269,7 +269,7 @@ class Consumer extends events_1.EventEmitter {
|
|
|
269
269
|
try {
|
|
270
270
|
if (this.heartbeatInterval) {
|
|
271
271
|
heartbeat = this.startHeartbeat(async () => {
|
|
272
|
-
return this.
|
|
272
|
+
return this.changeVisibilityTimeoutBatch(messages, this.visibilityTimeout);
|
|
273
273
|
});
|
|
274
274
|
}
|
|
275
275
|
await this.executeBatchHandler(messages);
|
|
@@ -281,7 +281,7 @@ class Consumer extends events_1.EventEmitter {
|
|
|
281
281
|
catch (err) {
|
|
282
282
|
this.emit('error', err, messages);
|
|
283
283
|
if (this.terminateVisibilityTimeout) {
|
|
284
|
-
await this.
|
|
284
|
+
await this.changeVisibilityTimeoutBatch(messages, 0);
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
finally {
|
|
@@ -302,7 +302,7 @@ class Consumer extends events_1.EventEmitter {
|
|
|
302
302
|
}))
|
|
303
303
|
};
|
|
304
304
|
try {
|
|
305
|
-
await this.sqs.
|
|
305
|
+
await this.sqs.send(new client_sqs_1.DeleteMessageBatchCommand(deleteParams));
|
|
306
306
|
}
|
|
307
307
|
catch (err) {
|
|
308
308
|
throw toSQSError(err, `SQS delete message failed: ${err.message}`);
|
|
@@ -317,7 +317,7 @@ class Consumer extends events_1.EventEmitter {
|
|
|
317
317
|
throw err;
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
|
-
async
|
|
320
|
+
async changeVisibilityTimeoutBatch(messages, timeout) {
|
|
321
321
|
const params = {
|
|
322
322
|
QueueUrl: this.queueUrl,
|
|
323
323
|
Entries: messages.map((message) => ({
|
|
@@ -327,7 +327,7 @@ class Consumer extends events_1.EventEmitter {
|
|
|
327
327
|
}))
|
|
328
328
|
};
|
|
329
329
|
try {
|
|
330
|
-
return await this.sqs.
|
|
330
|
+
return await this.sqs.send(new client_sqs_1.ChangeMessageVisibilityBatchCommand(params));
|
|
331
331
|
}
|
|
332
332
|
catch (err) {
|
|
333
333
|
this.emit('error', toSQSError(err, `Error changing visibility timeout: ${err.message}`), messages);
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
declare class SQSError extends Error {
|
|
2
2
|
code: string;
|
|
3
3
|
statusCode: number;
|
|
4
|
-
|
|
5
|
-
hostname: string;
|
|
4
|
+
service: string;
|
|
6
5
|
time: Date;
|
|
7
6
|
retryable: boolean;
|
|
7
|
+
fault: 'client' | 'server';
|
|
8
8
|
constructor(message: string);
|
|
9
9
|
}
|
|
10
10
|
declare class TimeoutError extends Error {
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { Consumer, ConsumerOptions } from './consumer';
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type AWSError = {
|
|
2
|
+
/**
|
|
3
|
+
* Name, eg. ConditionalCheckFailedException
|
|
4
|
+
*/
|
|
5
|
+
name: string;
|
|
6
|
+
/**
|
|
7
|
+
* Human-readable error response message
|
|
8
|
+
*/
|
|
9
|
+
message: string;
|
|
10
|
+
/**
|
|
11
|
+
* Non-standard stacktrace
|
|
12
|
+
*/
|
|
13
|
+
stack?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the client or server are at fault.
|
|
16
|
+
*/
|
|
17
|
+
readonly $fault?: 'client' | 'server';
|
|
18
|
+
/**
|
|
19
|
+
* The service that encountered the exception.
|
|
20
|
+
*/
|
|
21
|
+
readonly $service?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Indicates that an error MAY be retried by the client.
|
|
24
|
+
*/
|
|
25
|
+
readonly $retryable?: {
|
|
26
|
+
/**
|
|
27
|
+
* Indicates that the error is a retryable throttling error.
|
|
28
|
+
*/
|
|
29
|
+
readonly throttling?: boolean;
|
|
30
|
+
};
|
|
31
|
+
$metadata?: {
|
|
32
|
+
/**
|
|
33
|
+
* The status code of the last HTTP response received for this operation.
|
|
34
|
+
*/
|
|
35
|
+
httpStatusCode?: number;
|
|
36
|
+
/**
|
|
37
|
+
* A unique identifier for the last request sent for this operation. Often
|
|
38
|
+
* requested by AWS service teams to aid in debugging.
|
|
39
|
+
*/
|
|
40
|
+
requestId?: string;
|
|
41
|
+
/**
|
|
42
|
+
* A secondary identifier for the last request sent. Used for debugging.
|
|
43
|
+
*/
|
|
44
|
+
extendedRequestId?: string;
|
|
45
|
+
/**
|
|
46
|
+
* A tertiary identifier for the last request sent. Used for debugging.
|
|
47
|
+
*/
|
|
48
|
+
cfId?: string;
|
|
49
|
+
/**
|
|
50
|
+
* The number of times this operation was attempted.
|
|
51
|
+
*/
|
|
52
|
+
attempts?: number;
|
|
53
|
+
/**
|
|
54
|
+
* The total amount of time (in milliseconds) that was spent waiting between
|
|
55
|
+
* retry attempts.
|
|
56
|
+
*/
|
|
57
|
+
totalRetryDelay?: number;
|
|
58
|
+
};
|
|
59
|
+
};
|
package/dist/types.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sqs-consumer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0-alpha.2",
|
|
4
4
|
"description": "Build SQS-based Node applications without the boilerplate",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
"typescript": "^4.9.4"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"aws-sdk": "^
|
|
55
|
+
"@aws-sdk/client-sqs": "^3.226.0",
|
|
56
56
|
"debug": "^4.3.4"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"aws-sdk": "^
|
|
59
|
+
"@aws-sdk/client-sqs": "^3.226.0"
|
|
60
60
|
},
|
|
61
61
|
"mocha": {
|
|
62
62
|
"spec": "test/**/**/*.test.ts",
|
package/src/consumer.ts
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
SQSClient,
|
|
3
|
+
Message,
|
|
4
|
+
ChangeMessageVisibilityCommand,
|
|
5
|
+
ChangeMessageVisibilityCommandInput,
|
|
6
|
+
ChangeMessageVisibilityCommandOutput,
|
|
7
|
+
ChangeMessageVisibilityBatchCommand,
|
|
8
|
+
ChangeMessageVisibilityBatchCommandInput,
|
|
9
|
+
ChangeMessageVisibilityBatchCommandOutput,
|
|
10
|
+
DeleteMessageCommand,
|
|
11
|
+
DeleteMessageCommandInput,
|
|
12
|
+
DeleteMessageBatchCommand,
|
|
13
|
+
DeleteMessageBatchCommandInput,
|
|
14
|
+
ReceiveMessageCommand,
|
|
15
|
+
ReceiveMessageCommandInput,
|
|
16
|
+
ReceiveMessageCommandOutput
|
|
17
|
+
} from '@aws-sdk/client-sqs';
|
|
3
18
|
import Debug from 'debug';
|
|
4
19
|
import { EventEmitter } from 'events';
|
|
20
|
+
|
|
21
|
+
import { AWSError } from './types';
|
|
5
22
|
import { autoBind } from './bind';
|
|
6
23
|
import { SQSError, TimeoutError } from './errors';
|
|
7
24
|
|
|
8
25
|
const debug = Debug('sqs-consumer');
|
|
9
26
|
|
|
10
|
-
type ReceieveMessageResponse = PromiseResult<
|
|
11
|
-
SQS.Types.ReceiveMessageResult,
|
|
12
|
-
AWSError
|
|
13
|
-
>;
|
|
14
|
-
type ReceiveMessageRequest = SQS.Types.ReceiveMessageRequest;
|
|
15
|
-
export type SQSMessage = SQS.Types.Message;
|
|
16
|
-
|
|
17
27
|
const requiredOptions = [
|
|
18
28
|
'queueUrl',
|
|
19
29
|
// only one of handleMessage / handleMessagesBatch is required
|
|
@@ -70,17 +80,17 @@ function isConnectionError(err: Error): boolean {
|
|
|
70
80
|
|
|
71
81
|
function toSQSError(err: AWSError, message: string): SQSError {
|
|
72
82
|
const sqsError = new SQSError(message);
|
|
73
|
-
sqsError.code = err.
|
|
74
|
-
sqsError.statusCode = err
|
|
75
|
-
sqsError.
|
|
76
|
-
sqsError.
|
|
77
|
-
sqsError.
|
|
78
|
-
sqsError.time =
|
|
83
|
+
sqsError.code = err.name;
|
|
84
|
+
sqsError.statusCode = err.$metadata?.httpStatusCode;
|
|
85
|
+
sqsError.retryable = err.$retryable?.throttling;
|
|
86
|
+
sqsError.service = err.$service;
|
|
87
|
+
sqsError.fault = err.$fault;
|
|
88
|
+
sqsError.time = new Date();
|
|
79
89
|
|
|
80
90
|
return sqsError;
|
|
81
91
|
}
|
|
82
92
|
|
|
83
|
-
function hasMessages(response:
|
|
93
|
+
function hasMessages(response: ReceiveMessageCommandOutput): boolean {
|
|
84
94
|
return response.Messages && response.Messages.length > 0;
|
|
85
95
|
}
|
|
86
96
|
|
|
@@ -96,29 +106,29 @@ export interface ConsumerOptions {
|
|
|
96
106
|
pollingWaitTimeMs?: number;
|
|
97
107
|
terminateVisibilityTimeout?: boolean;
|
|
98
108
|
heartbeatInterval?: number;
|
|
99
|
-
sqs?:
|
|
109
|
+
sqs?: SQSClient;
|
|
100
110
|
region?: string;
|
|
101
111
|
handleMessageTimeout?: number;
|
|
102
112
|
shouldDeleteMessages?: boolean;
|
|
103
|
-
handleMessage?(message:
|
|
104
|
-
handleMessageBatch?(messages:
|
|
113
|
+
handleMessage?(message: Message): Promise<void>;
|
|
114
|
+
handleMessageBatch?(messages: Message[]): Promise<void>;
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
interface Events {
|
|
108
118
|
response_processed: [];
|
|
109
119
|
empty: [];
|
|
110
|
-
message_received: [
|
|
111
|
-
message_processed: [
|
|
112
|
-
error: [Error, void |
|
|
113
|
-
timeout_error: [Error,
|
|
114
|
-
processing_error: [Error,
|
|
120
|
+
message_received: [Message];
|
|
121
|
+
message_processed: [Message];
|
|
122
|
+
error: [Error, void | Message | Message[]];
|
|
123
|
+
timeout_error: [Error, Message];
|
|
124
|
+
processing_error: [Error, Message];
|
|
115
125
|
stopped: [];
|
|
116
126
|
}
|
|
117
127
|
|
|
118
128
|
export class Consumer extends EventEmitter {
|
|
119
129
|
private queueUrl: string;
|
|
120
|
-
private handleMessage: (message:
|
|
121
|
-
private handleMessageBatch: (message:
|
|
130
|
+
private handleMessage: (message: Message) => Promise<void>;
|
|
131
|
+
private handleMessageBatch: (message: Message[]) => Promise<void>;
|
|
122
132
|
private handleMessageTimeout: number;
|
|
123
133
|
private attributeNames: string[];
|
|
124
134
|
private messageAttributeNames: string[];
|
|
@@ -130,7 +140,7 @@ export class Consumer extends EventEmitter {
|
|
|
130
140
|
private pollingWaitTimeMs: number;
|
|
131
141
|
private terminateVisibilityTimeout: boolean;
|
|
132
142
|
private heartbeatInterval: number;
|
|
133
|
-
private sqs:
|
|
143
|
+
private sqs: SQSClient;
|
|
134
144
|
private shouldDeleteMessages: boolean;
|
|
135
145
|
|
|
136
146
|
constructor(options: ConsumerOptions) {
|
|
@@ -156,7 +166,7 @@ export class Consumer extends EventEmitter {
|
|
|
156
166
|
|
|
157
167
|
this.sqs =
|
|
158
168
|
options.sqs ||
|
|
159
|
-
new
|
|
169
|
+
new SQSClient({
|
|
160
170
|
region: options.region || process.env.AWS_REGION || 'eu-west-1'
|
|
161
171
|
});
|
|
162
172
|
|
|
@@ -203,7 +213,7 @@ export class Consumer extends EventEmitter {
|
|
|
203
213
|
}
|
|
204
214
|
|
|
205
215
|
private async handleSqsResponse(
|
|
206
|
-
response:
|
|
216
|
+
response: ReceiveMessageCommandOutput
|
|
207
217
|
): Promise<void> {
|
|
208
218
|
debug('Received SQS response');
|
|
209
219
|
debug(response);
|
|
@@ -223,7 +233,7 @@ export class Consumer extends EventEmitter {
|
|
|
223
233
|
}
|
|
224
234
|
}
|
|
225
235
|
|
|
226
|
-
private async processMessage(message:
|
|
236
|
+
private async processMessage(message: Message): Promise<void> {
|
|
227
237
|
this.emit('message_received', message);
|
|
228
238
|
|
|
229
239
|
let heartbeat;
|
|
@@ -248,16 +258,16 @@ export class Consumer extends EventEmitter {
|
|
|
248
258
|
}
|
|
249
259
|
|
|
250
260
|
private async receiveMessage(
|
|
251
|
-
params:
|
|
252
|
-
): Promise<
|
|
261
|
+
params: ReceiveMessageCommandInput
|
|
262
|
+
): Promise<ReceiveMessageCommandOutput> {
|
|
253
263
|
try {
|
|
254
|
-
return await this.sqs.
|
|
264
|
+
return await this.sqs.send(new ReceiveMessageCommand(params));
|
|
255
265
|
} catch (err) {
|
|
256
266
|
throw toSQSError(err, `SQS receive message failed: ${err.message}`);
|
|
257
267
|
}
|
|
258
268
|
}
|
|
259
269
|
|
|
260
|
-
private async deleteMessage(message:
|
|
270
|
+
private async deleteMessage(message: Message): Promise<void> {
|
|
261
271
|
if (!this.shouldDeleteMessages) {
|
|
262
272
|
debug(
|
|
263
273
|
'Skipping message delete since shouldDeleteMessages is set to false'
|
|
@@ -266,19 +276,19 @@ export class Consumer extends EventEmitter {
|
|
|
266
276
|
}
|
|
267
277
|
debug('Deleting message %s', message.MessageId);
|
|
268
278
|
|
|
269
|
-
const deleteParams = {
|
|
279
|
+
const deleteParams: DeleteMessageCommandInput = {
|
|
270
280
|
QueueUrl: this.queueUrl,
|
|
271
281
|
ReceiptHandle: message.ReceiptHandle
|
|
272
282
|
};
|
|
273
283
|
|
|
274
284
|
try {
|
|
275
|
-
await this.sqs.
|
|
285
|
+
await this.sqs.send(new DeleteMessageCommand(deleteParams));
|
|
276
286
|
} catch (err) {
|
|
277
287
|
throw toSQSError(err, `SQS delete message failed: ${err.message}`);
|
|
278
288
|
}
|
|
279
289
|
}
|
|
280
290
|
|
|
281
|
-
private async executeHandler(message:
|
|
291
|
+
private async executeHandler(message: Message): Promise<void> {
|
|
282
292
|
let timeout;
|
|
283
293
|
let pending;
|
|
284
294
|
try {
|
|
@@ -301,17 +311,16 @@ export class Consumer extends EventEmitter {
|
|
|
301
311
|
}
|
|
302
312
|
|
|
303
313
|
private async changeVisibilityTimeout(
|
|
304
|
-
message:
|
|
314
|
+
message: Message,
|
|
305
315
|
timeout: number
|
|
306
|
-
): Promise<
|
|
316
|
+
): Promise<ChangeMessageVisibilityCommandOutput> {
|
|
307
317
|
try {
|
|
308
|
-
|
|
309
|
-
.
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
.promise();
|
|
318
|
+
const input: ChangeMessageVisibilityCommandInput = {
|
|
319
|
+
QueueUrl: this.queueUrl,
|
|
320
|
+
ReceiptHandle: message.ReceiptHandle,
|
|
321
|
+
VisibilityTimeout: timeout
|
|
322
|
+
};
|
|
323
|
+
return await this.sqs.send(new ChangeMessageVisibilityCommand(input));
|
|
315
324
|
} catch (err) {
|
|
316
325
|
this.emit(
|
|
317
326
|
'error',
|
|
@@ -321,7 +330,7 @@ export class Consumer extends EventEmitter {
|
|
|
321
330
|
}
|
|
322
331
|
}
|
|
323
332
|
|
|
324
|
-
private emitError(err: Error, message:
|
|
333
|
+
private emitError(err: Error, message: Message): void {
|
|
325
334
|
if (err.name === SQSError.name) {
|
|
326
335
|
this.emit('error', err, message);
|
|
327
336
|
} else if (err instanceof TimeoutError) {
|
|
@@ -338,7 +347,7 @@ export class Consumer extends EventEmitter {
|
|
|
338
347
|
}
|
|
339
348
|
|
|
340
349
|
debug('Polling for messages');
|
|
341
|
-
const receiveParams = {
|
|
350
|
+
const receiveParams: ReceiveMessageCommandInput = {
|
|
342
351
|
QueueUrl: this.queueUrl,
|
|
343
352
|
AttributeNames: this.attributeNames,
|
|
344
353
|
MessageAttributeNames: this.messageAttributeNames,
|
|
@@ -366,7 +375,7 @@ export class Consumer extends EventEmitter {
|
|
|
366
375
|
});
|
|
367
376
|
}
|
|
368
377
|
|
|
369
|
-
private async processMessageBatch(messages:
|
|
378
|
+
private async processMessageBatch(messages: Message[]): Promise<void> {
|
|
370
379
|
messages.forEach((message) => {
|
|
371
380
|
this.emit('message_received', message);
|
|
372
381
|
});
|
|
@@ -375,7 +384,7 @@ export class Consumer extends EventEmitter {
|
|
|
375
384
|
try {
|
|
376
385
|
if (this.heartbeatInterval) {
|
|
377
386
|
heartbeat = this.startHeartbeat(async () => {
|
|
378
|
-
return this.
|
|
387
|
+
return this.changeVisibilityTimeoutBatch(
|
|
379
388
|
messages,
|
|
380
389
|
this.visibilityTimeout
|
|
381
390
|
);
|
|
@@ -390,14 +399,14 @@ export class Consumer extends EventEmitter {
|
|
|
390
399
|
this.emit('error', err, messages);
|
|
391
400
|
|
|
392
401
|
if (this.terminateVisibilityTimeout) {
|
|
393
|
-
await this.
|
|
402
|
+
await this.changeVisibilityTimeoutBatch(messages, 0);
|
|
394
403
|
}
|
|
395
404
|
} finally {
|
|
396
405
|
clearInterval(heartbeat);
|
|
397
406
|
}
|
|
398
407
|
}
|
|
399
408
|
|
|
400
|
-
private async deleteMessageBatch(messages:
|
|
409
|
+
private async deleteMessageBatch(messages: Message[]): Promise<void> {
|
|
401
410
|
if (!this.shouldDeleteMessages) {
|
|
402
411
|
debug(
|
|
403
412
|
'Skipping message delete since shouldDeleteMessages is set to false'
|
|
@@ -409,7 +418,7 @@ export class Consumer extends EventEmitter {
|
|
|
409
418
|
messages.map((msg) => msg.MessageId).join(' ,')
|
|
410
419
|
);
|
|
411
420
|
|
|
412
|
-
const deleteParams = {
|
|
421
|
+
const deleteParams: DeleteMessageBatchCommandInput = {
|
|
413
422
|
QueueUrl: this.queueUrl,
|
|
414
423
|
Entries: messages.map((message) => ({
|
|
415
424
|
Id: message.MessageId,
|
|
@@ -418,13 +427,13 @@ export class Consumer extends EventEmitter {
|
|
|
418
427
|
};
|
|
419
428
|
|
|
420
429
|
try {
|
|
421
|
-
await this.sqs.
|
|
430
|
+
await this.sqs.send(new DeleteMessageBatchCommand(deleteParams));
|
|
422
431
|
} catch (err) {
|
|
423
432
|
throw toSQSError(err, `SQS delete message failed: ${err.message}`);
|
|
424
433
|
}
|
|
425
434
|
}
|
|
426
435
|
|
|
427
|
-
private async executeBatchHandler(messages:
|
|
436
|
+
private async executeBatchHandler(messages: Message[]): Promise<void> {
|
|
428
437
|
try {
|
|
429
438
|
await this.handleMessageBatch(messages);
|
|
430
439
|
} catch (err) {
|
|
@@ -433,11 +442,11 @@ export class Consumer extends EventEmitter {
|
|
|
433
442
|
}
|
|
434
443
|
}
|
|
435
444
|
|
|
436
|
-
private async
|
|
437
|
-
messages:
|
|
445
|
+
private async changeVisibilityTimeoutBatch(
|
|
446
|
+
messages: Message[],
|
|
438
447
|
timeout: number
|
|
439
|
-
): Promise<
|
|
440
|
-
const params = {
|
|
448
|
+
): Promise<ChangeMessageVisibilityBatchCommandOutput> {
|
|
449
|
+
const params: ChangeMessageVisibilityBatchCommandInput = {
|
|
441
450
|
QueueUrl: this.queueUrl,
|
|
442
451
|
Entries: messages.map((message) => ({
|
|
443
452
|
Id: message.MessageId,
|
|
@@ -446,7 +455,9 @@ export class Consumer extends EventEmitter {
|
|
|
446
455
|
}))
|
|
447
456
|
};
|
|
448
457
|
try {
|
|
449
|
-
return await this.sqs.
|
|
458
|
+
return await this.sqs.send(
|
|
459
|
+
new ChangeMessageVisibilityBatchCommand(params)
|
|
460
|
+
);
|
|
450
461
|
} catch (err) {
|
|
451
462
|
this.emit(
|
|
452
463
|
'error',
|