Repository: getmoto/moto
Pull request #7055 (2023-11-22 15:39:41)

cognito-identity: Support for list identity pools
Hi, nice to meet you.

I added a cognito-identity implementation.
If you can hire me, I will be able to make progress in the development of my project.

Thank you very much.


Patch:
diff --git a/moto/cognitoidentity/models.py b/moto/cognitoidentity/models.py
index d326b7a4a..d7ed596b6 100644
--- a/moto/cognitoidentity/models.py
+++ b/moto/cognitoidentity/models.py
@@ -174,5 +174,17 @@ class CognitoIdentityBackend(BaseBackend):
         """
         return json.dumps(self.pools_identities[identity_pool_id])
 
+    def list_identity_pools(self) -> str:
+        """
+        The MaxResults-parameter has not yet been implemented
+        """
+        return json.dumps(
+            {
+                "IdentityPools": [
+                    json.loads(pool.to_json()) for pool in self.identity_pools.values()
+                ]
+            }
+        )
+
 
 cognitoidentity_backends = BackendDict(CognitoIdentityBackend, "cognito-identity")
diff --git a/moto/cognitoidentity/responses.py b/moto/cognitoidentity/responses.py
index 34499ed02..ba1d157bc 100644
--- a/moto/cognitoidentity/responses.py
+++ b/moto/cognitoidentity/responses.py
@@ -80,3 +80,6 @@ class CognitoIdentityResponse(BaseResponse):
         return self.backend.list_identities(
             self._get_param("IdentityPoolId") or get_random_identity_id(self.region)
         )
+
+    def list_identity_pools(self) -> str:
+        return self.backend.list_identity_pools()
diff --git a/moto/core/utils.py b/moto/core/utils.py
index e40c98fb4..6cbc7cd3f 100644
--- a/moto/core/utils.py
+++ b/moto/core/utils.py
@@ -326,16 +326,19 @@ def extract_region_from_aws_authorization(string: str) -> Optional[str]:
     return region
 
 
-def params_sort_function(item: Tuple[str, Any]) -> Tuple[str, Any]:
+def params_sort_function(item: Tuple[str, Any]) -> Tuple[str, int, str]:
     """
