Repository: getmoto/moto
Pull request #6956 (2023-10-27 18:32:48)

SES: list_identities() now supports the IdentityType-parameter


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 350d5d469..77b8a6040 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,48 @@
 Moto Changelog
 ==============
 
+4.2.7
+-----
+Docker Digest for 4.2.7: <autopopulateddigest>
+
+    New Services:
+        * Inspector2:
+            * associate_member()
+            * batch_get_account_status()
+            * create_filter()
+            * delete_filter()
+            * describe_organization_configuration()
+            * disable()
+            * disable_delegated_admin_account()
+            * disassociate_member()
+            * enable()
+            * enable_delegated_admin_account()
+            * get_member()
+            * list_delegated_admin_accounts()
+            * list_filters()
+            * list_findings()
+            * list_members()
+            * list_tags_for_resource()
+            * tag_resource()
+            * untag_resource()
+            * update_organization_configuration()
+
+    New Methods:
+        * ECR:
+            * batch_get_repository_scanning_configuration()
+            * put_registry_scanning_configuration()
+
+    Miscellaneous:
+        * Batch: submit_job() now returns the jobArn-attribute
+        * DynamoDB: execute_statement() has now support for nested WHERE-clauses with functions
+        * DynamoDB: update_item() now returns item for ConditionalCheckFailed-exceptions
+        * EC2: create_key_pair() and describe_key_pair(s)() now support tags
+        * Route53: get_hosted_zone() now returns the CallerReference
+        * S3: get_object/put_object() now accepts AccessPoint ARN's
+        * S3Control: create_access_point() now returns the correct Alias
+        * SES: list_identities() now supports the IdentityType-parameter
+        * SNS: create_platform_application has improved error handling
+
 4.2.6
 -----
 Docker Digest for 4.2.6: _sha256:ad3265531405fd48489ddee7e1fa7301b4d0f46b55daca7ba5039e73eaf70ac1_
diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index f0ff003b3..b82cc8b94 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -2344,6 +2344,7 @@
 - [ ] get_network_insights_access_scope_content
 - [X] get_password_data
 - [ ] get_reserved_instances_exchange_quote
+- [ ] get_security_groups_for_vpc
 - [ ] get_serial_console_access_status
 - [ ] get_spot_placement_scores
 - [ ] get_subnet_cidr_reservations
@@ -4857,7 +4858,7 @@
 
 ## opensearch
 <details>
-<summary>18% implemented</summary>
+<summary>17% implemented</summary>
 
 - [ ] accept_inbound_connection
 - [X] add_tags
@@ -4890,9 +4891,11 @@
 - [ ] describe_vpc_endpoints
 - [ ] dissociate_package
 - [X] get_compatible_versions
+- [ ] get_domain_maintenance_status
 - [ ] get_package_version_history
 - [ ] get_upgrade_history
 - [ ] get_upgrade_status
+- [ ] list_domain_maintenances
 - [X] list_domain_names
 - [ ] list_domains_for_package
 - [ ] list_instance_type_details
@@ -4907,6 +4910,7 @@
 - [ ] reject_inbound_connection
 - [X] remove_tags
 - [ ] revoke_vpc_endpoint_access
+- [ ] start_domain_maintenance
 - [ ] start_service_software_update
 - [X] update_domain_config
 - [ ] update_package
@@ -5643,7 +5647,7 @@
 
 ## redshift
 <details>
-<summary>24% implemented</summary>
+<summary>23% implemented</summary>
 
 - [ ] accept_reserved_node_exchange
 - [ ] add_partner
@@ -5685,6 +5689,7 @@
 - [ ] delete_hsm_client_certificate
 - [ ] delete_hsm_configuration
 - [ ] delete_partner
+- [ ] delete_resource_policy
 - [ ] delete_scheduled_action
 - [X] delete_snapshot_copy_grant
 - [ ] delete_snapshot_schedule
@@ -5713,6 +5718,7 @@
 - [ ] describe_events
 - [ ] describe_hsm_client_certificates
 - [ ] describe_hsm_configurations
+- [ ] describe_inbound_integrations
 - [ ] describe_logging_status
 - [ ] describe_node_configuration_options
 - [ ] describe_orderable_cluster_options
@@ -5737,6 +5743,7 @@
 - [ ] get_cluster_credentials_with_iam
 - [ ] get_reserved_node_exchange_configuration_options
 - [ ] get_reserved_node_exchange_offerings
+- [ ] get_resource_policy
 - [ ] modify_aqua_configuration
 - [ ] modify_authentication_profile
 - [X] modify_cluster
@@ -5756,6 +5763,7 @@
 - [ ] modify_usage_limit
 - [X] pause_cluster
 - [ ] purchase_reserved_node_offering
+- [ ] put_resource_policy
 - [ ] reboot_cluster
 - [ ] reject_data_share
 - [ ] reset_cluster_parameter_group
@@ -5828,6 +5836,7 @@
 - [ ] get_face_liveness_session_results
 - [X] get_face_search
 - [ ] get_label_detection
