From e1db34e7c1d39a8da0403728ab27dc424e98f44c Mon Sep 17 00:00:00 2001 From: Matthew Bessette Date: Wed, 22 Mar 2023 11:15:42 -0400 Subject: [PATCH] Fixed Issue #1: [SNS] Subscriptions all sub to the same topic arn --- src/aws-shared-entities/attributes.service.ts | 4 ++ src/aws-shared-entities/tags.service.ts | 4 ++ .../get-subscription-attributes.handler.ts | 7 ++- src/sns/get-topic-attributes.handler.ts | 9 +++- src/sns/sns.module.ts | 2 + src/sns/unsubscribe.handler.ts | 43 +++++++++++++++++++ src/sqs/receive-message.handler.ts | 8 ++-- src/sqs/sqs.module.ts | 2 + 8 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 src/sns/unsubscribe.handler.ts diff --git a/src/aws-shared-entities/attributes.service.ts b/src/aws-shared-entities/attributes.service.ts index 6d8e0e1..725de4a 100644 --- a/src/aws-shared-entities/attributes.service.ts +++ b/src/aws-shared-entities/attributes.service.ts @@ -30,6 +30,10 @@ export class AttributesService { return await this.repo.save(dto); } + async deleteByArn(arn: string) { + await this.repo.delete({ arn }); + } + async deleteByArnAndName(arn: string, name: string) { await this.repo.delete({ arn, name }); } diff --git a/src/aws-shared-entities/tags.service.ts b/src/aws-shared-entities/tags.service.ts index 6efef43..0178d5b 100644 --- a/src/aws-shared-entities/tags.service.ts +++ b/src/aws-shared-entities/tags.service.ts @@ -26,6 +26,10 @@ export class TagsService { } } + async deleteByArn(arn: string) { + await this.repo.delete({ arn }); + } + async deleteByArnAndName(arn: string, name: string) { await this.repo.delete({ arn, name }); } diff --git a/src/sns/get-subscription-attributes.handler.ts b/src/sns/get-subscription-attributes.handler.ts index 165356e..1cc26b0 100644 --- a/src/sns/get-subscription-attributes.handler.ts +++ b/src/sns/get-subscription-attributes.handler.ts @@ -28,8 +28,13 @@ export class GetSubscriptionAttributesHandler extends AbstractActionHandler { protected async handle({ SubscriptionArn }: QueryParams, awsProperties: AwsProperties) { - const id = SubscriptionArn.split(':')[-1]; + const id = SubscriptionArn.split(':').pop(); const subscription = await this.snsTopicSubscriptionRepo.findOne({ where: { id }}); + + if (!subscription) { + return; + } + const attributes = await this.attributeService.getByArn(SubscriptionArn); const attributeMap = attributes.reduce((m, a) => { m[a.name] = a.value; diff --git a/src/sns/get-topic-attributes.handler.ts b/src/sns/get-topic-attributes.handler.ts index d8a490f..a27c80f 100644 --- a/src/sns/get-topic-attributes.handler.ts +++ b/src/sns/get-topic-attributes.handler.ts @@ -6,6 +6,7 @@ import { Action } from '../action.enum'; import { SnsTopic } from './sns-topic.entity'; import * as Joi from 'joi'; import { AttributesService } from '../aws-shared-entities/attributes.service'; +import { SnsTopicSubscription } from './sns-topic-subscription.entity'; type QueryParams = { TopicArn: string; @@ -17,6 +18,8 @@ export class GetTopicAttributesHandler extends AbstractActionHandler { constructor( @InjectRepository(SnsTopic) private readonly snsTopicRepo: Repository, + @InjectRepository(SnsTopicSubscription) + private readonly snsTopicSubscriptionRepo: Repository, private readonly attributeService: AttributesService, ) { super(); @@ -28,7 +31,7 @@ export class GetTopicAttributesHandler extends AbstractActionHandler { protected async handle({ TopicArn }: QueryParams, awsProperties: AwsProperties) { - const name = TopicArn.split(':')[-1]; + const name = TopicArn.split(':').pop(); const topic = await this.snsTopicRepo.findOne({ where: { name }}); const attributes = await this.attributeService.getByArn(TopicArn); const attributeMap = attributes.reduce((m, a) => { @@ -36,10 +39,12 @@ export class GetTopicAttributesHandler extends AbstractActionHandler { return m; }, {}); + const subscriptionCount = await this.snsTopicSubscriptionRepo.count({ where: { topicArn: TopicArn } }); + const response = { DisplayName: topic.name, Owner: topic.accountId, - SubscriptionsConfirmed: '0', + SubscriptionsConfirmed: `${subscriptionCount}`, SubscriptionsDeleted: '0', SubscriptionsPending: '0', TopicArn: topic.topicArn, diff --git a/src/sns/sns.module.ts b/src/sns/sns.module.ts index c031a19..eaf95a4 100644 --- a/src/sns/sns.module.ts +++ b/src/sns/sns.module.ts @@ -19,6 +19,7 @@ import { SnsTopicSubscription } from './sns-topic-subscription.entity'; import { SnsTopic } from './sns-topic.entity'; import { SnsHandlers } from './sns.constants'; import { SubscribeHandler } from './subscribe.handler'; +import { UnsubscribeHandler } from './unsubscribe.handler'; const handlers = [ CreateTopicHandler, @@ -30,6 +31,7 @@ const handlers = [ SetSubscriptionAttributesHandler, SetTopicAttributesHandler, SubscribeHandler, + UnsubscribeHandler, ]; const actions = [ diff --git a/src/sns/unsubscribe.handler.ts b/src/sns/unsubscribe.handler.ts new file mode 100644 index 0000000..4929610 --- /dev/null +++ b/src/sns/unsubscribe.handler.ts @@ -0,0 +1,43 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler'; +import { Action } from '../action.enum'; +import * as Joi from 'joi'; +import { TagsService } from '../aws-shared-entities/tags.service'; +import { AttributesService } from '../aws-shared-entities/attributes.service'; +import { SnsTopicSubscription } from './sns-topic-subscription.entity'; +import * as uuid from 'uuid'; + +type QueryParams = { + SubscriptionArn: string; +} + +@Injectable() +export class UnsubscribeHandler extends AbstractActionHandler { + + constructor( + @InjectRepository(SnsTopicSubscription) + private readonly snsTopicSubscription: Repository, + private readonly tagsService: TagsService, + private readonly attributeService: AttributesService, + ) { + super(); + } + + format = Format.Xml; + action = Action.SnsUnsubscribe; + validator = Joi.object({ + SubscriptionArn: Joi.string().required(), + }); + + protected async handle(params: QueryParams, awsProperties: AwsProperties) { + + const id = params.SubscriptionArn.split(':').pop(); + const subscription = await this.snsTopicSubscription.findOne({ where: { id } }); + + await this.tagsService.deleteByArn(subscription.arn); + await this.attributeService.deleteByArn(subscription.arn); + await this.snsTopicSubscription.delete({ id }); + } +} diff --git a/src/sqs/receive-message.handler.ts b/src/sqs/receive-message.handler.ts index fdf8b40..763fb6e 100644 --- a/src/sqs/receive-message.handler.ts +++ b/src/sqs/receive-message.handler.ts @@ -7,7 +7,7 @@ import { SqsQueueEntryService } from './sqs-queue-entry.service'; import crypto from 'crypto'; type QueryParams = { - __path: string; + QueueUrl: string; MaxNumberOfMessages?: number; VisibilityTimeout?: number; } @@ -24,14 +24,14 @@ export class ReceiveMessageHandler extends AbstractActionHandler { format = Format.Xml; action = Action.SqsReceiveMessage; validator = Joi.object({ - __path: Joi.string().required(), + QueueUrl: Joi.string().required(), MaxNumberOfMessages: Joi.number(), VisibilityTimeout: Joi.number(), }); - protected async handle({ __path, MaxNumberOfMessages, VisibilityTimeout }: QueryParams, awsProperties: AwsProperties) { + protected async handle({ QueueUrl, MaxNumberOfMessages, VisibilityTimeout }: QueryParams, awsProperties: AwsProperties) { - const [accountId, name] = SqsQueue.getAccountIdAndNameFromPath(__path); + const [accountId, name] = SqsQueue.tryGetAccountIdAndNameFromPathOrArn(QueueUrl); const records = await this.sqsQueueEntryService.recieveMessages(accountId, name, MaxNumberOfMessages, VisibilityTimeout); return records.map(r => ({ Message: { diff --git a/src/sqs/sqs.module.ts b/src/sqs/sqs.module.ts index 27975c7..ddb8b96 100644 --- a/src/sqs/sqs.module.ts +++ b/src/sqs/sqs.module.ts @@ -7,6 +7,7 @@ import { DefaultActionHandlerProvider } from '../default-action-handler/default- import { ExistingActionHandlersProvider } from '../default-action-handler/existing-action-handlers.provider'; import { CreateQueueHandler } from './create-queue.handler'; import { PurgeQueueHandler } from './purge-queue.handler'; +import { ReceiveMessageHandler } from './receive-message.handler'; import { SetQueueAttributesHandler } from './set-queue-attributes.handler'; import { SqsQueueEntryService } from './sqs-queue-entry.service'; import { SqsQueue } from './sqs-queue.entity'; @@ -15,6 +16,7 @@ import { SqsHandlers } from './sqs.constants'; const handlers = [ CreateQueueHandler, PurgeQueueHandler, + ReceiveMessageHandler, SetQueueAttributesHandler, ]