@mbc-cqrs-serverless/core
Version:
CQRS and event base core
392 lines • 12.8 kB
JavaScript
"use strict";
/**
* AWS Error Factory
*
* Provides standardized factory functions for creating AWS SDK errors
* used in integration tests. This eliminates code duplication across
* test files and ensures consistent error structure.
*
* Usage:
* import { createDynamoDBError, createS3Error } from './utilities/aws-error-factory'
*
* const error = createDynamoDBError('ProvisionedThroughputExceededException', {
* message: 'Rate exceeded',
* httpStatusCode: 400,
* throttling: true,
* })
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDynamoDBError = createDynamoDBError;
exports.createS3Error = createS3Error;
exports.createSQSError = createSQSError;
exports.createSNSError = createSNSError;
exports.createSFNError = createSFNError;
exports.createSESError = createSESError;
exports.createNetworkError = createNetworkError;
exports.createTimeoutError = createTimeoutError;
exports.createHttpStatusError = createHttpStatusError;
exports.isRetriableAWSError = isRetriableAWSError;
exports.isThrottlingError = isThrottlingError;
exports.isNetworkError = isNetworkError;
/**
* Creates a base AWS error with the standard structure
*/
function createBaseAWSError(name, options = {}) {
const { message = name, httpStatusCode = 400, requestId = `test-request-${Date.now()}`, throttling, fault = httpStatusCode >= 500 ? 'server' : 'client', retryAfterSeconds, code, } = options;
const error = new Error(message);
error.name = name;
error.$metadata = {
httpStatusCode,
requestId,
};
error.$fault = fault;
if (throttling !== undefined) {
error.$retryable = { throttling };
}
if (retryAfterSeconds !== undefined) {
error.retryAfterSeconds = retryAfterSeconds;
}
if (code !== undefined) {
error.code = code;
}
return error;
}
/**
* Creates a DynamoDB-specific error
*/
function createDynamoDBError(name, options = {}) {
const defaultOptions = {};
// Set default properties based on error type
switch (name) {
case 'ProvisionedThroughputExceededException':
case 'ThrottlingException':
case 'RequestLimitExceeded':
defaultOptions.throttling = true;
defaultOptions.httpStatusCode = 400;
break;
case 'InternalServerError':
defaultOptions.httpStatusCode = 500;
defaultOptions.fault = 'server';
break;
case 'ServiceUnavailable':
defaultOptions.httpStatusCode = 503;
defaultOptions.fault = 'server';
break;
case 'ValidationException':
case 'ConditionalCheckFailedException':
case 'ResourceNotFoundException':
defaultOptions.httpStatusCode = 400;
defaultOptions.fault = 'client';
break;
case 'TransactionCanceledException':
case 'TransactionConflictException':
defaultOptions.httpStatusCode = 400;
break;
case 'ItemCollectionSizeLimitExceededException':
defaultOptions.httpStatusCode = 400;
break;
case 'AccessDeniedException':
defaultOptions.httpStatusCode = 403;
break;
}
const error = createBaseAWSError(name, { ...defaultOptions, ...options });
error.$service = 'DynamoDB';
return error;
}
/**
* Creates an S3-specific error
*/
function createS3Error(name, options = {}) {
const defaultOptions = {};
switch (name) {
case 'NoSuchKey':
case 'NoSuchBucket':
case 'NoSuchUpload':
defaultOptions.httpStatusCode = 404;
break;
case 'AccessDenied':
case 'InvalidAccessKeyId':
case 'SignatureDoesNotMatch':
defaultOptions.httpStatusCode = 403;
break;
case 'EntityTooLarge':
defaultOptions.httpStatusCode = 400;
break;
case 'SlowDown':
defaultOptions.httpStatusCode = 503;
defaultOptions.throttling = true;
break;
case 'InternalError':
defaultOptions.httpStatusCode = 500;
defaultOptions.fault = 'server';
break;
case 'ServiceUnavailable':
defaultOptions.httpStatusCode = 503;
defaultOptions.fault = 'server';
break;
case 'BucketNotEmpty':
case 'BucketAlreadyExists':
case 'BucketAlreadyOwnedByYou':
defaultOptions.httpStatusCode = 409;
break;
}
const error = createBaseAWSError(name, { ...defaultOptions, ...options });
error.$service = 'S3';
return error;
}
/**
* Creates an SQS-specific error
*/
function createSQSError(name, options = {}) {
const defaultOptions = {};
switch (name) {
case 'QueueDoesNotExist':
defaultOptions.httpStatusCode = 400;
break;
case 'ThrottlingException':
case 'OverLimit':
defaultOptions.throttling = true;
defaultOptions.httpStatusCode = 400;
break;
case 'AccessDeniedException':
defaultOptions.httpStatusCode = 403;
break;
case 'InvalidMessageContents':
case 'BatchEntryIdsNotDistinct':
case 'BatchRequestTooLong':
case 'EmptyBatchRequest':
case 'InvalidBatchEntryId':
case 'TooManyEntriesInBatchRequest':
defaultOptions.httpStatusCode = 400;
break;
case 'MessageNotInflight':
case 'ReceiptHandleIsInvalid':
defaultOptions.httpStatusCode = 400;
break;
}
const error = createBaseAWSError(name, { ...defaultOptions, ...options });
error.$service = 'SQS';
return error;
}
/**
* Creates an SNS-specific error
*/
function createSNSError(name, options = {}) {
const defaultOptions = {};
switch (name) {
case 'NotFoundException':
defaultOptions.httpStatusCode = 404;
break;
case 'TopicLimitExceededException':
case 'SubscriptionLimitExceededException':
case 'ThrottledException':
defaultOptions.throttling = true;
defaultOptions.httpStatusCode = 400;
break;
case 'AuthorizationErrorException':
defaultOptions.httpStatusCode = 403;
break;
case 'InternalErrorException':
defaultOptions.httpStatusCode = 500;
defaultOptions.fault = 'server';
break;
case 'InvalidParameterException':
case 'InvalidParameterValueException':
defaultOptions.httpStatusCode = 400;
break;
}
const error = createBaseAWSError(name, { ...defaultOptions, ...options });
error.$service = 'SNS';
return error;
}
/**
* Creates a Step Functions-specific error
*/
function createSFNError(name, options = {}) {
const defaultOptions = {};
switch (name) {
case 'ExecutionDoesNotExist':
case 'StateMachineDoesNotExist':
case 'TaskDoesNotExist':
case 'ActivityDoesNotExist':
case 'ResourceNotFound':
defaultOptions.httpStatusCode = 400;
break;
case 'ExecutionAlreadyExists':
defaultOptions.httpStatusCode = 400;
break;
case 'ThrottlingException':
case 'ExecutionLimitExceeded':
case 'StateMachineLimitExceeded':
case 'ActivityLimitExceeded':
case 'ServiceQuotaExceededException':
defaultOptions.throttling = true;
defaultOptions.httpStatusCode = 400;
break;
case 'InvalidArn':
case 'InvalidDefinition':
case 'InvalidExecutionInput':
case 'InvalidName':
case 'InvalidToken':
defaultOptions.httpStatusCode = 400;
break;
case 'TaskTimedOut':
defaultOptions.httpStatusCode = 400;
break;
}
const error = createBaseAWSError(name, { ...defaultOptions, ...options });
error.$service = 'StepFunctions';
return error;
}
/**
* Creates an SES-specific error
*/
function createSESError(name, options = {}) {
const defaultOptions = {};
switch (name) {
case 'MessageRejected':
defaultOptions.httpStatusCode = 400;
break;
case 'MailFromDomainNotVerifiedException':
defaultOptions.httpStatusCode = 400;
break;
case 'ConfigurationSetDoesNotExistException':
case 'NotFoundException':
defaultOptions.httpStatusCode = 404;
break;
case 'AccountSendingPausedException':
defaultOptions.httpStatusCode = 400;
break;
case 'LimitExceededException':
case 'TooManyRequestsException':
defaultOptions.throttling = true;
defaultOptions.httpStatusCode = 429;
break;
case 'BadRequestException':
defaultOptions.httpStatusCode = 400;
break;
}
const error = createBaseAWSError(name, { ...defaultOptions, ...options });
error.$service = 'SES';
return error;
}
/**
* Creates a network-level error
*/
function createNetworkError(code, message) {
const defaultMessages = {
ECONNRESET: 'read ECONNRESET',
ECONNREFUSED: 'connect ECONNREFUSED',
ETIMEDOUT: 'connect ETIMEDOUT',
ENETUNREACH: 'network unreachable',
ENOTFOUND: 'getaddrinfo ENOTFOUND',
EPIPE: 'write EPIPE',
EAI_AGAIN: 'getaddrinfo EAI_AGAIN',
};
const error = new Error(message || defaultMessages[code]);
error.code = code;
return error;
}
/**
* Creates a timeout error
*/
function createTimeoutError(message = 'Request timeout') {
const error = new Error(message);
error.name = 'TimeoutError';
error.code = 'ETIMEDOUT';
return error;
}
// ============================================================================
// Generic HTTP Status Errors
// ============================================================================
/**
* Creates an error for a specific HTTP status code
*/
function createHttpStatusError(statusCode, options = {}) {
const statusMessages = {
400: 'Bad Request',
401: 'Unauthorized',
403: 'Forbidden',
404: 'Not Found',
408: 'Request Timeout',
429: 'Too Many Requests',
500: 'Internal Server Error',
502: 'Bad Gateway',
503: 'Service Unavailable',
504: 'Gateway Timeout',
};
const name = options.name || statusMessages[statusCode] || 'UnknownError';
const message = options.message || statusMessages[statusCode] || 'Error';
return createBaseAWSError(name, {
message,
httpStatusCode: statusCode,
requestId: options.requestId,
fault: statusCode >= 500 ? 'server' : 'client',
throttling: statusCode === 429,
});
}
// ============================================================================
// Utility Functions
// ============================================================================
/**
* Checks if an error is retriable based on AWS SDK v3 patterns
*/
function isRetriableAWSError(error) {
// Check $retryable flag
if (error.$retryable) {
return true;
}
// Check HTTP status codes
const statusCode = error.$metadata?.httpStatusCode;
if (statusCode) {
// 5xx errors are retriable
if (statusCode >= 500 && statusCode < 600) {
return true;
}
// 429 Too Many Requests
if (statusCode === 429) {
return true;
}
}
// Check error names
const retriableErrorNames = [
'ProvisionedThroughputExceededException',
'ThrottlingException',
'InternalServerError',
'ServiceUnavailable',
'RequestLimitExceeded',
'SlowDown',
'TooManyRequestsException',
];
return retriableErrorNames.includes(error.name);
}
/**
* Checks if an error is a throttling error
*/
function isThrottlingError(error) {
return error.$retryable?.throttling === true;
}
/**
* Checks if an error is a network error
*/
function isNetworkError(error) {
const networkErrorCodes = [
'ECONNRESET',
'ECONNREFUSED',
'ETIMEDOUT',
'ENETUNREACH',
'ENOTFOUND',
'EPIPE',
'EAI_AGAIN',
];
if (error.code &&
networkErrorCodes.includes(error.code)) {
return true;
}
const message = error.message.toLowerCase();
return (message.includes('socket hang up') ||
message.includes('network error') ||
message.includes('connection reset') ||
message.includes('connection refused'));
}
//# sourceMappingURL=aws-error-factory.js.map