+- [ ] get_media_analysis_job
 - [ ] get_person_tracking
 - [ ] get_segment_detection
 - [X] get_text_detection
@@ -5836,6 +5845,7 @@
 - [ ] list_dataset_entries
 - [ ] list_dataset_labels
 - [ ] list_faces
+- [ ] list_media_analysis_jobs
 - [ ] list_project_policies
 - [ ] list_stream_processors
 - [ ] list_tags_for_resource
@@ -5851,6 +5861,7 @@
 - [ ] start_face_detection
 - [X] start_face_search
 - [ ] start_label_detection
+- [ ] start_media_analysis_job
 - [ ] start_person_tracking
 - [ ] start_project_version
 - [ ] start_segment_detection
@@ -7026,6 +7037,7 @@
 - [X] delete_document
 - [ ] delete_inventory
 - [X] delete_maintenance_window
+- [ ] delete_ops_item
 - [ ] delete_ops_metadata
 - [X] delete_parameter
 - [X] delete_parameters
@@ -7556,7 +7568,6 @@
 - frauddetector
 - fsx
 - gamelift
-- gamesparks
 - globalaccelerator
 - grafana
 - greengrassv2
diff --git a/docs/docs/services/ec2.rst b/docs/docs/services/ec2.rst
index 57b48a142..61c00625d 100644
--- a/docs/docs/services/ec2.rst
+++ b/docs/docs/services/ec2.rst
@@ -503,6 +503,7 @@ ec2
 - [ ] get_network_insights_access_scope_content
 - [X] get_password_data
 - [ ] get_reserved_instances_exchange_quote
+- [ ] get_security_groups_for_vpc
 - [ ] get_serial_console_access_status
 - [ ] get_spot_placement_scores
 - [ ] get_subnet_cidr_reservations
diff --git a/docs/docs/services/opensearch.rst b/docs/docs/services/opensearch.rst
index d87234028..6a608a250 100644
--- a/docs/docs/services/opensearch.rst
+++ b/docs/docs/services/opensearch.rst
@@ -58,9 +58,11 @@ opensearch
 - [ ] describe_vpc_endpoints
 - [ ] dissociate_package
 - [X] get_compatible_versions
+- [ ] get_domain_maintenance_status
 - [ ] get_package_version_history
 - [ ] get_upgrade_history
 - [ ] get_upgrade_status
+- [ ] list_domain_maintenances
 - [X] list_domain_names
 - [ ] list_domains_for_package
 - [ ] list_instance_type_details
@@ -75,6 +77,7 @@ opensearch
 - [ ] reject_inbound_connection
 - [X] remove_tags
 - [ ] revoke_vpc_endpoint_access
+- [ ] start_domain_maintenance
 - [ ] start_service_software_update
 - [X] update_domain_config
 - [ ] update_package
diff --git a/docs/docs/services/redshift.rst b/docs/docs/services/redshift.rst
index 28f8c766d..8c04f5611 100644
--- a/docs/docs/services/redshift.rst
+++ b/docs/docs/services/redshift.rst
@@ -65,6 +65,7 @@ redshift
 - [ ] delete_hsm_client_certificate
 - [ ] delete_hsm_configuration
 - [ ] delete_partner
+- [ ] delete_resource_policy
 - [ ] delete_scheduled_action
 - [X] delete_snapshot_copy_grant
 - [ ] delete_snapshot_schedule
@@ -93,6 +94,7 @@ redshift
 - [ ] describe_events
 - [ ] describe_hsm_client_certificates
 - [ ] describe_hsm_configurations
+- [ ] describe_inbound_integrations
 - [ ] describe_logging_status
 - [ ] describe_node_configuration_options
 - [ ] describe_orderable_cluster_options
@@ -117,6 +119,7 @@ redshift
 - [ ] get_cluster_credentials_with_iam
 - [ ] get_reserved_node_exchange_configuration_options
 - [ ] get_reserved_node_exchange_offerings
+- [ ] get_resource_policy
 - [ ] modify_aqua_configuration
 - [ ] modify_authentication_profile
 - [X] modify_cluster
@@ -136,6 +139,7 @@ redshift
 - [ ] modify_usage_limit
 - [X] pause_cluster
 - [ ] purchase_reserved_node_offering
+- [ ] put_resource_policy
 - [ ] reboot_cluster
 - [ ] reject_data_share
 - [ ] reset_cluster_parameter_group
diff --git a/docs/docs/services/rekognition.rst b/docs/docs/services/rekognition.rst
index 56ffbfd51..1c724dd27 100644
--- a/docs/docs/services/rekognition.rst
+++ b/docs/docs/services/rekognition.rst
@@ -69,6 +69,7 @@ rekognition
         
 
 - [ ] get_label_detection
+- [ ] get_media_analysis_job
 - [ ] get_person_tracking
 - [ ] get_segment_detection
 - [X] get_text_detection
@@ -81,6 +82,7 @@ rekognition
 - [ ] list_dataset_entries
 - [ ] list_dataset_labels
 - [ ] list_faces
