sqs-consumer 6.0.2 → 6.2.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/CONTRIBUTING.md +10 -0
- package/.github/ISSUE_TEMPLATE/bug-report.yml +115 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/workflows/cla.yml +25 -0
- package/.github/workflows/codeql.yml +76 -0
- package/.github/workflows/coverage.yml +1 -1
- package/.github/workflows/docs.yml +55 -0
- package/.prettierignore +2 -1
- package/README.md +22 -42
- package/dist/bind.d.ts +4 -0
- package/dist/bind.js +9 -0
- package/dist/consumer.d.ts +90 -51
- package/dist/consumer.js +247 -213
- package/dist/errors.d.ts +13 -1
- package/dist/errors.js +32 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +15 -0
- package/dist/types.d.ts +163 -0
- package/dist/types.js +28 -0
- package/dist/validation.d.ts +13 -0
- package/dist/validation.js +36 -0
- package/package.json +14 -14
- package/src/bind.ts +9 -0
- package/src/consumer.ts +291 -301
- package/src/errors.ts +36 -1
- package/src/index.ts +2 -1
- package/src/types.ts +178 -0
- package/src/validation.ts +45 -0
- package/typedoc.json +13 -0
- 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/dist/consumer.js
CHANGED
|
@@ -3,73 +3,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Consumer = void 0;
|
|
4
4
|
const client_sqs_1 = require("@aws-sdk/client-sqs");
|
|
5
5
|
const debug_1 = require("debug");
|
|
6
|
-
const
|
|
6
|
+
const types_1 = require("./types");
|
|
7
7
|
const bind_1 = require("./bind");
|
|
8
8
|
const errors_1 = require("./errors");
|
|
9
|
+
const validation_1 = require("./validation");
|
|
9
10
|
const debug = (0, debug_1.default)('sqs-consumer');
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
];
|
|
15
|
-
function createTimeout(duration) {
|
|
16
|
-
let timeout;
|
|
17
|
-
const pending = new Promise((_, reject) => {
|
|
18
|
-
timeout = setTimeout(() => {
|
|
19
|
-
reject(new errors_1.TimeoutError());
|
|
20
|
-
}, duration);
|
|
21
|
-
});
|
|
22
|
-
return [timeout, pending];
|
|
23
|
-
}
|
|
24
|
-
function assertOptions(options) {
|
|
25
|
-
requiredOptions.forEach((option) => {
|
|
26
|
-
const possibilities = option.split('|');
|
|
27
|
-
if (!possibilities.find((p) => options[p])) {
|
|
28
|
-
throw new Error(`Missing SQS consumer option [ ${possibilities.join(' or ')} ].`);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
if (options.batchSize > 10 || options.batchSize < 1) {
|
|
32
|
-
throw new Error('SQS batchSize option must be between 1 and 10.');
|
|
33
|
-
}
|
|
34
|
-
if (options.heartbeatInterval &&
|
|
35
|
-
!(options.heartbeatInterval < options.visibilityTimeout)) {
|
|
36
|
-
throw new Error('heartbeatInterval must be less than visibilityTimeout.');
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function isConnectionError(err) {
|
|
40
|
-
if (err instanceof errors_1.SQSError) {
|
|
41
|
-
return (err.statusCode === 403 ||
|
|
42
|
-
err.code === 'CredentialsError' ||
|
|
43
|
-
err.code === 'UnknownEndpoint');
|
|
44
|
-
}
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
function toSQSError(err, message) {
|
|
48
|
-
var _a, _b;
|
|
49
|
-
const sqsError = new errors_1.SQSError(message);
|
|
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();
|
|
56
|
-
return sqsError;
|
|
57
|
-
}
|
|
58
|
-
function hasMessages(response) {
|
|
59
|
-
return response.Messages && response.Messages.length > 0;
|
|
60
|
-
}
|
|
61
|
-
class Consumer extends events_1.EventEmitter {
|
|
11
|
+
/**
|
|
12
|
+
* [Usage](https://bbc.github.io/sqs-consumer/index.html#usage)
|
|
13
|
+
*/
|
|
14
|
+
class Consumer extends types_1.TypedEventEmitter {
|
|
62
15
|
constructor(options) {
|
|
63
16
|
var _a, _b, _c, _d;
|
|
64
17
|
super();
|
|
65
|
-
|
|
18
|
+
this.pollingTimeoutId = undefined;
|
|
19
|
+
this.heartbeatTimeoutId = undefined;
|
|
20
|
+
this.handleMessageTimeoutId = undefined;
|
|
21
|
+
this.stopped = true;
|
|
22
|
+
(0, validation_1.assertOptions)(options);
|
|
66
23
|
this.queueUrl = options.queueUrl;
|
|
67
24
|
this.handleMessage = options.handleMessage;
|
|
68
25
|
this.handleMessageBatch = options.handleMessageBatch;
|
|
69
26
|
this.handleMessageTimeout = options.handleMessageTimeout;
|
|
70
27
|
this.attributeNames = options.attributeNames || [];
|
|
71
28
|
this.messageAttributeNames = options.messageAttributeNames || [];
|
|
72
|
-
this.stopped = true;
|
|
73
29
|
this.batchSize = options.batchSize || 1;
|
|
74
30
|
this.visibilityTimeout = options.visibilityTimeout;
|
|
75
31
|
this.terminateVisibilityTimeout =
|
|
@@ -87,21 +43,15 @@ class Consumer extends events_1.EventEmitter {
|
|
|
87
43
|
});
|
|
88
44
|
(0, bind_1.autoBind)(this);
|
|
89
45
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
on(event, listener) {
|
|
94
|
-
return super.on(event, listener);
|
|
95
|
-
}
|
|
96
|
-
once(event, listener) {
|
|
97
|
-
return super.once(event, listener);
|
|
98
|
-
}
|
|
99
|
-
get isRunning() {
|
|
100
|
-
return !this.stopped;
|
|
101
|
-
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates a new SQS consumer.
|
|
48
|
+
*/
|
|
102
49
|
static create(options) {
|
|
103
50
|
return new Consumer(options);
|
|
104
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Start polling the queue for messages.
|
|
54
|
+
*/
|
|
105
55
|
start() {
|
|
106
56
|
if (this.stopped) {
|
|
107
57
|
debug('Starting consumer');
|
|
@@ -109,117 +59,38 @@ class Consumer extends events_1.EventEmitter {
|
|
|
109
59
|
this.poll();
|
|
110
60
|
}
|
|
111
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Stop polling the queue for messages (pre existing requests will still be made until concluded).
|
|
64
|
+
*/
|
|
112
65
|
stop() {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
async handleSqsResponse(response) {
|
|
117
|
-
debug('Received SQS response');
|
|
118
|
-
debug(response);
|
|
119
|
-
if (response) {
|
|
120
|
-
if (hasMessages(response)) {
|
|
121
|
-
if (this.handleMessageBatch) {
|
|
122
|
-
// prefer handling messages in batch when available
|
|
123
|
-
await this.processMessageBatch(response.Messages);
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
await Promise.all(response.Messages.map(this.processMessage));
|
|
127
|
-
}
|
|
128
|
-
this.emit('response_processed');
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
this.emit('empty');
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
async processMessage(message) {
|
|
136
|
-
this.emit('message_received', message);
|
|
137
|
-
let heartbeat;
|
|
138
|
-
try {
|
|
139
|
-
if (this.heartbeatInterval) {
|
|
140
|
-
heartbeat = this.startHeartbeat(async () => {
|
|
141
|
-
return this.changeVisibilityTimeout(message, this.visibilityTimeout);
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
await this.executeHandler(message);
|
|
145
|
-
await this.deleteMessage(message);
|
|
146
|
-
this.emit('message_processed', message);
|
|
147
|
-
}
|
|
148
|
-
catch (err) {
|
|
149
|
-
this.emitError(err, message);
|
|
150
|
-
if (this.terminateVisibilityTimeout) {
|
|
151
|
-
await this.changeVisibilityTimeout(message, 0);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
finally {
|
|
155
|
-
clearInterval(heartbeat);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
async receiveMessage(params) {
|
|
159
|
-
try {
|
|
160
|
-
return await this.sqs.send(new client_sqs_1.ReceiveMessageCommand(params));
|
|
161
|
-
}
|
|
162
|
-
catch (err) {
|
|
163
|
-
throw toSQSError(err, `SQS receive message failed: ${err.message}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
async deleteMessage(message) {
|
|
167
|
-
if (!this.shouldDeleteMessages) {
|
|
168
|
-
debug('Skipping message delete since shouldDeleteMessages is set to false');
|
|
66
|
+
if (this.stopped) {
|
|
67
|
+
debug('Consumer was already stopped');
|
|
169
68
|
return;
|
|
170
69
|
}
|
|
171
|
-
debug('
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
try {
|
|
177
|
-
await this.sqs.send(new client_sqs_1.DeleteMessageCommand(deleteParams));
|
|
178
|
-
}
|
|
179
|
-
catch (err) {
|
|
180
|
-
throw toSQSError(err, `SQS delete message failed: ${err.message}`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
async executeHandler(message) {
|
|
184
|
-
let timeout;
|
|
185
|
-
let pending;
|
|
186
|
-
try {
|
|
187
|
-
if (this.handleMessageTimeout) {
|
|
188
|
-
[timeout, pending] = createTimeout(this.handleMessageTimeout);
|
|
189
|
-
await Promise.race([this.handleMessage(message), pending]);
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
await this.handleMessage(message);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
catch (err) {
|
|
196
|
-
if (err instanceof errors_1.TimeoutError) {
|
|
197
|
-
err.message = `Message handler timed out after ${this.handleMessageTimeout}ms: Operation timed out.`;
|
|
198
|
-
}
|
|
199
|
-
else if (err instanceof Error) {
|
|
200
|
-
err.message = `Unexpected message handler failure: ${err.message}`;
|
|
201
|
-
}
|
|
202
|
-
throw err;
|
|
203
|
-
}
|
|
204
|
-
finally {
|
|
205
|
-
clearTimeout(timeout);
|
|
70
|
+
debug('Stopping consumer');
|
|
71
|
+
this.stopped = true;
|
|
72
|
+
if (this.pollingTimeoutId) {
|
|
73
|
+
clearTimeout(this.pollingTimeoutId);
|
|
74
|
+
this.pollingTimeoutId = undefined;
|
|
206
75
|
}
|
|
76
|
+
this.emit('stopped');
|
|
207
77
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
VisibilityTimeout: timeout
|
|
214
|
-
};
|
|
215
|
-
return await this.sqs.send(new client_sqs_1.ChangeMessageVisibilityCommand(input));
|
|
216
|
-
}
|
|
217
|
-
catch (err) {
|
|
218
|
-
this.emit('error', toSQSError(err, `Error changing visibility timeout: ${err.message}`), message);
|
|
219
|
-
}
|
|
78
|
+
/**
|
|
79
|
+
* Returns the current polling state of the consumer: `true` if it is actively polling, `false` if it is not.
|
|
80
|
+
*/
|
|
81
|
+
get isRunning() {
|
|
82
|
+
return !this.stopped;
|
|
220
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Emit one of the consumer's error events depending on the error received.
|
|
86
|
+
* @param err The error object to forward on
|
|
87
|
+
* @param message The message that the error occurred on
|
|
88
|
+
*/
|
|
221
89
|
emitError(err, message) {
|
|
222
|
-
if (
|
|
90
|
+
if (!message) {
|
|
91
|
+
this.emit('error', err);
|
|
92
|
+
}
|
|
93
|
+
else if (err.name === errors_1.SQSError.name) {
|
|
223
94
|
this.emit('error', err, message);
|
|
224
95
|
}
|
|
225
96
|
else if (err instanceof errors_1.TimeoutError) {
|
|
@@ -229,54 +100,121 @@ class Consumer extends events_1.EventEmitter {
|
|
|
229
100
|
this.emit('processing_error', err, message);
|
|
230
101
|
}
|
|
231
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Poll for new messages from SQS
|
|
105
|
+
*/
|
|
232
106
|
poll() {
|
|
233
107
|
if (this.stopped) {
|
|
234
|
-
|
|
108
|
+
debug('Poll was called while consumer was stopped, cancelling poll...');
|
|
235
109
|
return;
|
|
236
110
|
}
|
|
237
111
|
debug('Polling for messages');
|
|
238
|
-
|
|
112
|
+
let currentPollingTimeout = this.pollingWaitTimeMs;
|
|
113
|
+
this.receiveMessage({
|
|
239
114
|
QueueUrl: this.queueUrl,
|
|
240
115
|
AttributeNames: this.attributeNames,
|
|
241
116
|
MessageAttributeNames: this.messageAttributeNames,
|
|
242
117
|
MaxNumberOfMessages: this.batchSize,
|
|
243
118
|
WaitTimeSeconds: this.waitTimeSeconds,
|
|
244
119
|
VisibilityTimeout: this.visibilityTimeout
|
|
245
|
-
}
|
|
246
|
-
let currentPollingTimeout = this.pollingWaitTimeMs;
|
|
247
|
-
this.receiveMessage(receiveParams)
|
|
120
|
+
})
|
|
248
121
|
.then(this.handleSqsResponse)
|
|
249
122
|
.catch((err) => {
|
|
250
|
-
this.
|
|
251
|
-
if (isConnectionError(err)) {
|
|
123
|
+
this.emitError(err);
|
|
124
|
+
if ((0, errors_1.isConnectionError)(err)) {
|
|
252
125
|
debug('There was an authentication error. Pausing before retrying.');
|
|
253
126
|
currentPollingTimeout = this.authenticationErrorTimeout;
|
|
254
127
|
}
|
|
255
128
|
return;
|
|
256
129
|
})
|
|
257
130
|
.then(() => {
|
|
258
|
-
|
|
131
|
+
if (this.pollingTimeoutId) {
|
|
132
|
+
clearTimeout(this.pollingTimeoutId);
|
|
133
|
+
}
|
|
134
|
+
this.pollingTimeoutId = setTimeout(this.poll, currentPollingTimeout);
|
|
259
135
|
})
|
|
260
136
|
.catch((err) => {
|
|
261
|
-
this.
|
|
137
|
+
this.emitError(err);
|
|
262
138
|
});
|
|
263
139
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Send a request to SQS to retrieve messages
|
|
142
|
+
* @param params The required params to receive messages from SQS
|
|
143
|
+
*/
|
|
144
|
+
async receiveMessage(params) {
|
|
145
|
+
try {
|
|
146
|
+
return await this.sqs.send(new client_sqs_1.ReceiveMessageCommand(params));
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
throw (0, errors_1.toSQSError)(err, `SQS receive message failed: ${err.message}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Handles the response from AWS SQS, determining if we should proceed to
|
|
154
|
+
* the message handler.
|
|
155
|
+
* @param response The output from AWS SQS
|
|
156
|
+
*/
|
|
157
|
+
async handleSqsResponse(response) {
|
|
158
|
+
if ((0, validation_1.hasMessages)(response)) {
|
|
159
|
+
if (this.handleMessageBatch) {
|
|
160
|
+
await this.processMessageBatch(response.Messages);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
await Promise.all(response.Messages.map(this.processMessage));
|
|
164
|
+
}
|
|
165
|
+
this.emit('response_processed');
|
|
166
|
+
}
|
|
167
|
+
else if (response) {
|
|
168
|
+
this.emit('empty');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Process a message that has been received from SQS. This will execute the message
|
|
173
|
+
* handler and delete the message once complete.
|
|
174
|
+
* @param message The message that was delivered from SQS
|
|
175
|
+
*/
|
|
176
|
+
async processMessage(message) {
|
|
269
177
|
try {
|
|
178
|
+
this.emit('message_received', message);
|
|
270
179
|
if (this.heartbeatInterval) {
|
|
271
|
-
|
|
272
|
-
return this.changeVisibilityTimeoutBatch(messages, this.visibilityTimeout);
|
|
273
|
-
});
|
|
180
|
+
this.heartbeatTimeoutId = this.startHeartbeat(message);
|
|
274
181
|
}
|
|
275
|
-
await this.
|
|
276
|
-
|
|
277
|
-
|
|
182
|
+
const ackedMessage = await this.executeHandler(message);
|
|
183
|
+
if ((ackedMessage === null || ackedMessage === void 0 ? void 0 : ackedMessage.MessageId) === message.MessageId) {
|
|
184
|
+
await this.deleteMessage(message);
|
|
278
185
|
this.emit('message_processed', message);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
this.emitError(err, message);
|
|
190
|
+
if (this.terminateVisibilityTimeout) {
|
|
191
|
+
await this.changeVisibilityTimeout(message, 0);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
clearInterval(this.heartbeatTimeoutId);
|
|
196
|
+
this.heartbeatTimeoutId = undefined;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Process a batch of messages from the SQS queue.
|
|
201
|
+
* @param messages The messages that were delivered from SQS
|
|
202
|
+
*/
|
|
203
|
+
async processMessageBatch(messages) {
|
|
204
|
+
try {
|
|
205
|
+
messages.forEach((message) => {
|
|
206
|
+
this.emit('message_received', message);
|
|
279
207
|
});
|
|
208
|
+
if (this.heartbeatInterval) {
|
|
209
|
+
this.heartbeatTimeoutId = this.startHeartbeat(null, messages);
|
|
210
|
+
}
|
|
211
|
+
const ackedMessages = await this.executeBatchHandler(messages);
|
|
212
|
+
if ((ackedMessages === null || ackedMessages === void 0 ? void 0 : ackedMessages.length) > 0) {
|
|
213
|
+
await this.deleteMessageBatch(ackedMessages);
|
|
214
|
+
ackedMessages.forEach((message) => {
|
|
215
|
+
this.emit('message_processed', message);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
280
218
|
}
|
|
281
219
|
catch (err) {
|
|
282
220
|
this.emit('error', err, messages);
|
|
@@ -285,58 +223,154 @@ class Consumer extends events_1.EventEmitter {
|
|
|
285
223
|
}
|
|
286
224
|
}
|
|
287
225
|
finally {
|
|
288
|
-
clearInterval(
|
|
226
|
+
clearInterval(this.heartbeatTimeoutId);
|
|
227
|
+
this.heartbeatTimeoutId = undefined;
|
|
289
228
|
}
|
|
290
229
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Trigger a function on a set interval
|
|
232
|
+
* @param heartbeatFn The function that should be triggered
|
|
233
|
+
*/
|
|
234
|
+
startHeartbeat(message, messages) {
|
|
235
|
+
return setInterval(() => {
|
|
236
|
+
if (this.handleMessageBatch) {
|
|
237
|
+
return this.changeVisibilityTimeoutBatch(messages, this.visibilityTimeout);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
return this.changeVisibilityTimeout(message, this.visibilityTimeout);
|
|
241
|
+
}
|
|
242
|
+
}, this.heartbeatInterval * 1000);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Change the visibility timeout on a message
|
|
246
|
+
* @param message The message to change the value of
|
|
247
|
+
* @param timeout The new timeout that should be set
|
|
248
|
+
*/
|
|
249
|
+
async changeVisibilityTimeout(message, timeout) {
|
|
250
|
+
try {
|
|
251
|
+
const input = {
|
|
252
|
+
QueueUrl: this.queueUrl,
|
|
253
|
+
ReceiptHandle: message.ReceiptHandle,
|
|
254
|
+
VisibilityTimeout: timeout
|
|
255
|
+
};
|
|
256
|
+
return await this.sqs.send(new client_sqs_1.ChangeMessageVisibilityCommand(input));
|
|
295
257
|
}
|
|
296
|
-
|
|
297
|
-
|
|
258
|
+
catch (err) {
|
|
259
|
+
this.emit('error', (0, errors_1.toSQSError)(err, `Error changing visibility timeout: ${err.message}`), message);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Change the visibility timeout on a batch of messages
|
|
264
|
+
* @param messages The messages to change the value of
|
|
265
|
+
* @param timeout The new timeout that should be set
|
|
266
|
+
*/
|
|
267
|
+
async changeVisibilityTimeoutBatch(messages, timeout) {
|
|
268
|
+
const params = {
|
|
298
269
|
QueueUrl: this.queueUrl,
|
|
299
270
|
Entries: messages.map((message) => ({
|
|
300
271
|
Id: message.MessageId,
|
|
301
|
-
ReceiptHandle: message.ReceiptHandle
|
|
272
|
+
ReceiptHandle: message.ReceiptHandle,
|
|
273
|
+
VisibilityTimeout: timeout
|
|
302
274
|
}))
|
|
303
275
|
};
|
|
304
276
|
try {
|
|
305
|
-
await this.sqs.send(new client_sqs_1.
|
|
277
|
+
return await this.sqs.send(new client_sqs_1.ChangeMessageVisibilityBatchCommand(params));
|
|
306
278
|
}
|
|
307
279
|
catch (err) {
|
|
308
|
-
|
|
280
|
+
this.emit('error', (0, errors_1.toSQSError)(err, `Error changing visibility timeout: ${err.message}`), messages);
|
|
309
281
|
}
|
|
310
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Trigger the applications handleMessage function
|
|
285
|
+
* @param message The message that was received from SQS
|
|
286
|
+
*/
|
|
287
|
+
async executeHandler(message) {
|
|
288
|
+
try {
|
|
289
|
+
let result;
|
|
290
|
+
if (this.handleMessageTimeout) {
|
|
291
|
+
const pending = new Promise((_, reject) => {
|
|
292
|
+
this.handleMessageTimeoutId = setTimeout(() => {
|
|
293
|
+
reject(new errors_1.TimeoutError());
|
|
294
|
+
}, this.handleMessageTimeout);
|
|
295
|
+
});
|
|
296
|
+
result = await Promise.race([this.handleMessage(message), pending]);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
result = await this.handleMessage(message);
|
|
300
|
+
}
|
|
301
|
+
return result instanceof Object ? result : message;
|
|
302
|
+
}
|
|
303
|
+
catch (err) {
|
|
304
|
+
err.message =
|
|
305
|
+
err instanceof errors_1.TimeoutError
|
|
306
|
+
? `Message handler timed out after ${this.handleMessageTimeout}ms: Operation timed out.`
|
|
307
|
+
: `Unexpected message handler failure: ${err.message}`;
|
|
308
|
+
throw err;
|
|
309
|
+
}
|
|
310
|
+
finally {
|
|
311
|
+
if (this.handleMessageTimeoutId) {
|
|
312
|
+
clearTimeout(this.handleMessageTimeoutId);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Execute the application's message batch handler
|
|
318
|
+
* @param messages The messages that should be forwarded from the SQS queue
|
|
319
|
+
*/
|
|
311
320
|
async executeBatchHandler(messages) {
|
|
312
321
|
try {
|
|
313
|
-
await this.handleMessageBatch(messages);
|
|
322
|
+
const result = await this.handleMessageBatch(messages);
|
|
323
|
+
return result instanceof Object ? result : messages;
|
|
314
324
|
}
|
|
315
325
|
catch (err) {
|
|
316
326
|
err.message = `Unexpected message handler failure: ${err.message}`;
|
|
317
327
|
throw err;
|
|
318
328
|
}
|
|
319
329
|
}
|
|
320
|
-
|
|
321
|
-
|
|
330
|
+
/**
|
|
331
|
+
* Delete a single message from SQS
|
|
332
|
+
* @param message The message to delete from the SQS queue
|
|
333
|
+
*/
|
|
334
|
+
async deleteMessage(message) {
|
|
335
|
+
if (!this.shouldDeleteMessages) {
|
|
336
|
+
debug('Skipping message delete since shouldDeleteMessages is set to false');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
debug('Deleting message %s', message.MessageId);
|
|
340
|
+
const deleteParams = {
|
|
341
|
+
QueueUrl: this.queueUrl,
|
|
342
|
+
ReceiptHandle: message.ReceiptHandle
|
|
343
|
+
};
|
|
344
|
+
try {
|
|
345
|
+
await this.sqs.send(new client_sqs_1.DeleteMessageCommand(deleteParams));
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
throw (0, errors_1.toSQSError)(err, `SQS delete message failed: ${err.message}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Delete a batch of messages from the SQS queue.
|
|
353
|
+
* @param messages The messages that should be deleted from SQS
|
|
354
|
+
*/
|
|
355
|
+
async deleteMessageBatch(messages) {
|
|
356
|
+
if (!this.shouldDeleteMessages) {
|
|
357
|
+
debug('Skipping message delete since shouldDeleteMessages is set to false');
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
debug('Deleting messages %s', messages.map((msg) => msg.MessageId).join(' ,'));
|
|
361
|
+
const deleteParams = {
|
|
322
362
|
QueueUrl: this.queueUrl,
|
|
323
363
|
Entries: messages.map((message) => ({
|
|
324
364
|
Id: message.MessageId,
|
|
325
|
-
ReceiptHandle: message.ReceiptHandle
|
|
326
|
-
VisibilityTimeout: timeout
|
|
365
|
+
ReceiptHandle: message.ReceiptHandle
|
|
327
366
|
}))
|
|
328
367
|
};
|
|
329
368
|
try {
|
|
330
|
-
|
|
369
|
+
await this.sqs.send(new client_sqs_1.DeleteMessageBatchCommand(deleteParams));
|
|
331
370
|
}
|
|
332
371
|
catch (err) {
|
|
333
|
-
|
|
372
|
+
throw (0, errors_1.toSQSError)(err, `SQS delete message failed: ${err.message}`);
|
|
334
373
|
}
|
|
335
374
|
}
|
|
336
|
-
startHeartbeat(heartbeatFn) {
|
|
337
|
-
return setInterval(() => {
|
|
338
|
-
heartbeatFn();
|
|
339
|
-
}, this.heartbeatInterval * 1000);
|
|
340
|
-
}
|
|
341
375
|
}
|
|
342
376
|
exports.Consumer = Consumer;
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AWSError } from './types';
|
|
1
2
|
declare class SQSError extends Error {
|
|
2
3
|
code: string;
|
|
3
4
|
statusCode: number;
|
|
@@ -10,4 +11,15 @@ declare class SQSError extends Error {
|
|
|
10
11
|
declare class TimeoutError extends Error {
|
|
11
12
|
constructor(message?: string);
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Checks if the error provided should be treated as a connection error.
|
|
16
|
+
* @param err The error that was received.
|
|
17
|
+
*/
|
|
18
|
+
declare function isConnectionError(err: Error): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Formats an AWSError the the SQSError type.
|
|
21
|
+
* @param err The error object that was received.
|
|
22
|
+
* @param message The message that the error occurred on.
|
|
23
|
+
*/
|
|
24
|
+
declare function toSQSError(err: AWSError, message: string): SQSError;
|
|
25
|
+
export { SQSError, TimeoutError, isConnectionError, toSQSError };
|
package/dist/errors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TimeoutError = exports.SQSError = void 0;
|
|
3
|
+
exports.toSQSError = exports.isConnectionError = exports.TimeoutError = exports.SQSError = void 0;
|
|
4
4
|
class SQSError extends Error {
|
|
5
5
|
constructor(message) {
|
|
6
6
|
super(message);
|
|
@@ -16,3 +16,34 @@ class TimeoutError extends Error {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
exports.TimeoutError = TimeoutError;
|
|
19
|
+
/**
|
|
20
|
+
* Checks if the error provided should be treated as a connection error.
|
|
21
|
+
* @param err The error that was received.
|
|
22
|
+
*/
|
|
23
|
+
function isConnectionError(err) {
|
|
24
|
+
if (err instanceof SQSError) {
|
|
25
|
+
return (err.statusCode === 403 ||
|
|
26
|
+
err.code === 'CredentialsError' ||
|
|
27
|
+
err.code === 'UnknownEndpoint' ||
|
|
28
|
+
err.code === 'AWS.SimpleQueueService.NonExistentQueue');
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
exports.isConnectionError = isConnectionError;
|
|
33
|
+
/**
|
|
34
|
+
* Formats an AWSError the the SQSError type.
|
|
35
|
+
* @param err The error object that was received.
|
|
36
|
+
* @param message The message that the error occurred on.
|
|
37
|
+
*/
|
|
38
|
+
function toSQSError(err, message) {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
const sqsError = new SQSError(message);
|
|
41
|
+
sqsError.code = err.name;
|
|
42
|
+
sqsError.statusCode = (_a = err.$metadata) === null || _a === void 0 ? void 0 : _a.httpStatusCode;
|
|
43
|
+
sqsError.retryable = (_b = err.$retryable) === null || _b === void 0 ? void 0 : _b.throttling;
|
|
44
|
+
sqsError.service = err.$service;
|
|
45
|
+
sqsError.fault = err.$fault;
|
|
46
|
+
sqsError.time = new Date();
|
|
47
|
+
return sqsError;
|
|
48
|
+
}
|
|
49
|
+
exports.toSQSError = toSQSError;
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { Consumer
|
|
1
|
+
export { Consumer } from './consumer';
|
|
2
|
+
export * from './types';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
17
|
exports.Consumer = void 0;
|
|
4
18
|
var consumer_1 = require("./consumer");
|
|
5
19
|
Object.defineProperty(exports, "Consumer", { enumerable: true, get: function () { return consumer_1.Consumer; } });
|
|
20
|
+
__exportStar(require("./types"), exports);
|