Skip to content

Commit 45f0916

Browse files
feat: extend authentication error to handle more codes (#489)
* feat: extend authentication error to handle more codes * chore simplifying the conditional
1 parent 494ed94 commit 45f0916

File tree

3 files changed

+182
-7
lines changed

3 files changed

+182
-7
lines changed

src/consumer.ts

+1
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ export class Consumer extends TypedEventEmitter {
251251
this.emitError(err);
252252
if (isConnectionError(err)) {
253253
logger.debug("authentication_error", {
254+
code: err.code || "Unknown",
254255
detail:
255256
"There was an authentication error. Pausing before retrying.",
256257
});

src/errors.ts

+16-7
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,28 @@ class StandardError extends Error {
3636
}
3737
}
3838

39+
/**
40+
* List of SQS error codes that are considered connection errors.
41+
*/
42+
const CONNECTION_ERRORS = [
43+
"CredentialsError",
44+
"UnknownEndpoint",
45+
"AWS.SimpleQueueService.NonExistentQueue",
46+
"CredentialsProviderError",
47+
"InvalidAddress",
48+
"InvalidSecurity",
49+
"QueueDoesNotExist",
50+
"RequestThrottled",
51+
"OverLimit",
52+
];
53+
3954
/**
4055
* Checks if the error provided should be treated as a connection error.
4156
* @param err The error that was received.
4257
*/
4358
function isConnectionError(err: Error): boolean {
4459
if (err instanceof SQSError) {
45-
return (
46-
err.statusCode === 403 ||
47-
err.code === "CredentialsError" ||
48-
err.code === "UnknownEndpoint" ||
49-
err.code === "AWS.SimpleQueueService.NonExistentQueue" ||
50-
err.code === "CredentialsProviderError"
51-
);
60+
return err.statusCode === 403 || CONNECTION_ERRORS.includes(err.code);
5261
}
5362
return false;
5463
}

test/tests/consumer.test.ts

+165
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,8 @@ describe("Consumer", () => {
409409
});
410410

411411
it("waits before repolling when a credentials error occurs", async () => {
412+
const loggerDebug = sandbox.stub(logger, "debug");
413+
412414
const credentialsErr = {
413415
name: "CredentialsError",
414416
message: "Missing credentials in config",
@@ -424,9 +426,16 @@ describe("Consumer", () => {
424426
sandbox.assert.calledTwice(errorListener);
425427
sandbox.assert.calledWithMatch(sqs.send.firstCall, mockReceiveMessage);
426428
sandbox.assert.calledWithMatch(sqs.send.secondCall, mockReceiveMessage);
429+
430+
sandbox.assert.calledWith(loggerDebug, "authentication_error", {
431+
code: "CredentialsError",
432+
detail: "There was an authentication error. Pausing before retrying.",
433+
});
427434
});
428435

429436
it("waits before repolling when a 403 error occurs", async () => {
437+
const loggerDebug = sandbox.stub(logger, "debug");
438+
430439
const invalidSignatureErr = {
431440
$metadata: {
432441
httpStatusCode: 403,
@@ -444,9 +453,16 @@ describe("Consumer", () => {
444453
sandbox.assert.calledTwice(errorListener);
445454
sandbox.assert.calledWithMatch(sqs.send.firstCall, mockReceiveMessage);
446455
sandbox.assert.calledWithMatch(sqs.send.secondCall, mockReceiveMessage);
456+
457+
sandbox.assert.calledWith(loggerDebug, "authentication_error", {
458+
code: "Unknown",
459+
detail: "There was an authentication error. Pausing before retrying.",
460+
});
447461
});
448462

449463
it("waits before repolling when a UnknownEndpoint error occurs", async () => {
464+
const loggerDebug = sandbox.stub(logger, "debug");
465+
450466
const unknownEndpointErr = {
451467
name: "UnknownEndpoint",
452468
message:
@@ -464,9 +480,16 @@ describe("Consumer", () => {
464480
sandbox.assert.calledTwice(sqs.send);
465481
sandbox.assert.calledWithMatch(sqs.send.firstCall, mockReceiveMessage);
466482
sandbox.assert.calledWithMatch(sqs.send.secondCall, mockReceiveMessage);
483+
484+
sandbox.assert.calledWith(loggerDebug, "authentication_error", {
485+
code: "UnknownEndpoint",
486+
detail: "There was an authentication error. Pausing before retrying.",
487+
});
467488
});
468489

469490
it("waits before repolling when a NonExistentQueue error occurs", async () => {
491+
const loggerDebug = sandbox.stub(logger, "debug");
492+
470493
const nonExistentQueueErr = {
471494
name: "AWS.SimpleQueueService.NonExistentQueue",
472495
message: "The specified queue does not exist for this wsdl version.",
@@ -483,9 +506,16 @@ describe("Consumer", () => {
483506
sandbox.assert.calledTwice(sqs.send);
484507
sandbox.assert.calledWithMatch(sqs.send.firstCall, mockReceiveMessage);
485508
sandbox.assert.calledWithMatch(sqs.send.secondCall, mockReceiveMessage);
509+
510+
sandbox.assert.calledWith(loggerDebug, "authentication_error", {
511+
code: "AWS.SimpleQueueService.NonExistentQueue",
512+
detail: "There was an authentication error. Pausing before retrying.",
513+
});
486514
});
487515

488516
it("waits before repolling when a CredentialsProviderError error occurs", async () => {
517+
const loggerDebug = sandbox.stub(logger, "debug");
518+
489519
const credentialsProviderErr = {
490520
name: "CredentialsProviderError",
491521
message: "Could not load credentials from any providers.",
@@ -502,6 +532,141 @@ describe("Consumer", () => {
502532
sandbox.assert.calledTwice(sqs.send);
503533
sandbox.assert.calledWithMatch(sqs.send.firstCall, mockReceiveMessage);
504534
sandbox.assert.calledWithMatch(sqs.send.secondCall, mockReceiveMessage);
535+
536+
sandbox.assert.calledWith(loggerDebug, "authentication_error", {
537+
code: "CredentialsProviderError",
538+
detail: "There was an authentication error. Pausing before retrying.",
539+
});
540+
});
541+
542+
it("waits before repolling when a InvalidAddress error occurs", async () => {
543+
const loggerDebug = sandbox.stub(logger, "debug");
544+
545+
const credentialsProviderErr = {
546+
name: "InvalidAddress",
547+
message: "The address some-queue-url is not valid for this endpoint.",
548+
};
549+
sqs.send.withArgs(mockReceiveMessage).rejects(credentialsProviderErr);
550+
const errorListener = sandbox.stub();
551+
consumer.on("error", errorListener);
552+
553+
consumer.start();
554+
await clock.tickAsync(AUTHENTICATION_ERROR_TIMEOUT);
555+
consumer.stop();
556+
557+
sandbox.assert.calledTwice(errorListener);
558+
sandbox.assert.calledTwice(sqs.send);
559+
sandbox.assert.calledWithMatch(sqs.send.firstCall, mockReceiveMessage);
560+
sandbox.assert.calledWithMatch(sqs.send.secondCall, mockReceiveMessage);
561+
562+
sandbox.assert.calledWith(loggerDebug, "authentication_error", {
563+
code: "InvalidAddress",
564+
detail: "There was an authentication error. Pausing before retrying.",
565+
});
566+
});
567+
568+
it("waits before repolling when a InvalidSecurity error occurs", async () => {
569+
const loggerDebug = sandbox.stub(logger, "debug");
570+
571+
const credentialsProviderErr = {
572+
name: "InvalidSecurity",
573+
message: "The queue is not is not HTTPS and SigV4.",
574+
};
575+
sqs.send.withArgs(mockReceiveMessage).rejects(credentialsProviderErr);
576+
const errorListener = sandbox.stub();
577+
consumer.on("error", errorListener);
578+
579+
consumer.start();
580+
await clock.tickAsync(AUTHENTICATION_ERROR_TIMEOUT);
581+
consumer.stop();
582+
583+
sandbox.assert.calledTwice(errorListener);
584+
sandbox.assert.calledTwice(sqs.send);
585+
sandbox.assert.calledWithMatch(sqs.send.firstCall, mockReceiveMessage);
586+
sandbox.assert.calledWithMatch(sqs.send.secondCall, mockReceiveMessage);
587+
588+
sandbox.assert.calledWith(loggerDebug, "authentication_error", {
589+
code: "InvalidSecurity",
590+
detail: "There was an authentication error. Pausing before retrying.",
591+
});
592+
});
593+
594+
it("waits before repolling when a QueueDoesNotExist error occurs", async () => {
595+
const loggerDebug = sandbox.stub(logger, "debug");
596+
597+
const credentialsProviderErr = {
598+
name: "QueueDoesNotExist",
599+
message: "The queue does not exist.",
600+
};
601+
sqs.send.withArgs(mockReceiveMessage).rejects(credentialsProviderErr);
602+
const errorListener = sandbox.stub();
603+
consumer.on("error", errorListener);
604+
605+
consumer.start();
606+
await clock.tickAsync(AUTHENTICATION_ERROR_TIMEOUT);
607+
consumer.stop();
608+
609+
sandbox.assert.calledTwice(errorListener);
610+
sandbox.assert.calledTwice(sqs.send);
611+
sandbox.assert.calledWithMatch(sqs.send.firstCall, mockReceiveMessage);
612+
sandbox.assert.calledWithMatch(sqs.send.secondCall, mockReceiveMessage);
613+
614+
sandbox.assert.calledWith(loggerDebug, "authentication_error", {
615+
code: "QueueDoesNotExist",
616+
detail: "There was an authentication error. Pausing before retrying.",
617+
});
618+
});
619+
620+
it("waits before repolling when a RequestThrottled error occurs", async () => {
621+
const loggerDebug = sandbox.stub(logger, "debug");
622+
623+
const credentialsProviderErr = {
624+
name: "RequestThrottled",
625+
message: "Requests have been throttled.",
626+
};
627+
sqs.send.withArgs(mockReceiveMessage).rejects(credentialsProviderErr);
628+
const errorListener = sandbox.stub();
629+
consumer.on("error", errorListener);
630+
631+
consumer.start();
632+
await clock.tickAsync(AUTHENTICATION_ERROR_TIMEOUT);
633+
consumer.stop();
634+
635+
sandbox.assert.calledTwice(errorListener);
636+
sandbox.assert.calledTwice(sqs.send);
637+
sandbox.assert.calledWithMatch(sqs.send.firstCall, mockReceiveMessage);
638+
sandbox.assert.calledWithMatch(sqs.send.secondCall, mockReceiveMessage);
639+
640+
sandbox.assert.calledWith(loggerDebug, "authentication_error", {
641+
code: "RequestThrottled",
642+
detail: "There was an authentication error. Pausing before retrying.",
643+
});
644+
});
645+
646+
it("waits before repolling when a RequestThrottled error occurs", async () => {
647+
const loggerDebug = sandbox.stub(logger, "debug");
648+
649+
const credentialsProviderErr = {
650+
name: "OverLimit",
651+
message: "An over limit error.",
652+
};
653+
sqs.send.withArgs(mockReceiveMessage).rejects(credentialsProviderErr);
654+
const errorListener = sandbox.stub();
655+
consumer.on("error", errorListener);
656+
657+
consumer.start();
658+
await clock.tickAsync(AUTHENTICATION_ERROR_TIMEOUT);
659+
consumer.stop();
660+
661+
sandbox.assert.calledTwice(errorListener);
662+
sandbox.assert.calledTwice(sqs.send);
663+
sandbox.assert.calledWithMatch(sqs.send.firstCall, mockReceiveMessage);
664+
sandbox.assert.calledWithMatch(sqs.send.secondCall, mockReceiveMessage);
665+
666+
sandbox.assert.calledWith(loggerDebug, "authentication_error", {
667+
code: "OverLimit",
668+
detail: "There was an authentication error. Pausing before retrying.",
669+
});
505670
});
506671

507672
it("waits before repolling when a polling timeout is set", async () => {

0 commit comments

Comments
 (0)