+- [ ] list_media_analysis_jobs
 - [ ] list_project_policies
 - [ ] list_stream_processors
 - [ ] list_tags_for_resource
@@ -96,6 +98,7 @@ rekognition
 - [ ] start_face_detection
 - [X] start_face_search
 - [ ] start_label_detection
+- [ ] start_media_analysis_job
 - [ ] start_person_tracking
 - [ ] start_project_version
 - [ ] start_segment_detection
diff --git a/docs/docs/services/ssm.rst b/docs/docs/services/ssm.rst
index 8dde3357a..8a3349539 100644
--- a/docs/docs/services/ssm.rst
+++ b/docs/docs/services/ssm.rst
@@ -57,6 +57,7 @@ ssm
         Assumes the provided WindowId exists. No error handling has been implemented yet.
         
 
+- [ ] delete_ops_item
 - [ ] delete_ops_metadata
 - [X] delete_parameter
 - [X] delete_parameters
diff --git a/moto/ses/models.py b/moto/ses/models.py
index 0427b4105..f40c853fe 100644
--- a/moto/ses/models.py
+++ b/moto/ses/models.py
@@ -182,7 +182,11 @@ class SESBackend(BaseBackend):
         if domain.lower() not in self.domains:
             self.domains.append(domain.lower())
 
-    def list_identities(self) -> List[str]:
+    def list_identities(self, identity_type: str) -> List[str]:
+        if identity_type == "Domain":
+            return self.domains
+        if identity_type == "EmailAddress":
+            return self.addresses
         return self.domains + self.addresses
 
     def list_verified_email_addresses(self) -> List[str]:
diff --git a/moto/ses/responses.py b/moto/ses/responses.py
index ceef3ae19..4d35dedb6 100644
--- a/moto/ses/responses.py
+++ b/moto/ses/responses.py
@@ -3,6 +3,7 @@ from typing import Any, Dict, List
 
 from moto.core.responses import BaseResponse
 from moto.core.utils import utcnow
+from .exceptions import ValidationError
 from .models import ses_backends, SESBackend
 
 
@@ -27,7 +28,12 @@ class EmailResponse(BaseResponse):
         return template.render()
 
     def list_identities(self) -> str:
-        identities = self.backend.list_identities()
+        identity_type = self._get_param("IdentityType")
+        if identity_type not in [None, "EmailAddress", "Domain"]:
+            raise ValidationError(
+                f"Value '{identity_type}' at 'identityType' failed to satisfy constraint: Member must satisfy enum value set: [Domain, EmailAddress]"
+            )
+        identities = self.backend.list_identities(identity_type)
         template = self.response_template(LIST_IDENTITIES_RESPONSE)
         return template.render(identities=identities)
 


Test patch:
diff --git a/tests/test_ses/test_ses_boto3.py b/tests/test_ses/test_ses_boto3.py
index c06408c17..770839895 100644
--- a/tests/test_ses/test_ses_boto3.py
+++ b/tests/test_ses/test_ses_boto3.py
@@ -11,13 +11,32 @@ from . import ses_aws_verified
 
 
 @mock_ses
-def test_verify_email_identity():
+def test_list_verified_identities():
     conn = boto3.client("ses", region_name="us-east-1")
     conn.verify_email_identity(EmailAddress="test@example.com")
 
-    identities = conn.list_identities()
-    address = identities["Identities"][0]
-    assert address == "test@example.com"
+    identities = conn.list_identities()["Identities"]
+    assert identities == ["test@example.com"]
+
+    conn.verify_domain_dkim(Domain="domain1.com")
+    conn.verify_domain_identity(Domain="domain2.com")
+
+    identities = conn.list_identities()["Identities"]
+    assert identities == ["domain1.com", "domain2.com", "test@example.com"]
+
+    identities = conn.list_identities(IdentityType="EmailAddress")["Identities"]
+    assert identities == ["test@example.com"]
+
+    identities = conn.list_identities(IdentityType="Domain")["Identities"]
+    assert identities == ["domain1.com", "domain2.com"]
+
+    with pytest.raises(ClientError) as exc:
+        conn.list_identities(IdentityType="Unknown")
+    err = exc.value.response["Error"]
+    assert (
+        err["Message"]
+        == "Value 'Unknown' at 'identityType' failed to satisfy constraint: Member must satisfy enum value set: [Domain, EmailAddress]"
+    )
 
 
 @mock_ses
@@ -50,18 +69,6 @@ def test_verify_email_address():
     assert email == "test@example.com"
 
 
-@mock_ses
-def test_domain_verify():
-    conn = boto3.client("ses", region_name="us-east-1")
-
-    conn.verify_domain_dkim(Domain="domain1.com")
-    conn.verify_domain_identity(Domain="domain2.com")
-
-    identities = conn.list_identities()
-    domains = list(identities["Identities"])
-    assert domains == ["domain1.com", "domain2.com"]
-
-
 @mock_ses
 def test_delete_identity():
     conn = boto3.client("ses", region_name="us-east-1")