sqs-producer
Version:
Enqueues messages onto a given SQS queue
131 lines (115 loc) • 4.14 kB
text/typescript
import {
SQSClient,
type SendMessageBatchResultEntry,
SendMessageBatchCommand,
GetQueueAttributesCommand,
} from "@aws-sdk/client-sqs";
import type { Message, ProducerOptions } from "./types.js";
import { toEntry } from "./format.js";
import { FailedMessagesError } from "./errors.js";
const requiredOptions = ["queueUrl"];
/**
* [Usage](https://bbc.github.io/sqs-producer/index.html#usage)
*/
export class Producer {
static create: (options: ProducerOptions) => Producer;
queueUrl: string;
batchSize: number;
sqs: SQSClient;
region?: string;
constructor(options: ProducerOptions) {
this.validate(options);
this.queueUrl = options.queueUrl;
this.batchSize = options.batchSize || 10;
this.sqs =
options.sqs ||
new SQSClient({
...options,
useQueueUrlAsEndpoint: options.useQueueUrlAsEndpoint ?? true,
region: options.region || process.env.AWS_REGION || "eu-west-1",
});
}
/**
* Returns the number of messages in the queue.
* @returns A promise that resolves to the number of messages in the queue.
*/
async queueSize(): Promise<number> {
const command = new GetQueueAttributesCommand({
QueueUrl: this.queueUrl,
AttributeNames: ["ApproximateNumberOfMessages"],
});
const result = await this.sqs.send(command);
return Number(result && result.Attributes && result.Attributes.ApproximateNumberOfMessages);
}
/**
* Send a message to the queue.
* @param messages - A single message or an array of messages.
* @returns A promise that resolves to the result of the send operation.
*/
async send(
messages: string | Message | (string | Message)[],
): Promise<SendMessageBatchResultEntry[]> {
const failedMessages = [];
const successfulMessages = [];
const startIndex = 0;
const messagesArr = !Array.isArray(messages) ? [messages] : messages;
return this.sendBatch(failedMessages, successfulMessages, messagesArr, startIndex);
}
/**
* Validate the producer options.
* @param options - The producer options to validate.
* @throws Error if any required options are missing or invalid.
*/
private validate(options: ProducerOptions): void {
for (const option of requiredOptions) {
if (!options[option]) {
throw new Error(`Missing SQS producer option [${option}].`);
}
}
if (options.batchSize > 10 || options.batchSize < 1) {
throw new Error("SQS batchSize option must be between 1 and 10.");
}
}
/**
* Send a batch of messages to the queue.
* @param failedMessages - An array of failed message IDs.
* @param successfulMessages - An array of successful message results.
* @param messages - An array of messages to send.
* @param startIndex - The index of the first message in the batch.
* @returns A promise that resolves to the result of the send operation.
* @throws FailedMessagesError
*/
private async sendBatch(
failedMessages?: string[],
successfulMessages?: SendMessageBatchResultEntry[],
messages?: (string | Message)[],
startIndex?: number,
): Promise<SendMessageBatchResultEntry[]> {
const endIndex = startIndex + this.batchSize;
const batch = messages.slice(startIndex, endIndex);
const params = {
QueueUrl: this.queueUrl,
Entries: batch.map(toEntry),
};
const command = new SendMessageBatchCommand(params);
const result = await this.sqs.send(command);
const failedMessagesBatch = failedMessages.concat(
result?.Failed?.map((entry) => entry.Id) || [],
);
const successfulMessagesBatch = successfulMessages.concat(result?.Successful || []);
if (endIndex < messages.length) {
return this.sendBatch(failedMessagesBatch, successfulMessagesBatch, messages, endIndex);
}
if (failedMessagesBatch.length === 0) {
return successfulMessagesBatch;
}
throw new FailedMessagesError(failedMessagesBatch);
}
}
/**
* Creates a new producer.
* @param options - The producer options.
*/
Producer.create = (options: ProducerOptions): Producer => {
return new Producer(options);
};