-    Comparison function used to sort params appropriately taking tags non
-    alphabetical order into consideration
+    sort by <string-prefix>.member.<integer>.<string-postfix>:
+    in case there are more than 10 members, the default-string sort would lead to IndexError when parsing the content.
+
+    Note: currently considers only the first occurence of `member`, but there may be cases with nested members
     """
     key, _ = item
-    if key.startswith("Tags.member"):
-        member_num = int(key.split(".")[2])
-        return ("Tags.member", member_num)
-    return item
+
+    match = re.search(r"(.*?member)\.(\d+)(.*)", key)
+    if match:
+        return (match.group(1), int(match.group(2)), match.group(3))
+    return (key, 0, "")
 
 
 def gzip_decompress(body: bytes) -> bytes:


Test patch:
diff --git a/tests/test_autoscaling/test_autoscaling_tags.py b/tests/test_autoscaling/test_autoscaling_tags.py
index 69cb07cb0..13e82dc89 100644
--- a/tests/test_autoscaling/test_autoscaling_tags.py
+++ b/tests/test_autoscaling/test_autoscaling_tags.py
@@ -1,3 +1,5 @@
+import copy
+
 import boto3
 
 from moto import mock_autoscaling, mock_ec2
@@ -231,6 +233,39 @@ def test_describe_tags_filter_by_propgateatlaunch():
     ]
 
 
+@mock_autoscaling
+def test_create_20_tags_auto_scaling_group():
+    """test to verify that the tag-members are sorted correctly, and there is no regression for
+    https://github.com/getmoto/moto/issues/6033
+    """
+    subnet = setup_networking()["subnet1"]
+    client = boto3.client("autoscaling", region_name="us-east-1")
+    original = {
+        "ResourceId": "test_asg",
+        "PropagateAtLaunch": True,
+    }
+    tags = []
+    for i in range(0, 20):
+        cp = copy.deepcopy(original)
+        cp["Key"] = f"test_key{i}"
+        cp["Value"] = f"random-value-{i}"
+        tags.append(cp)
+    client.create_launch_configuration(
+        LaunchConfigurationName="test_launch_configuration",
+        ImageId=EXAMPLE_AMI_ID,
+        InstanceType="t2.medium",
+    )
+    client.create_auto_scaling_group(
+        AutoScalingGroupName="test_asg",
+        LaunchConfigurationName="test_launch_configuration",
+        MinSize=0,
+        MaxSize=20,
+        DesiredCapacity=5,
+        Tags=tags,
+        VPCZoneIdentifier=subnet,
+    )
+
+
 def create_asgs(client, subnet):
     _ = client.create_launch_configuration(
         LaunchConfigurationName="test_launch_configuration",
diff --git a/tests/test_cloudwatch/test_cloudwatch_boto3.py b/tests/test_cloudwatch/test_cloudwatch_boto3.py
index 5a34e9d99..2857b1110 100644
--- a/tests/test_cloudwatch/test_cloudwatch_boto3.py
+++ b/tests/test_cloudwatch/test_cloudwatch_boto3.py
@@ -1,3 +1,5 @@
+import copy
+
 import boto3
 import pytest
 
@@ -1903,3 +1905,41 @@ def test_get_metric_data_with_custom_label():
         )
         assert len(metric_result_data) == 1
         assert metric_result_data[0]["Label"] == expected_value
+
+
+@mock_cloudwatch
+def test_get_metric_data_queries():
+    """
+    verify that >= 10 queries can still be parsed
+    there was an error with the order of parsing items, leading to IndexError
+    """
+    now = datetime.utcnow().replace(microsecond=0)
+    start_time = now - timedelta(minutes=10)
+    end_time = now + timedelta(minutes=5)
+    original_query = {
+        "MetricStat": {
+            "Metric": {
+                "Namespace": "AWS/RDS",
+                "MetricName": "CPUUtilization",
+                "Dimensions": [{"Name": "DBInstanceIdentifier", "Value": ""}],
+            },
+            "Period": 1,
+            "Stat": "Average",
+            "Unit": "Percent",
+        },
+    }
+
+    queries = []
+    for i in range(0, 20):
+        query = copy.deepcopy(original_query)
+        query["Id"] = f"q{i}"
+        query["MetricStat"]["Metric"]["Dimensions"][0]["Value"] = f"id-{i}"
+        queries.append(query)
+
+    assert len(queries) == 20
+    response = boto3.client("cloudwatch", "eu-west-1").get_metric_data(
+        StartTime=start_time, EndTime=end_time, MetricDataQueries=queries
+    )
+
+    # we expect twenty results
+    assert len(response["MetricDataResults"]) == 20
diff --git a/tests/test_cognitoidentity/test_cognitoidentity.py b/tests/test_cognitoidentity/test_cognitoidentity.py
index 197cc4647..742c27a81 100644
--- a/tests/test_cognitoidentity/test_cognitoidentity.py
+++ b/tests/test_cognitoidentity/test_cognitoidentity.py
@@ -240,3 +240,15 @@ def test_list_identities():
     identities = conn.list_identities(IdentityPoolId=identity_pool_id, MaxResults=123)
     assert "IdentityPoolId" in identities and "Identities" in identities
     assert identity_id in [x["IdentityId"] for x in identities["Identities"]]
+
+
+@mock_cognitoidentity
+def test_list_identity_pools():
+    conn = boto3.client("cognito-identity", "us-west-2")
+    identity_pool_data = conn.create_identity_pool(
+        IdentityPoolName="test_identity_pool", AllowUnauthenticatedIdentities=True
+    )
+    identity_pool_id = identity_pool_data["IdentityPoolId"]
+    identity_data = conn.list_identity_pools(MaxResults=10)
+    assert identity_pool_id == identity_data["IdentityPools"][0]["IdentityPoolId"]
+    assert "test_identity_pool" == identity_data["IdentityPools"][0]["IdentityPoolName"]