@message-queue-toolkit/sns
Version:
SNS adapter for message-queue-toolkit
184 lines • 7.11 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.calculateOutgoingMessageSize = void 0;
exports.getTopicAttributes = getTopicAttributes;
exports.getSubscriptionAttributes = getSubscriptionAttributes;
exports.assertTopic = assertTopic;
exports.deleteTopic = deleteTopic;
exports.deleteSubscription = deleteSubscription;
exports.findSubscriptionByTopicAndQueue = findSubscriptionByTopicAndQueue;
exports.getTopicArnByName = getTopicArnByName;
const client_sns_1 = require("@aws-sdk/client-sns");
const client_sns_2 = require("@aws-sdk/client-sns");
const node_core_1 = require("@lokalise/node-core");
const sqs_1 = require("@message-queue-toolkit/sqs");
const snsAttributeUtils_1 = require("./snsAttributeUtils");
const stsUtils_1 = require("./stsUtils");
async function getTopicAttributes(snsClient, topicArn) {
const command = new client_sns_2.GetTopicAttributesCommand({
TopicArn: topicArn,
});
try {
const response = await snsClient.send(command);
return {
result: {
attributes: response.Attributes,
},
};
}
catch (err) {
// @ts-ignore
if (err.Code === 'AWS.SimpleQueueService.NonExistentQueue') {
return {
// @ts-ignore
error: 'not_found',
};
}
throw err;
}
}
async function getSubscriptionAttributes(snsClient, subscriptionArn) {
const command = new client_sns_2.GetSubscriptionAttributesCommand({
SubscriptionArn: subscriptionArn,
});
try {
const response = await snsClient.send(command);
return {
result: {
attributes: response.Attributes,
},
};
}
catch (err) {
// @ts-ignore
if (err.Code === 'AWS.SimpleQueueService.NonExistentQueue') {
return {
// @ts-ignore
error: 'not_found',
};
}
throw err;
}
}
async function assertTopic(snsClient, stsClient, topicOptions, extraParams) {
let topicArn;
try {
const command = new client_sns_2.CreateTopicCommand(topicOptions);
const response = await snsClient.send(command);
if (!response.TopicArn)
throw new Error('No topic arn in response');
topicArn = response.TopicArn;
}
catch (error) {
if (!(0, node_core_1.isError)(error))
throw error;
// To build ARN we need topic name and error should be "topic already exist with different tags"
if (!topicOptions.Name || !isTopicAlreadyExistWithDifferentTagsError(error))
throw error;
topicArn = await (0, stsUtils_1.buildTopicArn)(stsClient, topicOptions.Name);
if (!extraParams?.forceTagUpdate) {
const currentTags = await snsClient.send(new client_sns_1.ListTagsForResourceCommand({ ResourceArn: topicArn }));
throw new node_core_1.InternalError({
message: `${topicOptions.Name} - ${error.message}`,
details: {
currentTags: JSON.stringify(currentTags),
newTags: JSON.stringify(topicOptions.Tags),
},
errorCode: 'SNS_TOPIC_ALREADY_EXISTS_WITH_DIFFERENT_TAGS',
cause: error,
});
}
}
if (extraParams?.queueUrlsWithSubscribePermissionsPrefix || extraParams?.allowedSourceOwner) {
const setTopicAttributesCommand = new client_sns_2.SetTopicAttributesCommand({
TopicArn: topicArn,
AttributeName: 'Policy',
AttributeValue: (0, snsAttributeUtils_1.generateTopicSubscriptionPolicy)({
topicArn,
allowedSqsQueueUrlPrefix: extraParams.queueUrlsWithSubscribePermissionsPrefix,
allowedSourceOwner: extraParams.allowedSourceOwner,
}),
});
await snsClient.send(setTopicAttributesCommand);
}
if (extraParams?.forceTagUpdate && topicOptions.Tags) {
const tagTopicCommand = new client_sns_1.TagResourceCommand({
ResourceArn: topicArn,
Tags: topicOptions.Tags,
});
await snsClient.send(tagTopicCommand);
}
return topicArn;
}
async function deleteTopic(snsClient, stsClient, topicName) {
try {
const topicArn = await assertTopic(snsClient, stsClient, {
Name: topicName,
});
await snsClient.send(new client_sns_2.DeleteTopicCommand({
TopicArn: topicArn,
}));
}
catch (_) {
// we don't care it operation has failed
}
}
async function deleteSubscription(client, subscriptionArn) {
const command = new client_sns_2.UnsubscribeCommand({
SubscriptionArn: subscriptionArn,
});
try {
await client.send(command);
}
catch (_) {
// we don't care it operation has failed
}
}
async function findSubscriptionByTopicAndQueue(snsClient, topicArn, queueArn) {
const listSubscriptionsCommand = new client_sns_2.ListSubscriptionsByTopicCommand({
TopicArn: topicArn,
});
const listSubscriptionResult = await snsClient.send(listSubscriptionsCommand);
return listSubscriptionResult.Subscriptions?.find((entry) => {
return entry.Endpoint === queueArn;
});
}
async function getTopicArnByName(snsClient, topicName) {
if (!topicName) {
throw new Error('topicName is not provided');
}
// Use paginator to automatically handle NextToken
const paginator = (0, client_sns_1.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.
*
* SNS imposes a 256 KB limit on the total size of a message, which includes both the message body and any metadata (attributes).
* This function currently computes the size based solely on the message body, as no attributes are included at this time.
* For future updates, if message attributes are added, their sizes should also be considered.
*
* Reference: https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html
*
* A wrapper around the equivalent function in the SQS package.
*/
const calculateOutgoingMessageSize = (message) => (0, sqs_1.calculateOutgoingMessageSize)(message);
exports.calculateOutgoingMessageSize = calculateOutgoingMessageSize;
const isTopicAlreadyExistWithDifferentTagsError = (error) => !!error &&
(0, node_core_1.isError)(error) &&
'Error' in error &&
!!error.Error &&
typeof error.Error === 'object' &&
'Code' in error.Error &&
'Message' in error.Error &&
typeof error.Error.Message === 'string' &&
error.Error.Code === 'InvalidParameter' &&
error.Error.Message.includes('already exists with different tags');
//# sourceMappingURL=snsUtils.js.map
;