Repository: getmoto/moto (python)

Issue:
resourcegroupstaggingapi get_resources doesn't properly filter on s3:bucket
When using the `get_resources` method from the resource groups tagging api with `s3:bucket` as an entry in `ResourceTypesFilters`, no S3 Buckets are being returned. Moto seems to only return S3 buckets when `s3` is provided as a filter rather than the full `s3:bucket`

Here's a snippet that shows the issue:

```python
@mock_aws
class MyTestClass(unittest.TestCase):
    def setUp(self):
        s3 = boto3.resource("s3", region_name="us-east-1")
        s3.create_bucket(Bucket="test-bucket")
        s3.BucketTagging("test-bucket").put(
            Tagging={
                "TagSet": [
                    {"Key": "foo", "Value": "bar"}
                ]
            }
        )

    def test_stuff(self):
        boto3.client("resourcegroupstaggingapi", region_name="us-east-1").get_resources(
                    ResourceTypeFilters=["s3:bucket"]
                )["ResourceTagMappingList"] # Empty list! This should be returning the bucket
        
        boto3.client("resourcegroupstaggingapi", region_name="us-east-1").get_resources(
                    ResourceTypeFilters=["s3"]
                )["ResourceTagMappingList"] # This does work and returns the bucket properly
```

`s3:buckets` is a valid type filter that works in the real AWS API, and is even called out in the docs as an example https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/resourcegroupstaggingapi/client/get_resources.html. It seems that moto should support this as well.

Relevant Versions:
moto - 5.0.9
boto3 - 1.34.135
Python - 3.11.6
MacOS - 14.5

Pull request:
ResourceGroupsTagging: Add support for `s3:bucket` resource type filter
AWS Resource Groups Tagging API only supports S3 bucket resources[1], so `s3` and `s3:bucket` are interchangeable and return the same results.

[1]: https://docs.aws.amazon.com/resourcegroupstagging/latest/APIReference/supported-services.html

Closes #7797 

Patch:
diff --git a/moto/resourcegroupstaggingapi/models.py b/moto/resourcegroupstaggingapi/models.py
index 55d5e5f55..c3548be44 100644
--- a/moto/resourcegroupstaggingapi/models.py
+++ b/moto/resourcegroupstaggingapi/models.py
@@ -211,7 +211,11 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
                 yield {"ResourceARN": f"{vault.backup_vault_arn}", "Tags": tags}
 
         # S3
-        if not resource_type_filters or "s3" in resource_type_filters:
+        if (
+            not resource_type_filters
+            or "s3" in resource_type_filters
+            or "s3:bucket" in resource_type_filters
+        ):
             for bucket in self.s3_backend.buckets.values():
                 tags = self.s3_backend.tagger.list_tags_for_resource(bucket.arn)["Tags"]
                 if not tags or not tag_filter(


Test patch:
diff --git a/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py b/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
index 60fc8cc6d..ed9dacc46 100644
--- a/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
+++ b/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
@@ -1,6 +1,7 @@
 import json
 
 import boto3
+import pytest
 from botocore.client import ClientError
 
 from moto import mock_aws
@@ -534,8 +535,9 @@ def test_get_resources_target_group():
     assert {"Key": "Test", "Value": "1"} in resp["ResourceTagMappingList"][0]["Tags"]
 
 
+@pytest.mark.parametrize("resource_type", ["s3", "s3:bucket"])
 @mock_aws
-def test_get_resources_s3():
+def test_get_resources_s3(resource_type):
     # Tests pagination
     s3_client = boto3.client("s3", region_name="eu-central-1")
 
@@ -556,14 +558,16 @@ def test_get_resources_s3():
         response_keys.add("key" + i_str)
 
     rtapi = boto3.client("resourcegroupstaggingapi", region_name="eu-central-1")
-    resp = rtapi.get_resources(ResourcesPerPage=2)
+    resp = rtapi.get_resources(ResourcesPerPage=2, ResourceTypeFilters=[resource_type])
     for resource in resp["ResourceTagMappingList"]:
         response_keys.remove(resource["Tags"][0]["Key"])
 
     assert len(response_keys) == 2
 
     resp = rtapi.get_resources(
-        ResourcesPerPage=2, PaginationToken=resp["PaginationToken"]
+        ResourcesPerPage=2,
+        PaginationToken=resp["PaginationToken"],
+        ResourceTypeFilters=[resource_type],
     )
     for resource in resp["ResourceTagMappingList"]:
         response_keys.remove(resource["Tags"][0]["Key"])