diff --git a/packages/amqp/lib/AbstractAmqpQueuePublisher.ts b/packages/amqp/lib/AbstractAmqpQueuePublisher.ts index 9a28b72d..e4535484 100644 --- a/packages/amqp/lib/AbstractAmqpQueuePublisher.ts +++ b/packages/amqp/lib/AbstractAmqpQueuePublisher.ts @@ -32,9 +32,14 @@ export abstract class AbstractAmqpQueuePublisher< options: AMQPPublisherOptions, ) { super(dependencies, options) + + if (!options.locatorConfig?.queueName && !options.creationConfig?.queueName) { + throw new Error('Either locatorConfig or creationConfig must provide queueName') + } + this.queueName = options.locatorConfig ? options.locatorConfig.queueName - : options.creationConfig.queueName + : options.creationConfig!.queueName } protected publishInternal(message: Buffer, options: AmqpQueueMessageOptions): void { diff --git a/packages/amqp/package.json b/packages/amqp/package.json index b97b7ecb..92dd7daf 100644 --- a/packages/amqp/package.json +++ b/packages/amqp/package.json @@ -25,7 +25,7 @@ "prepublishOnly": "npm run build:release" }, "dependencies": { - "@lokalise/node-core": "^12.0.0", + "@lokalise/node-core": "^13.0.1", "zod": "^3.23.8" }, "peerDependencies": { @@ -38,14 +38,14 @@ "@kibertoad/biome-config": "^1.2.1", "@message-queue-toolkit/core": "*", "@types/amqplib": "^0.10.5", - "@types/node": "^22.0.0", - "@vitest/coverage-v8": "^2.0.4", + "@types/node": "^22.7.5", + "@vitest/coverage-v8": "^2.1.2", "amqplib": "^0.10.4", "awilix": "^12.0.1", "awilix-manager": "^5.4.0", "del-cli": "^6.0.0", - "typescript": "^5.5.2", - "vitest": "^2.0.4" + "typescript": "^5.6.3", + "vitest": "^2.1.2" }, "homepage": "https://github.com/kibertoad/message-queue-toolkit", "repository": { diff --git a/packages/amqp/vitest.config.mts b/packages/amqp/vitest.config.mts index 253e240a..045dec5f 100644 --- a/packages/amqp/vitest.config.mts +++ b/packages/amqp/vitest.config.mts @@ -26,10 +26,10 @@ export default defineConfig({ reporter: ['text'], all: true, thresholds: { - lines: 90, + lines: 89, functions: 95, branches: 79, - statements: 90, + statements: 89, }, }, }, diff --git a/packages/core/lib/types/queueOptionsTypes.ts b/packages/core/lib/types/queueOptionsTypes.ts index c8d4ecc6..04ba85ff 100644 --- a/packages/core/lib/types/queueOptionsTypes.ts +++ b/packages/core/lib/types/queueOptionsTypes.ts @@ -38,20 +38,18 @@ export type DeletionConfig = { } type NewQueueOptions = { - locatorConfig?: never - creationConfig: CreationConfigType + creationConfig?: CreationConfigType } type ExistingQueueOptions = { - locatorConfig: QueueLocatorType - creationConfig?: never + locatorConfig?: QueueLocatorType } export type QueueOptions< CreationConfigType extends CommonCreationConfigType, QueueLocatorType extends object, > = CommonQueueOptions & - (NewQueueOptions | ExistingQueueOptions) + (NewQueueOptions & ExistingQueueOptions) export type QueuePublisherOptions< CreationConfigType extends CommonCreationConfigType, @@ -68,7 +66,8 @@ export type DeadLetterQueueOptions< > = { deletionConfig?: DeletionConfig } & DeadLetterQueueIntegrationOptions & - (NewQueueOptions | ExistingQueueOptions) + NewQueueOptions & + ExistingQueueOptions export type QueueConsumerOptions< CreationConfigType extends object, diff --git a/packages/core/package.json b/packages/core/package.json index 6a00f8b2..e52ea753 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,10 +25,10 @@ "prepublishOnly": "npm run build:release" }, "dependencies": { - "@lokalise/node-core": "^12.0.0", + "@lokalise/node-core": "^13.0.1", "@message-queue-toolkit/schemas": "^4.0.0", "fast-equals": "^5.0.1", - "json-stream-stringify": "^3.1.4", + "json-stream-stringify": "^3.1.6", "tmp": "^0.2.3", "toad-cache": "^3.7.0", "zod": "^3.23.8" @@ -36,14 +36,14 @@ "devDependencies": { "@biomejs/biome": "1.9.3", "@kibertoad/biome-config": "^1.2.1", - "@types/node": "^22.0.0", + "@types/node": "^22.7.5", "@types/tmp": "^0.2.6", - "@vitest/coverage-v8": "^2.0.4", + "@vitest/coverage-v8": "^2.1.2", "awilix": "^12.0.1", "awilix-manager": "^5.4.0", "del-cli": "^6.0.0", - "typescript": "^5.5.4", - "vitest": "^2.0.4" + "typescript": "^5.6.3", + "vitest": "^2.1.2" }, "homepage": "https://github.com/kibertoad/message-queue-toolkit", "repository": { diff --git a/packages/schemas/package.json b/packages/schemas/package.json index dbd7bda9..0ba01de6 100644 --- a/packages/schemas/package.json +++ b/packages/schemas/package.json @@ -17,8 +17,9 @@ "build:release": "del-cli dist && npm run lint && tsc --project tsconfig.release.json", "lint": "biome check . && tsc --project tsconfig.json --noEmit", "lint:fix": "biome check --write .", - "test:coverage": "", - "test:ci": "", + "test": "vitest", + "test:coverage": "npm test -- --coverage", + "test:ci": "npm test", "docker:start:dev": "", "docker:stop:dev": "", "prepublishOnly": "npm run build:release" @@ -29,11 +30,11 @@ "devDependencies": { "@biomejs/biome": "1.9.3", "@kibertoad/biome-config": "^1.2.1", - "@types/node": "^22.0.0", - "@vitest/coverage-v8": "^2.0.4", + "@types/node": "^22.7.5", + "@vitest/coverage-v8": "^2.1.2", "del-cli": "^6.0.0", - "typescript": "^5.5.3", - "vitest": "^2.0.4" + "typescript": "^5.6.3", + "vitest": "^2.1.2" }, "homepage": "https://github.com/kibertoad/message-queue-toolkit", "repository": { diff --git a/packages/schemas/vitest.config.mts b/packages/schemas/vitest.config.mts new file mode 100644 index 00000000..560b8f3c --- /dev/null +++ b/packages/schemas/vitest.config.mts @@ -0,0 +1,35 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + globals: true, + poolOptions: { + threads: { + singleThread: true, + }, + }, + pool: 'threads', + watch: false, + environment: 'node', + reporters: ['default'], + coverage: { + provider: 'v8', + include: ['lib/**/*.ts'], + exclude: [ + 'lib/**/*.spec.ts', + 'lib/**/*.test.ts', + 'test/**/*.*', + 'lib/types/**/*.*', + 'lib/sns/fakes', + ], + reporter: ['text'], + all: true, + thresholds: { + lines: 8, + functions: 71, + branches: 70, + statements: 8, + }, + }, + }, +}) diff --git a/packages/sns/index.ts b/packages/sns/index.ts index 4281fecb..ba79d820 100644 --- a/packages/sns/index.ts +++ b/packages/sns/index.ts @@ -1,7 +1,7 @@ export type { SNSTopicAWSConfig, SNSTopicConfig, - SNSQueueLocatorType, + SNSTopicLocatorType, SNSCreationConfig, SNSDependencies, } from './lib/sns/AbstractSnsService' diff --git a/packages/sns/lib/sns/AbstractSnsPublisher.ts b/packages/sns/lib/sns/AbstractSnsPublisher.ts index 5088bf89..d2d0d5e4 100644 --- a/packages/sns/lib/sns/AbstractSnsPublisher.ts +++ b/packages/sns/lib/sns/AbstractSnsPublisher.ts @@ -16,7 +16,7 @@ import { resolveOutgoingMessageAttributes } from '@message-queue-toolkit/sqs' import { calculateOutgoingMessageSize } from '../utils/snsUtils' -import type { SNSCreationConfig, SNSDependencies, SNSQueueLocatorType } from './AbstractSnsService' +import type { SNSCreationConfig, SNSDependencies, SNSTopicLocatorType } from './AbstractSnsService' import { AbstractSnsService } from './AbstractSnsService' export type SNSMessageOptions = { @@ -26,7 +26,7 @@ export type SNSMessageOptions = { export type SNSPublisherOptions = QueuePublisherOptions< SNSCreationConfig, - SNSQueueLocatorType, + SNSTopicLocatorType, MessagePayloadType > diff --git a/packages/sns/lib/sns/AbstractSnsService.ts b/packages/sns/lib/sns/AbstractSnsService.ts index bae12046..cbae6ae1 100644 --- a/packages/sns/lib/sns/AbstractSnsService.ts +++ b/packages/sns/lib/sns/AbstractSnsService.ts @@ -34,15 +34,16 @@ export type ExtraSNSCreationParams = { } export type SNSCreationConfig = { - topic: SNSTopicAWSConfig + topic?: SNSTopicAWSConfig updateAttributesIfExists?: boolean } & ExtraSNSCreationParams -export type SNSQueueLocatorType = { - topicArn: string +export type SNSTopicLocatorType = { + topicArn?: string + topicName?: string } -export type SNSOptions = QueueOptions +export type SNSOptions = QueueOptions export abstract class AbstractSnsService< MessagePayloadType extends object, @@ -54,7 +55,7 @@ export abstract class AbstractSnsService< MessageEnvelopeType, DependenciesType, SNSCreationConfig, - SNSQueueLocatorType, + SNSTopicLocatorType, SNSOptionsType > { protected readonly snsClient: SNSClient diff --git a/packages/sns/lib/sns/AbstractSnsSqsConsumer.ts b/packages/sns/lib/sns/AbstractSnsSqsConsumer.ts index 6e3fda12..69c16c12 100644 --- a/packages/sns/lib/sns/AbstractSnsSqsConsumer.ts +++ b/packages/sns/lib/sns/AbstractSnsSqsConsumer.ts @@ -12,15 +12,15 @@ import { deleteSnsSqs, initSnsSqs } from '../utils/snsInitter' import { readSnsMessage } from '../utils/snsMessageReader' import type { SNSSubscriptionOptions } from '../utils/snsSubscriber' -import type { SNSCreationConfig, SNSOptions, SNSQueueLocatorType } from './AbstractSnsService' +import type { SNSCreationConfig, SNSOptions, SNSTopicLocatorType } from './AbstractSnsService' export type SNSSQSConsumerDependencies = SQSConsumerDependencies & { snsClient: SNSClient } export type SNSSQSCreationConfig = SQSCreationConfig & SNSCreationConfig -export type SNSSQSQueueLocatorType = SQSQueueLocatorType & - SNSQueueLocatorType & { +export type SNSSQSQueueLocatorType = Partial & + SNSTopicLocatorType & { subscriptionArn?: string } @@ -85,6 +85,8 @@ export abstract class AbstractSnsSqsConsumer< this.creationConfig.queue, this.creationConfig.topic, this.subscriptionConfig, + undefined, + this.locatorConfig, ) } else if (this.deletionConfig && this.creationConfig) { await deleteSqs(this.sqsClient, this.deletionConfig, this.creationConfig) diff --git a/packages/sns/lib/sns/SnsPublisherManager.ts b/packages/sns/lib/sns/SnsPublisherManager.ts index b0313b4e..572cc4fb 100644 --- a/packages/sns/lib/sns/SnsPublisherManager.ts +++ b/packages/sns/lib/sns/SnsPublisherManager.ts @@ -15,7 +15,7 @@ import type { SNSMessageOptions, SNSPublisherOptions, } from './AbstractSnsPublisher' -import type { SNSCreationConfig, SNSDependencies, SNSQueueLocatorType } from './AbstractSnsService' +import type { SNSCreationConfig, SNSDependencies, SNSTopicLocatorType } from './AbstractSnsService' import type { SnsPublisherFactory } from './CommonSnsPublisherFactory' import { CommonSnsPublisherFactory } from './CommonSnsPublisherFactory' @@ -53,7 +53,7 @@ export class SnsPublisherManager< AbstractSnsPublisher>, SNSDependencies, SNSCreationConfig, - SNSQueueLocatorType, + SNSTopicLocatorType, SnsMessageSchemaType, Omit< SNSPublisherOptions>, diff --git a/packages/sns/lib/types/TopicTypes.ts b/packages/sns/lib/types/TopicTypes.ts new file mode 100644 index 00000000..65761020 --- /dev/null +++ b/packages/sns/lib/types/TopicTypes.ts @@ -0,0 +1,15 @@ +import type { CreateTopicCommandInput } from '@aws-sdk/client-sns' +import type { SNSTopicLocatorType } from '../sns/AbstractSnsService' + +export type TopicResolutionOptions = CreateTopicCommandInput | SNSTopicLocatorType + +export function isCreateTopicCommand(value: unknown): value is CreateTopicCommandInput { + return !!value && !!(value as CreateTopicCommandInput).Name +} + +export function isSNSTopicLocatorType(value: unknown): value is SNSTopicLocatorType { + return ( + !!value && + (!!(value as SNSTopicLocatorType).topicArn || !!(value as SNSTopicLocatorType).topicName) + ) +} diff --git a/packages/sns/lib/utils/snsInitter.ts b/packages/sns/lib/utils/snsInitter.ts index 81fe7e62..0c61edcb 100644 --- a/packages/sns/lib/utils/snsInitter.ts +++ b/packages/sns/lib/utils/snsInitter.ts @@ -2,16 +2,25 @@ import type { CreateTopicCommandInput, SNSClient } from '@aws-sdk/client-sns' import type { CreateQueueCommandInput, SQSClient } from '@aws-sdk/client-sqs' import type { DeletionConfig, ExtraParams } from '@message-queue-toolkit/core' import { isProduction } from '@message-queue-toolkit/core' -import type { SQSCreationConfig } from '@message-queue-toolkit/sqs' +import type { SQSCreationConfig, SQSQueueLocatorType } from '@message-queue-toolkit/sqs' import { deleteQueue, getQueueAttributes } from '@message-queue-toolkit/sqs' -import type { SNSCreationConfig, SNSQueueLocatorType } from '../sns/AbstractSnsService' +import type { SNSCreationConfig, SNSTopicLocatorType } from '../sns/AbstractSnsService' import type { SNSSQSQueueLocatorType } from '../sns/AbstractSnsSqsConsumer' +import type { Either } from '@lokalise/node-core' +import { type TopicResolutionOptions, isCreateTopicCommand } from '../types/TopicTypes' import type { SNSSubscriptionOptions } from './snsSubscriber' import { subscribeToTopic } from './snsSubscriber' -import { assertTopic, deleteSubscription, deleteTopic, getTopicAttributes } from './snsUtils' - +import { + assertTopic, + deleteSubscription, + deleteTopic, + getTopicArnByName, + getTopicAttributes, +} from './snsUtils' + +// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: export async function initSnsSqs( sqsClient: SQSClient, snsClient: SNSClient, @@ -19,11 +28,11 @@ export async function initSnsSqs( creationConfig?: SNSCreationConfig & SQSCreationConfig, subscriptionConfig?: SNSSubscriptionOptions, extraParams?: ExtraParams, -) { +): Promise<{ subscriptionArn: string; topicArn: string; queueUrl: string; queueName: string }> { if (!locatorConfig?.subscriptionArn) { - if (!creationConfig?.topic) { + if (!creationConfig?.topic && !locatorConfig?.topicArn && !locatorConfig?.topicName) { throw new Error( - 'If locatorConfig.subscriptionArn is not specified, creationConfig.topic parameter is mandatory, as there will be an attempt to create the missing topic', + 'If locatorConfig.subscriptionArn is not specified, creationConfig.topic is mandatory in order to attempt to create missing topic and subscribe to it OR locatorConfig.name or locatorConfig.topicArn parameter is mandatory, to create subscription for existing topic.', ) } if (!creationConfig?.queue) { @@ -42,11 +51,16 @@ export async function initSnsSqs( ) } + const topicResolutionOptions: TopicResolutionOptions = { + ...locatorConfig, + ...creationConfig.topic, + } + const { subscriptionArn, topicArn, queueUrl } = await subscribeToTopic( sqsClient, snsClient, creationConfig.queue, - creationConfig.topic, + topicResolutionOptions, subscriptionConfig, { updateAttributesIfExists: creationConfig.updateAttributesIfExists, @@ -68,25 +82,47 @@ export async function initSnsSqs( } } + if (!locatorConfig.queueUrl) { + throw new Error( + 'If locatorConfig.subscriptionArn is provided, you have to also provide locatorConfig.queueUrl', + ) + } + + const checkPromises: Promise>[] = [] // Check for existing resources, using the locators - const queuePromise = getQueueAttributes(sqsClient, locatorConfig) - const topicPromise = getTopicAttributes(snsClient, locatorConfig.topicArn) + const subscriptionTopicArn = + locatorConfig.topicArn ?? (await getTopicArnByName(snsClient, locatorConfig.topicName)) + const topicPromise = getTopicAttributes(snsClient, subscriptionTopicArn) + checkPromises.push(topicPromise) - const [queueCheckResult, topicCheckResult] = await Promise.all([queuePromise, topicPromise]) + if (locatorConfig.queueUrl) { + const queuePromise = getQueueAttributes( + sqsClient, + (locatorConfig as SQSQueueLocatorType).queueUrl, + ) + checkPromises.push(queuePromise) + } - if (queueCheckResult.error === 'not_found') { + const [topicCheckResult, queueCheckResult] = await Promise.all(checkPromises) + + if (queueCheckResult?.error === 'not_found') { throw new Error(`Queue with queueUrl ${locatorConfig.queueUrl} does not exist.`) } if (topicCheckResult.error === 'not_found') { throw new Error(`Topic with topicArn ${locatorConfig.topicArn} does not exist.`) } - const splitUrl = locatorConfig.queueUrl.split('/') - const queueName = splitUrl[splitUrl.length - 1] + let queueName: string + if ((locatorConfig as SQSQueueLocatorType).queueUrl) { + const splitUrl = (locatorConfig as SQSQueueLocatorType).queueUrl.split('/') + queueName = splitUrl[splitUrl.length - 1] + } else { + queueName = creationConfig!.queue.QueueName! + } return { subscriptionArn: locatorConfig.subscriptionArn, - topicArn: locatorConfig.topicArn, + topicArn: subscriptionTopicArn, queueUrl: locatorConfig.queueUrl, queueName, } @@ -97,9 +133,10 @@ export async function deleteSnsSqs( snsClient: SNSClient, deletionConfig: DeletionConfig, queueConfiguration: CreateQueueCommandInput, - topicConfiguration: CreateTopicCommandInput, + topicConfiguration: CreateTopicCommandInput | undefined, subscriptionConfiguration: SNSSubscriptionOptions, extraParams?: ExtraParams, + topicLocator?: SNSTopicLocatorType, ) { if (!deletionConfig.deleteIfExists) { return @@ -115,7 +152,7 @@ export async function deleteSnsSqs( sqsClient, snsClient, queueConfiguration, - topicConfiguration, + topicConfiguration ?? topicLocator!, subscriptionConfiguration, extraParams, ) @@ -130,8 +167,16 @@ export async function deleteSnsSqs( queueConfiguration.QueueName!, deletionConfig.waitForConfirmation !== false, ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await deleteTopic(snsClient, topicConfiguration.Name!) + + if (topicConfiguration) { + const topicName = isCreateTopicCommand(topicConfiguration) + ? topicConfiguration.Name + : 'undefined' + if (!topicName) { + throw new Error('Failed to resolve topic name') + } + await deleteTopic(snsClient, topicName) + } await deleteSubscription(snsClient, subscriptionArn) } @@ -150,7 +195,7 @@ export async function deleteSns( ) } - if (!creationConfig.topic.Name) { + if (!creationConfig.topic?.Name) { throw new Error('topic.Name must be set for automatic deletion') } @@ -159,17 +204,19 @@ export async function deleteSns( export async function initSns( snsClient: SNSClient, - locatorConfig?: SNSQueueLocatorType, + locatorConfig?: SNSTopicLocatorType, creationConfig?: SNSCreationConfig, ) { if (locatorConfig) { - const checkResult = await getTopicAttributes(snsClient, locatorConfig.topicArn) + const topicArn = + locatorConfig.topicArn ?? (await getTopicArnByName(snsClient, locatorConfig.topicName)) + const checkResult = await getTopicAttributes(snsClient, topicArn) if (checkResult.error === 'not_found') { throw new Error(`Topic with topicArn ${locatorConfig.topicArn} does not exist.`) } return { - topicArn: locatorConfig.topicArn, + topicArn, } } @@ -179,7 +226,7 @@ export async function initSns( 'When locatorConfig for the topic is not specified, creationConfig of the topic is mandatory', ) } - const topicArn = await assertTopic(snsClient, creationConfig.topic, { + const topicArn = await assertTopic(snsClient, creationConfig.topic!, { queueUrlsWithSubscribePermissionsPrefix: creationConfig.queueUrlsWithSubscribePermissionsPrefix, allowedSourceOwner: creationConfig.allowedSourceOwner, }) diff --git a/packages/sns/lib/utils/snsSubscriber.ts b/packages/sns/lib/utils/snsSubscriber.ts index 6c3ba82c..03e2527c 100644 --- a/packages/sns/lib/utils/snsSubscriber.ts +++ b/packages/sns/lib/utils/snsSubscriber.ts @@ -1,4 +1,4 @@ -import type { CreateTopicCommandInput, SNSClient } from '@aws-sdk/client-sns' +import type { SNSClient } from '@aws-sdk/client-sns' import { SetSubscriptionAttributesCommand, SubscribeCommand } from '@aws-sdk/client-sns' import type { SubscribeCommandInput } from '@aws-sdk/client-sns/dist-types/commands/SubscribeCommand' import type { CreateQueueCommandInput, SQSClient } from '@aws-sdk/client-sqs' @@ -8,25 +8,50 @@ import { assertQueue } from '@message-queue-toolkit/sqs' import type { ExtraSNSCreationParams } from '../sns/AbstractSnsService' -import { assertTopic, findSubscriptionByTopicAndQueue } from './snsUtils' +import { + type TopicResolutionOptions, + isCreateTopicCommand, + isSNSTopicLocatorType, +} from '../types/TopicTypes' +import { assertTopic, findSubscriptionByTopicAndQueue, getTopicArnByName } from './snsUtils' export type SNSSubscriptionOptions = Omit< SubscribeCommandInput, 'TopicArn' | 'Endpoint' | 'Protocol' | 'ReturnSubscriptionArn' > & { updateAttributesIfExists: boolean } +async function resolveTopicArnToSubscribeTo( + topicConfiguration: TopicResolutionOptions, + snsClient: SNSClient, + extraParams: (ExtraSNSCreationParams & ExtraSQSCreationParams & ExtraParams) | undefined, +) { + //If topicArn is present, let's use it and return early. + if (isSNSTopicLocatorType(topicConfiguration) && topicConfiguration.topicArn) { + return topicConfiguration.topicArn + } + + //If input configuration is capable of creating a topic, let's create it and return its ARN. + if (isCreateTopicCommand(topicConfiguration)) { + return await assertTopic(snsClient, topicConfiguration, { + queueUrlsWithSubscribePermissionsPrefix: extraParams?.queueUrlsWithSubscribePermissionsPrefix, + allowedSourceOwner: extraParams?.allowedSourceOwner, + }) + } + + //Last option: let's not create a topic but resolve a ARN based on the desired topic name. + return await getTopicArnByName(snsClient, topicConfiguration.topicName) +} + export async function subscribeToTopic( sqsClient: SQSClient, snsClient: SNSClient, queueConfiguration: CreateQueueCommandInput, - topicConfiguration: CreateTopicCommandInput, + topicConfiguration: TopicResolutionOptions, subscriptionConfiguration: SNSSubscriptionOptions, extraParams?: ExtraSNSCreationParams & ExtraSQSCreationParams & ExtraParams, ) { - const topicArn = await assertTopic(snsClient, topicConfiguration, { - queueUrlsWithSubscribePermissionsPrefix: extraParams?.queueUrlsWithSubscribePermissionsPrefix, - allowedSourceOwner: extraParams?.allowedSourceOwner, - }) + const topicArn = await resolveTopicArnToSubscribeTo(topicConfiguration, snsClient, extraParams) + const { queueUrl, queueArn } = await assertQueue(sqsClient, queueConfiguration, { topicArnsWithPublishPermissionsPrefix: extraParams?.topicArnsWithPublishPermissionsPrefix, updateAttributesIfExists: extraParams?.updateAttributesIfExists, @@ -53,7 +78,9 @@ export async function subscribeToTopic( // @ts-ignore logger.error( `Error while creating subscription for queue "${queueConfiguration.QueueName}", topic "${ - topicConfiguration.Name + isCreateTopicCommand(topicConfiguration) + ? topicConfiguration.Name + : topicConfiguration.topicName }": ${(err as Error).message}`, ) diff --git a/packages/sns/lib/utils/snsUtils.ts b/packages/sns/lib/utils/snsUtils.ts index 85b038a5..925c06d6 100644 --- a/packages/sns/lib/utils/snsUtils.ts +++ b/packages/sns/lib/utils/snsUtils.ts @@ -1,4 +1,8 @@ -import type { CreateTopicCommandInput, SNSClient } from '@aws-sdk/client-sns' +import { + type CreateTopicCommandInput, + type SNSClient, + paginateListTopics, +} from '@aws-sdk/client-sns' import { CreateTopicCommand, DeleteTopicCommand, @@ -144,6 +148,23 @@ export async function findSubscriptionByTopicAndQueue( }) } +export async function getTopicArnByName(snsClient: SNSClient, topicName?: string): Promise { + if (!topicName) { + throw new Error('topicName is not provided') + } + + // Use paginator to automatically handle NextToken + const paginator = paginateListTopics({ client: snsClient }, {}) + for await (const page of paginator) { + for (const topic of page.Topics || []) { + if (topic.TopicArn?.includes(topicName)) { + return topic.TopicArn + } + } + } + throw new Error(`Failed to resolve topic by name ${topicName}`) +} + /** * Calculates the size of an outgoing SNS message. * diff --git a/packages/sns/package.json b/packages/sns/package.json index 9b792fd2..142e7d95 100644 --- a/packages/sns/package.json +++ b/packages/sns/package.json @@ -25,33 +25,33 @@ "prepublishOnly": "npm run build:release" }, "dependencies": { - "@lokalise/node-core": "^12.0.0", - "sqs-consumer": "^11.0.1", + "@lokalise/node-core": "^13.0.1", + "sqs-consumer": "^11.1.0", "zod": "^3.23.8" }, "peerDependencies": { - "@aws-sdk/client-sns": "^3.614.0", - "@aws-sdk/client-sqs": "^3.614.0", + "@aws-sdk/client-sns": "^3.632.0", + "@aws-sdk/client-sqs": "^3.632.0", "@message-queue-toolkit/core": ">=15.0.0", "@message-queue-toolkit/schemas": ">=2.0.0", "@message-queue-toolkit/sqs": "^17.0.0" }, "devDependencies": { - "@aws-sdk/client-s3": "^3.614.0", - "@aws-sdk/client-sns": "^3.614.0", - "@aws-sdk/client-sqs": "^3.614.0", + "@aws-sdk/client-s3": "^3.670.0", + "@aws-sdk/client-sns": "^3.670.0", + "@aws-sdk/client-sqs": "^3.670.0", "@message-queue-toolkit/core": "*", "@message-queue-toolkit/s3-payload-store": "*", "@message-queue-toolkit/sqs": "*", "@biomejs/biome": "1.9.3", "@kibertoad/biome-config": "^1.2.1", - "@types/node": "^22.0.0", - "@vitest/coverage-v8": "^2.0.4", + "@types/node": "^22.7.5", + "@vitest/coverage-v8": "^2.1.2", "awilix": "^12.0.1", "awilix-manager": "^5.1.0", "del-cli": "^6.0.0", - "typescript": "^5.5.3", - "vitest": "^2.0.4" + "typescript": "^5.6.3", + "vitest": "^2.1.2" }, "homepage": "https://github.com/kibertoad/message-queue-toolkit", "repository": { diff --git a/packages/sns/test/consumers/CreateLocateConfigMixConsumer.spec.ts b/packages/sns/test/consumers/CreateLocateConfigMixConsumer.spec.ts new file mode 100644 index 00000000..f42f7981 --- /dev/null +++ b/packages/sns/test/consumers/CreateLocateConfigMixConsumer.spec.ts @@ -0,0 +1,33 @@ +import type { SNSClient } from '@aws-sdk/client-sns' +import type { SQSClient } from '@aws-sdk/client-sqs' +import { deleteQueue } from '@message-queue-toolkit/sqs' +import type { AwilixContainer } from 'awilix' +import { beforeAll, beforeEach, describe, it } from 'vitest' +import { assertTopic, deleteTopic } from '../../lib/utils/snsUtils' +import { type Dependencies, registerDependencies } from '../utils/testContext' +import { CreateLocateConfigMixConsumer } from './CreateLocateConfigMixConsumer' + +describe('CreateLocateConfigMixConsumer', () => { + let diContainer: AwilixContainer + let sqsClient: SQSClient + let snsClient: SNSClient + + beforeAll(async () => { + diContainer = await registerDependencies({}, false) + sqsClient = diContainer.cradle.sqsClient + snsClient = diContainer.cradle.snsClient + }) + + beforeEach(async () => { + await deleteQueue(sqsClient, CreateLocateConfigMixConsumer.CONSUMED_QUEUE_NAME) + await deleteTopic(snsClient, CreateLocateConfigMixConsumer.SUBSCRIBED_TOPIC_NAME) + }) + + it('accepts mixed config of create and locate', async () => { + await assertTopic(snsClient, { + Name: CreateLocateConfigMixConsumer.SUBSCRIBED_TOPIC_NAME, + }) + const consumer = new CreateLocateConfigMixConsumer(diContainer.cradle) + await consumer.init() + }) +}) diff --git a/packages/sns/test/consumers/CreateLocateConfigMixConsumer.ts b/packages/sns/test/consumers/CreateLocateConfigMixConsumer.ts new file mode 100644 index 00000000..69e7a0cb --- /dev/null +++ b/packages/sns/test/consumers/CreateLocateConfigMixConsumer.ts @@ -0,0 +1,67 @@ +import { MessageHandlerConfigBuilder } from '@message-queue-toolkit/core' + +import type { SNSSQSConsumerDependencies } from '../../lib/sns/AbstractSnsSqsConsumer' +import { AbstractSnsSqsConsumer } from '../../lib/sns/AbstractSnsSqsConsumer' + +import type { ConsumerMessageSchema } from '@message-queue-toolkit/schemas' +import { TestEvents } from '../utils/testContext' +import { entityCreatedHandler } from './handlers/EntityCreatedHandler' +import { entityUpdatedHandler } from './handlers/EntityUpdatedHandler' + +export type SupportedMessages = ConsumerMessageSchema< + typeof TestEvents.created | typeof TestEvents.updated +> +type ExecutionContext = {} + +type PreHandlerOutput = {} + +export class CreateLocateConfigMixConsumer extends AbstractSnsSqsConsumer< + SupportedMessages, + ExecutionContext, + PreHandlerOutput +> { + public static readonly CONSUMED_QUEUE_NAME = 'create_locate_queue' + public static readonly SUBSCRIBED_TOPIC_NAME = 'create_locate_topic' + + constructor(dependencies: SNSSQSConsumerDependencies) { + super( + dependencies, + { + handlerSpy: true, + handlers: new MessageHandlerConfigBuilder< + SupportedMessages, + ExecutionContext, + PreHandlerOutput + >() + .addConfig(TestEvents.created, entityCreatedHandler, {}) + .addConfig(TestEvents.updated, entityUpdatedHandler, {}) + .build(), + messageTypeField: 'type', + creationConfig: { + queue: { + QueueName: CreateLocateConfigMixConsumer.CONSUMED_QUEUE_NAME, + }, + }, + locatorConfig: { + topicName: CreateLocateConfigMixConsumer.SUBSCRIBED_TOPIC_NAME, + }, + subscriptionConfig: { + updateAttributesIfExists: false, + }, + }, + { + incrementAmount: 1, + }, + ) + } + + get subscriptionProps() { + return { + topicArn: this.topicArn, + queueUrl: this.queueUrl, + queueName: this.queueName, + subscriptionArn: this.subscriptionArn, + deadLetterQueueUrl: this.deadLetterQueueUrl, + } + } +} diff --git a/packages/sns/test/consumers/SnsSqsPermissionConsumer.deadLetterQueue.spec.ts b/packages/sns/test/consumers/SnsSqsPermissionConsumer.deadLetterQueue.spec.ts index 7db03c25..dfd6efda 100644 --- a/packages/sns/test/consumers/SnsSqsPermissionConsumer.deadLetterQueue.spec.ts +++ b/packages/sns/test/consumers/SnsSqsPermissionConsumer.deadLetterQueue.spec.ts @@ -77,9 +77,7 @@ describe('SnsSqsPermissionConsumer - dead letter queue', () => { 'http://sqs.eu-west-1.localstack:4566/000000000000/deadLetterQueue', ) - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: newConsumer.subscriptionProps.queueUrl, - }) + const attributes = await getQueueAttributes(sqsClient, newConsumer.subscriptionProps.queueUrl) expect(attributes.result?.attributes).toMatchObject({ RedrivePolicy: JSON.stringify({ @@ -116,9 +114,7 @@ describe('SnsSqsPermissionConsumer - dead letter queue', () => { 'http://sqs.eu-west-1.localstack:4566/000000000000/deadLetterQueue', ) - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: newConsumer.subscriptionProps.queueUrl, - }) + const attributes = await getQueueAttributes(sqsClient, newConsumer.subscriptionProps.queueUrl) expect(attributes.result?.attributes).toMatchObject({ RedrivePolicy: JSON.stringify({ diff --git a/packages/sns/test/consumers/SnsSqsPermissionConsumer.spec.ts b/packages/sns/test/consumers/SnsSqsPermissionConsumer.spec.ts index b1a62cf5..1c4180b0 100644 --- a/packages/sns/test/consumers/SnsSqsPermissionConsumer.spec.ts +++ b/packages/sns/test/consumers/SnsSqsPermissionConsumer.spec.ts @@ -76,6 +76,34 @@ describe('SnsSqsPermissionConsumer', () => { await deleteTopic(snsClient, 'existingTopic') }) + it('does not create a new topic when mixed locator is passed', async () => { + const arn = await assertTopic(snsClient, { + Name: 'existingTopic', + }) + + const newConsumer = new SnsSqsPermissionConsumer(diContainer.cradle, { + locatorConfig: { + topicName: 'existingTopic', + }, + creationConfig: { + queue: { + QueueName: 'newQueue', + }, + }, + }) + + await newConsumer.init() + expect(newConsumer.subscriptionProps.queueUrl).toBe( + 'http://sqs.eu-west-1.localstack:4566/000000000000/newQueue', + ) + expect(newConsumer.subscriptionProps.queueName).toBe('newQueue') + expect(newConsumer.subscriptionProps.topicArn).toEqual(arn) + expect(newConsumer.subscriptionProps.subscriptionArn).toMatch( + 'arn:aws:sns:eu-west-1:000000000000:existingTopic', + ) + await deleteTopic(snsClient, 'existingTopic') + }) + it('updates existing queue when one with different attributes exist', async () => { await assertQueue(sqsClient, { QueueName: 'existingQueue', @@ -109,9 +137,7 @@ describe('SnsSqsPermissionConsumer', () => { ) expect(newConsumer.subscriptionProps.queueName).toBe('existingQueue') - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: newConsumer.subscriptionProps.queueUrl, - }) + const attributes = await getQueueAttributes(sqsClient, newConsumer.subscriptionProps.queueUrl) expect(attributes.result?.attributes).toMatchObject({ KmsMasterKeyId: 'othervalue', @@ -151,9 +177,7 @@ describe('SnsSqsPermissionConsumer', () => { 'http://sqs.eu-west-1.localstack:4566/000000000000/existingQueue', ) - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: newConsumer.subscriptionProps.queueUrl, - }) + const attributes = await getQueueAttributes(sqsClient, newConsumer.subscriptionProps.queueUrl) expect(newConsumer.subscriptionProps.queueName).toBe('existingQueue') expect(attributes.result?.attributes!.Policy).toBe( @@ -186,9 +210,7 @@ describe('SnsSqsPermissionConsumer', () => { ) expect(newConsumer.subscriptionProps.queueName).toBe('existingQueue') - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: newConsumer.subscriptionProps.queueUrl, - }) + const attributes = await getQueueAttributes(sqsClient, newConsumer.subscriptionProps.queueUrl) expect(attributes.result?.attributes!.KmsMasterKeyId).toBe('othervalue') }) @@ -216,9 +238,7 @@ describe('SnsSqsPermissionConsumer', () => { 'http://sqs.eu-west-1.localstack:4566/000000000000/deadLetterQueue', ) - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: newConsumer.subscriptionProps.queueUrl, - }) + const attributes = await getQueueAttributes(sqsClient, newConsumer.subscriptionProps.queueUrl) expect(attributes.result?.attributes).toMatchObject({ RedrivePolicy: JSON.stringify({ @@ -255,9 +275,7 @@ describe('SnsSqsPermissionConsumer', () => { 'http://sqs.eu-west-1.localstack:4566/000000000000/deadLetterQueue', ) - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: newConsumer.subscriptionProps.queueUrl, - }) + const attributes = await getQueueAttributes(sqsClient, newConsumer.subscriptionProps.queueUrl) expect(attributes.result?.attributes).toMatchObject({ RedrivePolicy: JSON.stringify({ @@ -396,7 +414,7 @@ describe('SnsSqsPermissionConsumer', () => { }) describe('happy path', () => { - it('Processes messages', async () => { + it('Processes messages with prehandlers', async () => { await publisher.publish({ id: '1', messageType: 'add', @@ -418,29 +436,6 @@ describe('SnsSqsPermissionConsumer', () => { expect(consumer.addCounter).toBe(1) expect(consumer.removeCounter).toBe(2) }, 10000) - - it('Handles preHandlers', async () => { - await publisher.publish({ - id: '1', - messageType: 'add', - }) - await publisher.publish({ - id: '2', - messageType: 'remove', - }) - await publisher.publish({ - id: '3', - messageType: 'remove', - }) - - await consumer.handlerSpy.waitForMessageWithId('1', 'consumed') - await consumer.handlerSpy.waitForMessageWithId('2', 'consumed') - await consumer.handlerSpy.waitForMessageWithId('3', 'consumed') - - expect(consumer.addBarrierCounter).toBe(3) - expect(consumer.addCounter).toBe(1) - expect(consumer.removeCounter).toBe(2) - }) }) }) diff --git a/packages/sns/test/consumers/SnsSqsPermissionConsumer.ts b/packages/sns/test/consumers/SnsSqsPermissionConsumer.ts index 1935fc5f..a55b61b8 100644 --- a/packages/sns/test/consumers/SnsSqsPermissionConsumer.ts +++ b/packages/sns/test/consumers/SnsSqsPermissionConsumer.ts @@ -149,7 +149,8 @@ export class SnsSqsPermissionConsumer extends AbstractSnsSqsConsumer< }, deadLetterQueue: options.deadLetterQueue, ...(options.locatorConfig - ? { locatorConfig: options.locatorConfig } + ? // biome-ignore lint/suspicious/noExplicitAny: This is expected + { locatorConfig: options.locatorConfig, creationConfig: options.creationConfig as any } : { creationConfig: options.creationConfig ?? { queue: { diff --git a/packages/sns/test/publishers/CreateLocateConfigMixPublisher.ts b/packages/sns/test/publishers/CreateLocateConfigMixPublisher.ts new file mode 100644 index 00000000..06ad820d --- /dev/null +++ b/packages/sns/test/publishers/CreateLocateConfigMixPublisher.ts @@ -0,0 +1,30 @@ +import { AbstractSnsPublisher } from '../../lib/sns/AbstractSnsPublisher' +import type { SNSDependencies, SNSOptions } from '../../lib/sns/AbstractSnsService' +import { + CreateLocateConfigMixConsumer, + type SupportedMessages, +} from '../consumers/CreateLocateConfigMixConsumer' +import { TestEvents } from '../utils/testContext' + +export class CreateLocateConfigMixPublisher extends AbstractSnsPublisher { + constructor( + dependencies: SNSDependencies, + options?: Pick, + ) { + super(dependencies, { + ...(options?.locatorConfig + ? { locatorConfig: options?.locatorConfig } + : { + creationConfig: options?.creationConfig ?? { + topic: { Name: CreateLocateConfigMixConsumer.SUBSCRIBED_TOPIC_NAME }, + }, + }), + deletionConfig: { + deleteIfExists: false, + }, + messageSchemas: [TestEvents.created.consumerSchema, TestEvents.updated.consumerSchema], + handlerSpy: true, + messageTypeField: 'type', + }) + } +} diff --git a/packages/sns/test/utils/testContext.ts b/packages/sns/test/utils/testContext.ts index f3048e15..e4c16e1d 100644 --- a/packages/sns/test/utils/testContext.ts +++ b/packages/sns/test/utils/testContext.ts @@ -21,6 +21,7 @@ import { SnsPublisherManager } from '../../lib/sns/SnsPublisherManager' import { SnsSqsPermissionConsumer } from '../consumers/SnsSqsPermissionConsumer' import { SnsPermissionPublisher } from '../publishers/SnsPermissionPublisher' +import { CreateLocateConfigMixPublisher } from '../publishers/CreateLocateConfigMixPublisher' import { TEST_AWS_CONFIG } from './testSnsConfig' export const SINGLETON_CONFIG = { lifetime: Lifetime.SINGLETON } @@ -118,6 +119,14 @@ export async function registerDependencies( asyncDisposePriority: 40, enabled: queuesEnabled, }), + createLocateConfigMixPublisher: asClass(CreateLocateConfigMixPublisher, { + lifetime: Lifetime.SINGLETON, + asyncInit: 'init', + asyncDispose: 'close', + asyncInitPriority: 40, + asyncDisposePriority: 40, + enabled: queuesEnabled, + }), eventRegistry: asFunction(() => { return new EventRegistry(Object.values(TestEvents)) }, SINGLETON_CONFIG), @@ -188,4 +197,5 @@ export interface Dependencies { CommonSnsPublisher, TestEventsType > + createLocateConfigMixPublisher: CreateLocateConfigMixPublisher } diff --git a/packages/sns/vitest.config.mts b/packages/sns/vitest.config.mts index e73e97ac..86545da4 100644 --- a/packages/sns/vitest.config.mts +++ b/packages/sns/vitest.config.mts @@ -25,10 +25,10 @@ export default defineConfig({ reporter: ['text'], all: true, thresholds: { - lines: 85, + lines: 84, functions: 90, branches: 65, - statements: 85, + statements: 84, }, }, }, diff --git a/packages/sqs/lib/sqs/AbstractSqsConsumer.ts b/packages/sqs/lib/sqs/AbstractSqsConsumer.ts index 74fbcb25..0d02fdfc 100644 --- a/packages/sqs/lib/sqs/AbstractSqsConsumer.ts +++ b/packages/sqs/lib/sqs/AbstractSqsConsumer.ts @@ -44,7 +44,7 @@ export type SQSConsumerOptions< ExecutionContext, PrehandlerOutput, CreationConfigType extends SQSCreationConfig = SQSCreationConfig, - QueueLocatorType extends SQSQueueLocatorType = SQSQueueLocatorType, + QueueLocatorType extends object = SQSQueueLocatorType, > = QueueConsumerOptions< CreationConfigType, QueueLocatorType, @@ -70,7 +70,7 @@ export abstract class AbstractSqsConsumer< ExecutionContext, PrehandlerOutput = undefined, CreationConfigType extends SQSCreationConfig = SQSCreationConfig, - QueueLocatorType extends SQSQueueLocatorType = SQSQueueLocatorType, + QueueLocatorType extends object = SQSQueueLocatorType, ConsumerOptionsType extends SQSConsumerOptions< MessagePayloadType, ExecutionContext, @@ -454,11 +454,9 @@ export abstract class AbstractSqsConsumer< visibilityTimeoutString = this.creationConfig.queue.Attributes?.VisibilityTimeout } else { // if user is using locatorConfig, we should look into queue config - const queueAttributes = await getQueueAttributes( - this.sqsClient, - { queueUrl: this.queueUrl }, - ['VisibilityTimeout'], - ) + const queueAttributes = await getQueueAttributes(this.sqsClient, this.queueUrl, [ + 'VisibilityTimeout', + ]) visibilityTimeoutString = queueAttributes.result?.attributes?.VisibilityTimeout } diff --git a/packages/sqs/lib/sqs/AbstractSqsService.ts b/packages/sqs/lib/sqs/AbstractSqsService.ts index f233b7b0..09eb450d 100644 --- a/packages/sqs/lib/sqs/AbstractSqsService.ts +++ b/packages/sqs/lib/sqs/AbstractSqsService.ts @@ -28,7 +28,7 @@ export type SQSQueueLocatorType = { export abstract class AbstractSqsService< MessagePayloadType extends object, - QueueLocatorType extends SQSQueueLocatorType = SQSQueueLocatorType, + QueueLocatorType extends object = SQSQueueLocatorType, CreationConfigType extends SQSCreationConfig = SQSCreationConfig, SQSOptionsType extends QueueOptions = QueueOptions< CreationConfigType, diff --git a/packages/sqs/lib/utils/sqsInitter.ts b/packages/sqs/lib/utils/sqsInitter.ts index cf3f1bb5..11c321f4 100644 --- a/packages/sqs/lib/utils/sqsInitter.ts +++ b/packages/sqs/lib/utils/sqsInitter.ts @@ -47,12 +47,16 @@ export async function updateQueueAttributes( export async function initSqs( sqsClient: SQSClient, - locatorConfig?: SQSQueueLocatorType, + locatorConfig?: Partial, creationConfig?: SQSCreationConfig, ) { // reuse existing queue only - if (locatorConfig) { - const checkResult = await getQueueAttributes(sqsClient, locatorConfig, ['QueueArn']) + if (locatorConfig?.queueUrl) { + const checkResult = await getQueueAttributes( + sqsClient, + (locatorConfig as SQSQueueLocatorType).queueUrl, + ['QueueArn'], + ) if (checkResult.error === 'not_found') { throw new Error(`Queue with queueUrl ${locatorConfig.queueUrl} does not exist.`) } diff --git a/packages/sqs/lib/utils/sqsUtils.ts b/packages/sqs/lib/utils/sqsUtils.ts index d60c15ad..7be06c90 100644 --- a/packages/sqs/lib/utils/sqsUtils.ts +++ b/packages/sqs/lib/utils/sqsUtils.ts @@ -16,7 +16,7 @@ import type { Either } from '@lokalise/node-core' import { globalLogger } from '@lokalise/node-core' import { isShallowSubset, waitAndRetry } from '@message-queue-toolkit/core' -import type { ExtraSQSCreationParams, SQSQueueLocatorType } from '../sqs/AbstractSqsService' +import type { ExtraSQSCreationParams } from '../sqs/AbstractSqsService' import { generateQueuePublishForTopicPolicy } from './sqsAttributeUtils' import { updateQueueAttributes } from './sqsInitter' @@ -60,11 +60,11 @@ export async function getQueueUrl( export async function getQueueAttributes( sqsClient: SQSClient, - queueLocator: SQSQueueLocatorType, + queueUrl: string, attributeNames: QueueAttributeName[] = ['All'], ): Promise> { const command = new GetQueueAttributesCommand({ - QueueUrl: queueLocator.queueUrl, + QueueUrl: queueUrl, AttributeNames: attributeNames, }) @@ -93,9 +93,7 @@ async function updateExistingQueue( queueConfig: CreateQueueCommandInput, extraParams?: ExtraSQSCreationParams, ) { - const existingAttributes = await getQueueAttributes(sqsClient, { - queueUrl, - }) + const existingAttributes = await getQueueAttributes(sqsClient, queueUrl) if (!existingAttributes.result?.attributes) { throw new Error('Attributes are not set') diff --git a/packages/sqs/package.json b/packages/sqs/package.json index be2412dd..7b73947e 100644 --- a/packages/sqs/package.json +++ b/packages/sqs/package.json @@ -25,28 +25,28 @@ "prepublishOnly": "npm run build:release" }, "dependencies": { - "@lokalise/node-core": "^12.0.0", - "sqs-consumer": "^11.0.1", + "@lokalise/node-core": "^13.0.1", + "sqs-consumer": "^11.1.0", "zod": "^3.23.8" }, "peerDependencies": { - "@aws-sdk/client-sqs": "^3.614.0", + "@aws-sdk/client-sqs": "^3.632.0", "@message-queue-toolkit/core": ">=15.0.0" }, "devDependencies": { - "@aws-sdk/client-s3": "^3.614.0", - "@aws-sdk/client-sqs": "^3.614.0", + "@aws-sdk/client-s3": "^3.670.0", + "@aws-sdk/client-sqs": "^3.670.0", "@biomejs/biome": "1.9.3", "@kibertoad/biome-config": "^1.2.1", "@message-queue-toolkit/core": "*", "@message-queue-toolkit/s3-payload-store": "*", - "@types/node": "^22.0.0", - "@vitest/coverage-v8": "^2.0.4", + "@types/node": "^22.7.5", + "@vitest/coverage-v8": "^2.1.2", "awilix": "^12.0.1", "awilix-manager": "^5.1.0", "del-cli": "^6.0.0", - "typescript": "^5.5.3", - "vitest": "^2.0.4" + "typescript": "^5.6.3", + "vitest": "^2.1.2" }, "homepage": "https://github.com/kibertoad/message-queue-toolkit", "repository": { diff --git a/packages/sqs/test/consumers/SqsPermissionConsumer.spec.ts b/packages/sqs/test/consumers/SqsPermissionConsumer.spec.ts index 86dafbe5..e74b53bf 100644 --- a/packages/sqs/test/consumers/SqsPermissionConsumer.spec.ts +++ b/packages/sqs/test/consumers/SqsPermissionConsumer.spec.ts @@ -100,9 +100,7 @@ describe('SqsPermissionConsumer', () => { }) expect(updateCall).toBeDefined() - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: newConsumer.queueProps.url, - }) + const attributes = await getQueueAttributes(sqsClient, newConsumer.queueProps.url) expect(attributes.result?.attributes).toMatchObject({ KmsMasterKeyId: 'othervalue', @@ -146,9 +144,7 @@ describe('SqsPermissionConsumer', () => { }) expect(updateCall).toBeUndefined() - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: newConsumer.queueProps.url, - }) + const attributes = await getQueueAttributes(sqsClient, newConsumer.queueProps.url) expect(attributes.result?.attributes!.KmsMasterKeyId).toBe('somevalue') }) diff --git a/packages/sqs/test/consumers/SqsPermisssionConsumer.deadLetterQueue.spec.ts b/packages/sqs/test/consumers/SqsPermisssionConsumer.deadLetterQueue.spec.ts index b53c2fcd..b0d32d70 100644 --- a/packages/sqs/test/consumers/SqsPermisssionConsumer.deadLetterQueue.spec.ts +++ b/packages/sqs/test/consumers/SqsPermisssionConsumer.deadLetterQueue.spec.ts @@ -74,9 +74,7 @@ describe('SqsPermissionConsumer - deadLetterQueue', () => { `http://sqs.eu-west-1.localstack:4566/000000000000/${customDeadLetterQueueName}`, ) - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: consumer.queueProps.url, - }) + const attributes = await getQueueAttributes(sqsClient, consumer.queueProps.url) expect(attributes.result?.attributes).toMatchObject({ RedrivePolicy: JSON.stringify({ deadLetterTargetArn: `arn:aws:sqs:eu-west-1:000000000000:${customDeadLetterQueueName}`, @@ -103,9 +101,7 @@ describe('SqsPermissionConsumer - deadLetterQueue', () => { `http://sqs.eu-west-1.localstack:4566/000000000000/${customDeadLetterQueueName}`, ) - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: consumer.queueProps.url, - }) + const attributes = await getQueueAttributes(sqsClient, consumer.queueProps.url) expect(attributes.result?.attributes).toMatchObject({ RedrivePolicy: JSON.stringify({ deadLetterTargetArn: `arn:aws:sqs:eu-west-1:000000000000:${customDeadLetterQueueName}`, @@ -179,9 +175,7 @@ describe('SqsPermissionConsumer - deadLetterQueue', () => { ) expect(consumer.dlqUrl).toBe(dlqUrl) - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: consumer.dlqUrl, - }) + const attributes = await getQueueAttributes(sqsClient, consumer.dlqUrl) expect(attributes.result?.attributes!.KmsMasterKeyId).toBe('new value') }) @@ -201,9 +195,7 @@ describe('SqsPermissionConsumer - deadLetterQueue', () => { expect(consumer.queueProps.url).toBe(queueUrl) expect(consumer.dlqUrl).toBe(dlqUrl) - const attributes = await getQueueAttributes(sqsClient, { - queueUrl, - }) + const attributes = await getQueueAttributes(sqsClient, queueUrl) expect(attributes.result?.attributes).toMatchObject({ RedrivePolicy: JSON.stringify({ diff --git a/packages/sqs/test/publishers/SqsPermissionPublisher.spec.ts b/packages/sqs/test/publishers/SqsPermissionPublisher.spec.ts index 69869afa..995c089e 100644 --- a/packages/sqs/test/publishers/SqsPermissionPublisher.spec.ts +++ b/packages/sqs/test/publishers/SqsPermissionPublisher.spec.ts @@ -95,9 +95,7 @@ describe('SqsPermissionPublisher', () => { }) expect(updateCall).toBeDefined() - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: newPublisher.queueProps.url, - }) + const attributes = await getQueueAttributes(sqsClient, newPublisher.queueProps.url) expect(attributes.result?.attributes!.KmsMasterKeyId).toBe('othervalue') }) @@ -138,9 +136,7 @@ describe('SqsPermissionPublisher', () => { }) expect(updateCall).toBeUndefined() - const attributes = await getQueueAttributes(sqsClient, { - queueUrl: newPublisher.queueProps.url, - }) + const attributes = await getQueueAttributes(sqsClient, newPublisher.queueProps.url) expect(attributes.result?.attributes!.KmsMasterKeyId).toBe('somevalue') }) diff --git a/packages/sqs/vitest.config.mts b/packages/sqs/vitest.config.mts index c33d002e..f5c6d61b 100644 --- a/packages/sqs/vitest.config.mts +++ b/packages/sqs/vitest.config.mts @@ -19,10 +19,10 @@ export default defineConfig({ reporter: ['text'], all: true, thresholds: { - lines: 89, + lines: 88, functions: 100, branches: 74, - statements: 89, + statements: 88, }, }, },