sqs-consumer 5.4.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/.eslintignore +4 -0
- package/.github/CODEOWNERS +2 -0
- package/.github/CONTRIBUTING.md +39 -0
- package/.github/ISSUE_TEMPLATE/bug-report.md +27 -0
- package/.github/ISSUE_TEMPLATE/feature-request.md +20 -0
- package/.github/ISSUE_TEMPLATE/technical-question.md +17 -0
- package/.github/pull_request_template.md +20 -0
- package/.travis.yml +1 -1
- package/README.md +36 -6
- package/dist/bind.js +2 -1
- package/dist/consumer.d.ts +23 -4
- package/dist/consumer.js +77 -30
- package/dist/errors.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +35 -19
- package/src/bind.ts +0 -2
- package/src/consumer.ts +93 -31
- package/src/errors.ts +1 -1
- package/src/index.ts +1 -1
- package/test/consumer.ts +226 -86
- package/tsconfig.json +1 -1
- package/.nyc_output/6b82944a-e1d8-43f1-b7e6-4250bba9eb0c.json +0 -1
- package/.nyc_output/b96929df-d0ad-46a6-83d3-f796728a2d38.json +0 -1
- package/CONTRIBUTING.md +0 -8
- package/issue_template.md +0 -6
- package/test/mocha.opts +0 -5
- package/tslint.json +0 -74
package/.eslintignore
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to the sqs-consumer.
|
|
4
|
+
|
|
5
|
+
* If you're unsure if a feature would make a good addition, you can always [create an issue](https://github.com/bbc/sqs-consumer/issues/new) first. Raising an issue before creating a pull request is recommended.
|
|
6
|
+
* We aim for 100% test coverage. Please write tests for any new functionality or changes.
|
|
7
|
+
* Any API changes should be fully documented.
|
|
8
|
+
* Make sure your code meets our linting standards. Run `npm run lint` to check your code.
|
|
9
|
+
* Maintain the existing coding style. There are some settings in `.jsbeautifyrc` to help.
|
|
10
|
+
* Be mindful of others when making suggestions and/or code reviewing.
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Reporting Issues
|
|
15
|
+
Before opening a new issue, first check that there is not already an [open issue or Pull Request](https://github.com/bbc/sqs-consumer/issues?utf8=%E2%9C%93&q=is%3Aopen) that addresses it.
|
|
16
|
+
|
|
17
|
+
If there is, make relevant comments and add your reaction. Use a reaction in place of a "+1" comment:
|
|
18
|
+
* 👍 - upvote
|
|
19
|
+
* 👎 - downvote
|
|
20
|
+
|
|
21
|
+
If you cannot find an existing issue that describes your bug or feature, create a new issue using the guidelines below.
|
|
22
|
+
|
|
23
|
+
1. Pick an appropriate template for the type of issue [from here](https://github.com/bbc/sqs-consumer/issues/choose)
|
|
24
|
+
2. Provide as much detail as possible
|
|
25
|
+
3. Follow your issue in the issue tracking workflow
|
|
26
|
+
|
|
27
|
+
## Contributing Code
|
|
28
|
+
If you do not have push access to the repository, please [fork it](https://help.github.com/en/articles/fork-a-repo). You should then work on your own `master` branch.
|
|
29
|
+
|
|
30
|
+
Otherwise, you may clone this repository and create a working branch with a _kebab-case_ name reflecting what you are working on (e.g. `fix-the-thing`).
|
|
31
|
+
|
|
32
|
+
Follow the setup instructions in the [README](../README.md).
|
|
33
|
+
|
|
34
|
+
Ensure all your code is thoroughly tested and that this testing is detailed in the pull request.
|
|
35
|
+
|
|
36
|
+
## Pull Request Process
|
|
37
|
+
1. Make sure you have opened an issue and it was approved by a project maintainer before working on a PR
|
|
38
|
+
2. Read and complete all relevant sections of the PR template
|
|
39
|
+
3. Wait for the PR get approved
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Create a report to help us improve
|
|
4
|
+
title: ''
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Describe the bug**
|
|
11
|
+
A clear and concise description of what the bug is.
|
|
12
|
+
|
|
13
|
+
**To Reproduce**
|
|
14
|
+
Steps to reproduce the behaviour:
|
|
15
|
+
1. Go to '...'
|
|
16
|
+
2. Select '....'
|
|
17
|
+
3. Scroll down to '....'
|
|
18
|
+
4. See error
|
|
19
|
+
|
|
20
|
+
**Expected behaviour**
|
|
21
|
+
A clear and concise description of what you expected to happen.
|
|
22
|
+
|
|
23
|
+
**screenshots**
|
|
24
|
+
If applicable, add screenshots to help explain your problem.
|
|
25
|
+
|
|
26
|
+
**Additional context**
|
|
27
|
+
Add any other context about the problem here, such as specific device information.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest an idea for this project
|
|
4
|
+
title: ''
|
|
5
|
+
labels: feature-request
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**The problem**
|
|
11
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
12
|
+
|
|
13
|
+
**Suggested solution**
|
|
14
|
+
A clear and concise description of what you want to happen.
|
|
15
|
+
|
|
16
|
+
**Alternatives considered**
|
|
17
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
|
18
|
+
|
|
19
|
+
**Additional context**
|
|
20
|
+
Add any other context or screenshots about the feature request here.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Technical question
|
|
3
|
+
about: Want to ask a technical question about the project
|
|
4
|
+
title: ''
|
|
5
|
+
labels: question
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Question**
|
|
11
|
+
A clear technical question could be asked here.
|
|
12
|
+
|
|
13
|
+
**screenshots**
|
|
14
|
+
If applicable, add screenshots to help explain your question.
|
|
15
|
+
|
|
16
|
+
**Additional context**
|
|
17
|
+
Add any other context about the question here, such as specific device information, technology choice etc.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!--- Provide a general summary of your changes in the Title above -->
|
|
2
|
+
## Description
|
|
3
|
+
<!--- Describe your changes in detail -->
|
|
4
|
+
## Motivation and Context
|
|
5
|
+
<!--- Why is this change required? What problem does it solve? -->
|
|
6
|
+
<!--- If it fixes an open issue, please link to the issue here. -->
|
|
7
|
+
## Types of changes
|
|
8
|
+
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
|
9
|
+
- [ ] Bug fix (non-breaking change which fixes an issue)
|
|
10
|
+
- [ ] New feature (non-breaking change which adds functionality)
|
|
11
|
+
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
|
12
|
+
## Checklist:
|
|
13
|
+
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
|
14
|
+
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
|
15
|
+
- [ ] My code follows the code style of this project.
|
|
16
|
+
- [ ] My change requires a change to the documentation.
|
|
17
|
+
- [ ] I have updated the documentation accordingly.
|
|
18
|
+
- [ ] I have read the **CONTRIBUTING** document.
|
|
19
|
+
- [ ] I have added tests to cover my changes.
|
|
20
|
+
- [ ] All new and existing tests passed.
|
package/.travis.yml
CHANGED
package/README.md
CHANGED
|
@@ -40,6 +40,36 @@ app.start();
|
|
|
40
40
|
* Messages are deleted from the queue once the handler function has completed successfully.
|
|
41
41
|
* 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
42
|
* 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
|
+
```js
|
|
45
|
+
const { Consumer } = require('sqs-consumer');
|
|
46
|
+
const AWS = require('aws-sdk');
|
|
47
|
+
const https = require('https');
|
|
48
|
+
|
|
49
|
+
const app = Consumer.create({
|
|
50
|
+
queueUrl: 'https://sqs.eu-west-1.amazonaws.com/account-id/queue-name',
|
|
51
|
+
handleMessage: async (message) => {
|
|
52
|
+
// do some work with `message`
|
|
53
|
+
},
|
|
54
|
+
sqs: new AWS.SQS({
|
|
55
|
+
httpOptions: {
|
|
56
|
+
agent: new https.Agent({
|
|
57
|
+
keepAlive: true
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
app.on('error', (err) => {
|
|
64
|
+
console.error(err.message);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
app.on('processing_error', (err) => {
|
|
68
|
+
console.error(err.message);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
app.start();
|
|
72
|
+
```
|
|
43
73
|
|
|
44
74
|
### Credentials
|
|
45
75
|
|
|
@@ -98,17 +128,18 @@ Creates a new SQS consumer.
|
|
|
98
128
|
* `region` - _String_ - The AWS region (default `eu-west-1`)
|
|
99
129
|
* `handleMessage` - _Function_ - An `async` function (or function that returns a `Promise`) to be called whenever a message is received. Receives an SQS message object as it's first argument.
|
|
100
130
|
* `handleMessageBatch` - _Function_ - An `async` function (or function that returns a `Promise`) to be called whenever a batch of messages is received. Similar to `handleMessage` but will receive the list of messages, not each message individually. **If both are set, `handleMessageBatch` overrides `handleMessage`**.
|
|
101
|
-
* `handleMessageTimeout` -
|
|
131
|
+
* `handleMessageTimeout` - _Number_ - Time in ms to wait for `handleMessage` to process a message before timing out. Emits `timeout_error` on timeout. By default, if `handleMessage` times out, the unprocessed message returns to the end of the queue.
|
|
102
132
|
* `attributeNames` - _Array_ - List of queue attributes to retrieve (i.e. `['All', 'ApproximateFirstReceiveTimestamp', 'ApproximateReceiveCount']`).
|
|
103
133
|
* `messageAttributeNames` - _Array_ - List of message attributes to retrieve (i.e. `['name', 'address']`).
|
|
104
134
|
* `batchSize` - _Number_ - The number of messages to request from SQS when polling (default `1`). This cannot be higher than the AWS limit of 10.
|
|
105
135
|
* `visibilityTimeout` - _Number_ - The duration (in seconds) that the received messages are hidden from subsequent retrieve requests after being retrieved by a ReceiveMessage request.
|
|
136
|
+
* `heartbeatInterval` - _Number_ - The interval (in seconds) between requests to extend the message visibility timeout. On each heartbeat the visibility is extended by adding `visibilityTimeout` to the number of seconds since the start of the handler function. This value must less than `visibilityTimeout`.
|
|
106
137
|
* `terminateVisibilityTimeout` - _Boolean_ - If true, sets the message visibility timeout to 0 after a `processing_error` (defaults to `false`).
|
|
107
138
|
* `waitTimeSeconds` - _Number_ - The duration (in seconds) for which the call will wait for a message to arrive in the queue before returning.
|
|
108
139
|
* `authenticationErrorTimeout` - _Number_ - The duration (in milliseconds) to wait before retrying after an authentication error (defaults to `10000`).
|
|
109
140
|
* `pollingWaitTimeMs` - _Number_ - The duration (in milliseconds) to wait before repolling the queue (defaults to `0`).
|
|
110
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
|
|
111
|
-
|
|
142
|
+
* `shouldDeleteMessages` - _Boolean_ - Default to `true`, if you don't want the package to delete messages from sqs set this to `false`
|
|
112
143
|
### `consumer.start()`
|
|
113
144
|
|
|
114
145
|
Start polling the queue for messages.
|
|
@@ -127,7 +158,7 @@ Each consumer is an [`EventEmitter`](http://nodejs.org/api/events.html) and emit
|
|
|
127
158
|
|
|
128
159
|
|Event|Params|Description|
|
|
129
160
|
|-----|------|-----------|
|
|
130
|
-
|`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|
|
|
131
162
|
|`processing_error`|`err`, `message`|Fired when an error occurs processing the message.|
|
|
132
163
|
|`timeout_error`|`err`, `message`|Fired when `handleMessageTimeout` is supplied as an option and if `handleMessage` times out.|
|
|
133
164
|
|`message_received`|`message`|Fired when a message is received.|
|
|
@@ -138,8 +169,7 @@ Each consumer is an [`EventEmitter`](http://nodejs.org/api/events.html) and emit
|
|
|
138
169
|
|
|
139
170
|
### AWS IAM Permissions
|
|
140
171
|
|
|
141
|
-
Consumer will receive and delete messages from the SQS queue. Ensure `sqs:ReceiveMessage` and `sqs:
|
|
142
|
-
|
|
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.
|
|
143
173
|
|
|
144
174
|
### Contributing
|
|
145
|
-
See contributing [
|
|
175
|
+
See contributing [guidelines](https://github.com/bbc/sqs-consumer/blob/master/.github/CONTRIBUTING.md).
|
package/dist/bind.js
CHANGED
package/dist/consumer.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import * as SQS from 'aws-sdk/clients/sqs';
|
|
2
3
|
import { EventEmitter } from 'events';
|
|
3
|
-
declare type SQSMessage = SQS.Types.Message;
|
|
4
|
+
export declare type SQSMessage = SQS.Types.Message;
|
|
4
5
|
export interface ConsumerOptions {
|
|
5
6
|
queueUrl?: string;
|
|
6
7
|
attributeNames?: string[];
|
|
@@ -12,12 +13,24 @@ export interface ConsumerOptions {
|
|
|
12
13
|
authenticationErrorTimeout?: number;
|
|
13
14
|
pollingWaitTimeMs?: number;
|
|
14
15
|
terminateVisibilityTimeout?: boolean;
|
|
16
|
+
heartbeatInterval?: number;
|
|
15
17
|
sqs?: SQS;
|
|
16
18
|
region?: string;
|
|
17
19
|
handleMessageTimeout?: number;
|
|
20
|
+
shouldDeleteMessages?: boolean;
|
|
18
21
|
handleMessage?(message: SQSMessage): Promise<void>;
|
|
19
22
|
handleMessageBatch?(messages: SQSMessage[]): Promise<void>;
|
|
20
23
|
}
|
|
24
|
+
interface Events {
|
|
25
|
+
'response_processed': [];
|
|
26
|
+
'empty': [];
|
|
27
|
+
'message_received': [SQSMessage];
|
|
28
|
+
'message_processed': [SQSMessage];
|
|
29
|
+
'error': [Error, void | SQSMessage | SQSMessage[]];
|
|
30
|
+
'timeout_error': [Error, SQSMessage];
|
|
31
|
+
'processing_error': [Error, SQSMessage];
|
|
32
|
+
'stopped': [];
|
|
33
|
+
}
|
|
21
34
|
export declare class Consumer extends EventEmitter {
|
|
22
35
|
private queueUrl;
|
|
23
36
|
private handleMessage;
|
|
@@ -32,9 +45,14 @@ export declare class Consumer extends EventEmitter {
|
|
|
32
45
|
private authenticationErrorTimeout;
|
|
33
46
|
private pollingWaitTimeMs;
|
|
34
47
|
private terminateVisibilityTimeout;
|
|
48
|
+
private heartbeatInterval;
|
|
35
49
|
private sqs;
|
|
50
|
+
private shouldDeleteMessages;
|
|
36
51
|
constructor(options: ConsumerOptions);
|
|
37
|
-
|
|
52
|
+
emit<T extends keyof Events>(event: T, ...args: Events[T]): boolean;
|
|
53
|
+
on<T extends keyof Events>(event: T, listener: (...args: Events[T]) => void): this;
|
|
54
|
+
once<T extends keyof Events>(event: T, listener: (...args: Events[T]) => void): this;
|
|
55
|
+
get isRunning(): boolean;
|
|
38
56
|
static create(options: ConsumerOptions): Consumer;
|
|
39
57
|
start(): void;
|
|
40
58
|
stop(): void;
|
|
@@ -43,12 +61,13 @@ export declare class Consumer extends EventEmitter {
|
|
|
43
61
|
private receiveMessage;
|
|
44
62
|
private deleteMessage;
|
|
45
63
|
private executeHandler;
|
|
46
|
-
private
|
|
64
|
+
private changeVisibilityTimeout;
|
|
47
65
|
private emitError;
|
|
48
66
|
private poll;
|
|
49
67
|
private processMessageBatch;
|
|
50
68
|
private deleteMessageBatch;
|
|
51
69
|
private executeBatchHandler;
|
|
52
|
-
private
|
|
70
|
+
private changeVisabilityTimeoutBatch;
|
|
71
|
+
private startHeartbeat;
|
|
53
72
|
}
|
|
54
73
|
export {};
|
package/dist/consumer.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Consumer = void 0;
|
|
3
4
|
const SQS = require("aws-sdk/clients/sqs");
|
|
4
5
|
const Debug = require("debug");
|
|
5
6
|
const events_1 = require("events");
|
|
@@ -30,6 +31,9 @@ function assertOptions(options) {
|
|
|
30
31
|
if (options.batchSize > 10 || options.batchSize < 1) {
|
|
31
32
|
throw new Error('SQS batchSize option must be between 1 and 10.');
|
|
32
33
|
}
|
|
34
|
+
if (options.heartbeatInterval && !(options.heartbeatInterval < options.visibilityTimeout)) {
|
|
35
|
+
throw new Error('heartbeatInterval must be less than visibilityTimeout.');
|
|
36
|
+
}
|
|
33
37
|
}
|
|
34
38
|
function isConnectionError(err) {
|
|
35
39
|
if (err instanceof errors_1.SQSError) {
|
|
@@ -52,6 +56,7 @@ function hasMessages(response) {
|
|
|
52
56
|
}
|
|
53
57
|
class Consumer extends events_1.EventEmitter {
|
|
54
58
|
constructor(options) {
|
|
59
|
+
var _a, _b, _c, _d;
|
|
55
60
|
super();
|
|
56
61
|
assertOptions(options);
|
|
57
62
|
this.queueUrl = options.queueUrl;
|
|
@@ -64,14 +69,25 @@ class Consumer extends events_1.EventEmitter {
|
|
|
64
69
|
this.batchSize = options.batchSize || 1;
|
|
65
70
|
this.visibilityTimeout = options.visibilityTimeout;
|
|
66
71
|
this.terminateVisibilityTimeout = options.terminateVisibilityTimeout || false;
|
|
67
|
-
this.
|
|
68
|
-
this.
|
|
69
|
-
this.
|
|
72
|
+
this.heartbeatInterval = options.heartbeatInterval;
|
|
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;
|
|
70
77
|
this.sqs = options.sqs || new SQS({
|
|
71
78
|
region: options.region || process.env.AWS_REGION || 'eu-west-1'
|
|
72
79
|
});
|
|
73
80
|
bind_1.autoBind(this);
|
|
74
81
|
}
|
|
82
|
+
emit(event, ...args) {
|
|
83
|
+
return super.emit(event, ...args);
|
|
84
|
+
}
|
|
85
|
+
on(event, listener) {
|
|
86
|
+
return super.on(event, listener);
|
|
87
|
+
}
|
|
88
|
+
once(event, listener) {
|
|
89
|
+
return super.once(event, listener);
|
|
90
|
+
}
|
|
75
91
|
get isRunning() {
|
|
76
92
|
return !this.stopped;
|
|
77
93
|
}
|
|
@@ -110,7 +126,13 @@ class Consumer extends events_1.EventEmitter {
|
|
|
110
126
|
}
|
|
111
127
|
async processMessage(message) {
|
|
112
128
|
this.emit('message_received', message);
|
|
129
|
+
let heartbeat;
|
|
113
130
|
try {
|
|
131
|
+
if (this.heartbeatInterval) {
|
|
132
|
+
heartbeat = this.startHeartbeat(async () => {
|
|
133
|
+
return this.changeVisibilityTimeout(message, this.visibilityTimeout);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
114
136
|
await this.executeHandler(message);
|
|
115
137
|
await this.deleteMessage(message);
|
|
116
138
|
this.emit('message_processed', message);
|
|
@@ -118,14 +140,12 @@ class Consumer extends events_1.EventEmitter {
|
|
|
118
140
|
catch (err) {
|
|
119
141
|
this.emitError(err, message);
|
|
120
142
|
if (this.terminateVisibilityTimeout) {
|
|
121
|
-
|
|
122
|
-
await this.terminateVisabilityTimeout(message);
|
|
123
|
-
}
|
|
124
|
-
catch (err) {
|
|
125
|
-
this.emit('error', err, message);
|
|
126
|
-
}
|
|
143
|
+
await this.changeVisibilityTimeout(message, 0);
|
|
127
144
|
}
|
|
128
145
|
}
|
|
146
|
+
finally {
|
|
147
|
+
clearInterval(heartbeat);
|
|
148
|
+
}
|
|
129
149
|
}
|
|
130
150
|
async receiveMessage(params) {
|
|
131
151
|
try {
|
|
@@ -138,6 +158,10 @@ class Consumer extends events_1.EventEmitter {
|
|
|
138
158
|
}
|
|
139
159
|
}
|
|
140
160
|
async deleteMessage(message) {
|
|
161
|
+
if (!this.shouldDeleteMessages) {
|
|
162
|
+
debug('Skipping message delete since shouldDeleteMessages is set to false');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
141
165
|
debug('Deleting message %s', message.MessageId);
|
|
142
166
|
const deleteParams = {
|
|
143
167
|
QueueUrl: this.queueUrl,
|
|
@@ -171,7 +195,7 @@ class Consumer extends events_1.EventEmitter {
|
|
|
171
195
|
if (err instanceof errors_1.TimeoutError) {
|
|
172
196
|
err.message = `Message handler timed out after ${this.handleMessageTimeout}ms: Operation timed out.`;
|
|
173
197
|
}
|
|
174
|
-
else {
|
|
198
|
+
else if (err instanceof Error) {
|
|
175
199
|
err.message = `Unexpected message handler failure: ${err.message}`;
|
|
176
200
|
}
|
|
177
201
|
throw err;
|
|
@@ -180,14 +204,19 @@ class Consumer extends events_1.EventEmitter {
|
|
|
180
204
|
clearTimeout(timeout);
|
|
181
205
|
}
|
|
182
206
|
}
|
|
183
|
-
async
|
|
184
|
-
|
|
185
|
-
.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
207
|
+
async changeVisibilityTimeout(message, timeout) {
|
|
208
|
+
try {
|
|
209
|
+
return await this.sqs
|
|
210
|
+
.changeMessageVisibility({
|
|
211
|
+
QueueUrl: this.queueUrl,
|
|
212
|
+
ReceiptHandle: message.ReceiptHandle,
|
|
213
|
+
VisibilityTimeout: timeout
|
|
214
|
+
})
|
|
215
|
+
.promise();
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
this.emit('error', toSQSError(err, `Error changing visibility timeout: ${err.message}`), message);
|
|
219
|
+
}
|
|
191
220
|
}
|
|
192
221
|
emitError(err, message) {
|
|
193
222
|
if (err.name === errors_1.SQSError.name) {
|
|
@@ -234,7 +263,13 @@ class Consumer extends events_1.EventEmitter {
|
|
|
234
263
|
messages.forEach((message) => {
|
|
235
264
|
this.emit('message_received', message);
|
|
236
265
|
});
|
|
266
|
+
let heartbeat;
|
|
237
267
|
try {
|
|
268
|
+
if (this.heartbeatInterval) {
|
|
269
|
+
heartbeat = this.startHeartbeat(async () => {
|
|
270
|
+
return this.changeVisabilityTimeoutBatch(messages, this.visibilityTimeout);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
238
273
|
await this.executeBatchHandler(messages);
|
|
239
274
|
await this.deleteMessageBatch(messages);
|
|
240
275
|
messages.forEach((message) => {
|
|
@@ -244,20 +279,22 @@ class Consumer extends events_1.EventEmitter {
|
|
|
244
279
|
catch (err) {
|
|
245
280
|
this.emit('error', err, messages);
|
|
246
281
|
if (this.terminateVisibilityTimeout) {
|
|
247
|
-
|
|
248
|
-
await this.terminateVisabilityTimeoutBatch(messages);
|
|
249
|
-
}
|
|
250
|
-
catch (err) {
|
|
251
|
-
this.emit('error', err, messages);
|
|
252
|
-
}
|
|
282
|
+
await this.changeVisabilityTimeoutBatch(messages, 0);
|
|
253
283
|
}
|
|
254
284
|
}
|
|
285
|
+
finally {
|
|
286
|
+
clearInterval(heartbeat);
|
|
287
|
+
}
|
|
255
288
|
}
|
|
256
289
|
async deleteMessageBatch(messages) {
|
|
290
|
+
if (!this.shouldDeleteMessages) {
|
|
291
|
+
debug('Skipping message delete since shouldDeleteMessages is set to false');
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
257
294
|
debug('Deleting messages %s', messages.map((msg) => msg.MessageId).join(' ,'));
|
|
258
295
|
const deleteParams = {
|
|
259
296
|
QueueUrl: this.queueUrl,
|
|
260
|
-
Entries: messages.map(message => ({
|
|
297
|
+
Entries: messages.map((message) => ({
|
|
261
298
|
Id: message.MessageId,
|
|
262
299
|
ReceiptHandle: message.ReceiptHandle
|
|
263
300
|
}))
|
|
@@ -280,18 +317,28 @@ class Consumer extends events_1.EventEmitter {
|
|
|
280
317
|
throw err;
|
|
281
318
|
}
|
|
282
319
|
}
|
|
283
|
-
async
|
|
320
|
+
async changeVisabilityTimeoutBatch(messages, timeout) {
|
|
284
321
|
const params = {
|
|
285
322
|
QueueUrl: this.queueUrl,
|
|
286
323
|
Entries: messages.map((message) => ({
|
|
287
324
|
Id: message.MessageId,
|
|
288
325
|
ReceiptHandle: message.ReceiptHandle,
|
|
289
|
-
VisibilityTimeout:
|
|
326
|
+
VisibilityTimeout: timeout
|
|
290
327
|
}))
|
|
291
328
|
};
|
|
292
|
-
|
|
293
|
-
.
|
|
294
|
-
|
|
329
|
+
try {
|
|
330
|
+
return await this.sqs
|
|
331
|
+
.changeMessageVisibilityBatch(params)
|
|
332
|
+
.promise();
|
|
333
|
+
}
|
|
334
|
+
catch (err) {
|
|
335
|
+
this.emit('error', toSQSError(err, `Error changing visibility timeout: ${err.message}`), messages);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
startHeartbeat(heartbeatFn) {
|
|
339
|
+
return setInterval(() => {
|
|
340
|
+
heartbeatFn();
|
|
341
|
+
}, this.heartbeatInterval * 1000);
|
|
295
342
|
}
|
|
296
343
|
}
|
|
297
344
|
exports.Consumer = Consumer;
|
package/dist/errors.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Consumer, ConsumerOptions } from './consumer';
|
|
1
|
+
export { SQSMessage, Consumer, ConsumerOptions } from './consumer';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
var consumer_1 = require("./consumer");
|
|
4
|
-
exports
|
|
4
|
+
Object.defineProperty(exports, "Consumer", { enumerable: true, get: function () { return consumer_1.Consumer; } });
|
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",
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
"clean": "rm -fr dist/*",
|
|
11
11
|
"prepublish": "npm run build",
|
|
12
12
|
"pretest": "npm run build",
|
|
13
|
-
"test": "mocha",
|
|
14
|
-
"lint": "
|
|
13
|
+
"test": "mocha --recursive --full-trace --exit",
|
|
14
|
+
"lint": "eslint . --ext .ts",
|
|
15
|
+
"lint:fix": "eslint . --fix",
|
|
15
16
|
"coverage": "nyc mocha && nyc report --reporter=html && nyc report --reporter=json-summary",
|
|
16
17
|
"lcov": "nyc mocha && nyc report --reporter=lcov",
|
|
17
18
|
"posttest": "npm run lint"
|
|
@@ -31,27 +32,31 @@
|
|
|
31
32
|
],
|
|
32
33
|
"license": "Apache-2.0",
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"@types/chai": "^4.
|
|
35
|
-
"@types/debug": "^4.1.
|
|
36
|
-
"@types/mocha": "^
|
|
37
|
-
"@types/node": "^
|
|
38
|
-
"@types/sinon": "^
|
|
35
|
+
"@types/chai": "^4.2.11",
|
|
36
|
+
"@types/debug": "^4.1.5",
|
|
37
|
+
"@types/mocha": "^7.0.2",
|
|
38
|
+
"@types/node": "^14.0.13",
|
|
39
|
+
"@types/sinon": "^9.0.4",
|
|
39
40
|
"@types/typescript": "^2.0.0",
|
|
40
41
|
"chai": "^4.2.0",
|
|
41
42
|
"codeclimate-test-reporter": "^0.5.1",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"tslint-
|
|
50
|
-
"
|
|
43
|
+
"eslint": "^7.2.0",
|
|
44
|
+
"eslint-config-iplayer-ts": "^2.0.0",
|
|
45
|
+
"mocha": "^8.0.1",
|
|
46
|
+
"nyc": "^15.1.0",
|
|
47
|
+
"p-event": "^4.2.0",
|
|
48
|
+
"sinon": "^9.0.2",
|
|
49
|
+
"ts-node": "^8.10.2",
|
|
50
|
+
"tslint-config-airbnb": "^5.11.2",
|
|
51
|
+
"tslint-microsoft-contrib": "^6.2.0",
|
|
52
|
+
"typescript": "^3.9.5",
|
|
53
|
+
"aws-sdk": "^2.1114.0"
|
|
51
54
|
},
|
|
52
55
|
"dependencies": {
|
|
53
|
-
"
|
|
54
|
-
|
|
56
|
+
"debug": "^4.3.1"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"aws-sdk": "^2.1114.0"
|
|
55
60
|
},
|
|
56
61
|
"nyc": {
|
|
57
62
|
"include": [
|
|
@@ -65,5 +70,16 @@
|
|
|
65
70
|
],
|
|
66
71
|
"sourceMap": true,
|
|
67
72
|
"instrument": true
|
|
73
|
+
},
|
|
74
|
+
"eslintConfig": {
|
|
75
|
+
"extends": "iplayer-ts",
|
|
76
|
+
"parserOptions": {
|
|
77
|
+
"ecmaVersion": 2017,
|
|
78
|
+
"sourceType": "module"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"mocha": {
|
|
82
|
+
"spec": "test/**/**/*.ts",
|
|
83
|
+
"require": "ts-node/register"
|
|
68
84
|
}
|
|
69
85
|
}
|