Skip to content

Commit 5a0bc0d

Browse files
authored
Adding dynamodb support to get_resources in resourcegroupstagging api (#7471)
1 parent 62188e7 commit 5a0bc0d

2 files changed

Lines changed: 91 additions & 5 deletions

File tree

moto/resourcegroupstaggingapi/models.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from moto.backup.models import BackupBackend, backup_backends
66
from moto.core.base_backend import BackendDict, BaseBackend
77
from moto.core.exceptions import RESTError
8+
from moto.dynamodb.models import DynamoDBBackend, dynamodb_backends
89
from moto.ec2 import ec2_backends
910
from moto.ecs.models import EC2ContainerServiceBackend, ecs_backends
1011
from moto.elb.models import ELBBackend, elb_backends
@@ -110,6 +111,10 @@ def sqs_backend(self) -> SQSBackend:
110111
def backup_backend(self) -> BackupBackend:
111112
return backup_backends[self.account_id][self.region_name]
112113

114+
@property
115+
def dynamodb_backend(self) -> DynamoDBBackend:
116+
return dynamodb_backends[self.account_id][self.region_name]
117+
113118
def _get_resources_generator(
114119
self,
115120
tag_filters: Optional[List[Dict[str, Any]]] = None,
@@ -198,7 +203,6 @@ def format_tag_keys(
198203

199204
# CloudFormation
200205
if not resource_type_filters or "cloudformation:stack" in resource_type_filters:
201-
202206
try:
203207
from moto.cloudformation import cloudformation_backends
204208

@@ -500,7 +504,6 @@ def format_tag_keys(
500504

501505
# SNS
502506
if not resource_type_filters or "sns" in resource_type_filters:
503-
504507
for topic in self.sns_backend.topics.values():
505508
tags = format_tags(topic._tags)
506509
if not tags or not tag_filter(
@@ -545,6 +548,21 @@ def format_tag_keys(
545548
"Tags": tags,
546549
}
547550

551+
if (
552+
not resource_type_filters
553+
or "dynamodb" in resource_type_filters
554+
or "dynamodb:table" in resource_type_filters
555+
):
556+
for table in self.dynamodb_backend.tables.values():
557+
tags = table.tags
558+
559+
if not tags or not tag_filter(tags):
560+
continue
561+
yield {
562+
"ResourceARN": table.table_arn,
563+
"Tags": tags,
564+
}
565+
548566
def _get_tag_keys_generator(self) -> Iterator[str]:
549567
# Look at
550568
# https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
@@ -738,7 +756,6 @@ def get_resources(
738756
def get_tag_keys(
739757
self, pagination_token: Optional[str] = None
740758
) -> Tuple[Optional[str], List[str]]:
741-
742759
if pagination_token:
743760
if pagination_token not in self._pages:
744761
raise RESTError(
@@ -786,7 +803,6 @@ def get_tag_keys(
786803
def get_tag_values(
787804
self, pagination_token: Optional[str], key: str
788805
) -> Tuple[Optional[str], List[str]]:
789-
790806
if pagination_token:
791807
if pagination_token not in self._pages:
792808
raise RESTError(
@@ -835,7 +851,7 @@ def tag_resources(
835851
self, resource_arns: List[str], tags: Dict[str, str]
836852
) -> Dict[str, Dict[str, Any]]:
837853
"""
838-
Only Logs and RDS resources are currently supported
854+
Only DynamoDB, Logs and RDS resources are currently supported
839855
"""
840856
missing_resources = []
841857
missing_error: Dict[str, Any] = {
@@ -850,6 +866,10 @@ def tag_resources(
850866
)
851867
if arn.startswith("arn:aws:logs:"):
852868
self.logs_backend.tag_resource(arn, tags)
869+
if arn.startswith("arn:aws:dynamodb"):
870+
self.dynamodb_backend.tag_resource(
871+
arn, TaggingService.convert_dict_to_tags_input(tags)
872+
)
853873
else:
854874
missing_resources.append(arn)
855875
return {arn: missing_error for arn in missing_resources}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import unittest
2+
3+
import boto3
4+
5+
from moto import mock_aws
6+
7+
8+
@mock_aws
9+
class TestDynamoDBTagging(unittest.TestCase):
10+
def setUp(self) -> None:
11+
self.dynamodb = boto3.client("dynamodb", region_name="us-west-2")
12+
self.rtapi = boto3.client("resourcegroupstaggingapi", region_name="us-west-2")
13+
self.resources_tagged = []
14+
self.resources_untagged = []
15+
for i in range(3):
16+
table = self.dynamodb.create_table(
17+
AttributeDefinitions=[{"AttributeName": "col", "AttributeType": "S"}],
18+
TableName=f"table-{i}",
19+
KeySchema=[{"AttributeName": "col", "KeyType": "HASH"}],
20+
BillingMode="PAY_PER_REQUEST",
21+
).get("TableDescription")
22+
self.dynamodb.tag_resource(
23+
ResourceArn=table["TableArn"],
24+
Tags=[{"Key": "test", "Value": f"value-{i}"}] if i else [],
25+
)
26+
group = self.resources_tagged if i else self.resources_untagged
27+
group.append(table["TableArn"])
28+
29+
def test_get_resources_dynamodb(self):
30+
def assert_response(response, expected_count, resource_type=None):
31+
results = response.get("ResourceTagMappingList", [])
32+
assert len(results) == expected_count
33+
for item in results:
34+
arn = item["ResourceARN"]
35+
assert arn in self.resources_tagged
36+
assert arn not in self.resources_untagged
37+
if resource_type:
38+
assert f":{resource_type}:" in arn
39+
40+
resp = self.rtapi.get_resources(ResourceTypeFilters=["dynamodb"])
41+
assert_response(resp, 2)
42+
resp = self.rtapi.get_resources(ResourceTypeFilters=["dynamodb:table"])
43+
assert_response(resp, 2)
44+
resp = self.rtapi.get_resources(
45+
TagFilters=[{"Key": "test", "Values": ["value-1"]}]
46+
)
47+
assert_response(resp, 1)
48+
49+
def test_tag_resources_dynamodb(self):
50+
# WHEN
51+
# we tag resources
52+
self.rtapi.tag_resources(
53+
ResourceARNList=self.resources_tagged,
54+
Tags={"key1": "value1", "key2": "value2"},
55+
)
56+
57+
# THEN
58+
# we can retrieve the tags using the DynamoDB API
59+
def get_tags(arn):
60+
return self.dynamodb.list_tags_of_resource(ResourceArn=arn)["Tags"]
61+
62+
for arn in self.resources_tagged:
63+
assert {"Key": "key1", "Value": "value1"} in get_tags(arn)
64+
assert {"Key": "key2", "Value": "value2"} in get_tags(arn)
65+
for arn in self.resources_untagged:
66+
assert get_tags(arn) == []

0 commit comments

Comments
 (0)