99from localstack .aws .api import RequestContext
1010from localstack .aws .api .sns import (
1111 ConfirmSubscriptionResponse ,
12+ AmazonResourceName ,
1213 CreateTopicResponse ,
1314 GetSMSAttributesResponse ,
1415 GetSubscriptionAttributesResponse ,
1718 ListString ,
1819 ListSubscriptionsByTopicResponse ,
1920 ListSubscriptionsResponse ,
21+ ListTagsForResourceResponse ,
2022 ListTopicsResponse ,
2123 MapStringToString ,
2224 NotFoundException ,
2628 SubscribeResponse ,
2729 Subscription ,
2830 SubscriptionAttributesMap ,
31+ TagKeyList ,
2932 TagList ,
33+ TagResourceResponse ,
3034 TopicAttributesMap ,
35+ UntagResourceResponse ,
3136 attributeName ,
3237 attributeValue ,
3338 authenticateOnUnsubscribe ,
@@ -102,6 +107,11 @@ def create_topic(
102107 if not attrs .get (k ) or not attrs .get (k ) == v :
103108 # TODO:
104109 raise InvalidParameterException ("Fix this Exception message and type" )
110+ tag_resource_success = _check_matching_tags (topic_arn , tags , store )
111+ if not tag_resource_success :
112+ raise InvalidParameterException (
113+ "Invalid parameter: Tags Reason: Topic already exists with different tags"
114+ )
105115 return CreateTopicResponse (TopicArn = topic_arn )
106116
107117 attributes = attributes or {}
@@ -121,7 +131,8 @@ def create_topic(
121131 raise InvalidParameterException ("Invalid parameter: Topic Name" )
122132
123133 topic = _create_topic (name = name , attributes = attributes , context = context )
124- # todo: tags
134+ if tags :
135+ self .tag_resource (context = context , resource_arn = topic_arn , tags = tags )
125136
126137 store .topics [topic_arn ] = topic
127138
@@ -546,6 +557,34 @@ def get_sms_attributes(
546557
547558 return GetSMSAttributesResponse (attributes = return_attributes )
548559
560+ def list_tags_for_resource (
561+ self , context : RequestContext , resource_arn : AmazonResourceName , ** kwargs
562+ ) -> ListTagsForResourceResponse :
563+ store = sns_stores [context .account_id ][context .region ]
564+ tags = store .TAGS .list_tags_for_resource (resource_arn )
565+ return ListTagsForResourceResponse (Tags = tags .get ("Tags" ))
566+
567+ def tag_resource (
568+ self , context : RequestContext , resource_arn : AmazonResourceName , tags : TagList , ** kwargs
569+ ) -> TagResourceResponse :
570+ unique_tag_keys = {tag ["Key" ] for tag in tags }
571+ if len (unique_tag_keys ) < len (tags ):
572+ raise InvalidParameterException ("Invalid parameter: Duplicated keys are not allowed." )
573+ store = sns_stores [context .account_id ][context .region ]
574+ store .TAGS .tag_resource (resource_arn , tags )
575+ return TagResourceResponse ()
576+
577+ def untag_resource (
578+ self ,
579+ context : RequestContext ,
580+ resource_arn : AmazonResourceName ,
581+ tag_keys : TagKeyList ,
582+ ** kwargs ,
583+ ) -> UntagResourceResponse :
584+ store = sns_stores [context .account_id ][context .region ]
585+ store .TAGS .untag_resource (resource_arn , tag_keys )
586+ return UntagResourceResponse ()
587+
549588 @staticmethod
550589 def get_store (account_id : str , region : str ) -> SnsStore :
551590 return sns_stores [account_id ][region ]
@@ -649,3 +688,24 @@ def _validate_sms_attributes(attributes: dict) -> None:
649688def _set_sms_attribute_default (store : SnsStore ) -> None :
650689 # TODO: don't call this on every sms attribute crud api call
651690 store .sms_attributes .setdefault ("MonthlySpendLimit" , "1" )
691+
692+
693+ def _check_matching_tags (topic_arn : str , tags : TagList | None , store : SnsStore ) -> bool :
694+ """
695+ Checks if a topic to be created doesn't already exist with different tags
696+ :param topic_arn: Arn of the topic
697+ :param tags: Tags to be checked
698+ :param store: Store object that holds the topics and tags
699+ :return: False if there is a mismatch in tags, True otherwise
700+ """
701+ existing_tags = store .TAGS .list_tags_for_resource (topic_arn )["Tags" ]
702+ # if this is none there is nothing to check
703+ if topic_arn in store .topics :
704+ if tags is None :
705+ tags = []
706+ for tag in tags :
707+ # this means topic already created with empty tags and when we try to create it
708+ # again with other tag value then it should fail according to aws documentation.
709+ if existing_tags is not None and tag not in existing_tags :
710+ return False
711+ return True
0 commit comments