SES v2 (#6259)
* feat: add ses v2 * feat: move wrap v1 logic into v2 api * feat: v2 api on v2 url * chore: types * chore: linting * feat: raw emails * chore: linting * feat: add list_contacts * fix: urls need to be explicit for this to work with moto server * chore: linting * chore: remodel * chore: rework * chore: cleanup * chore: fix test * chore: sort out mypy * feat: add contact lists * fix: new url for server mode * feat: create and delete * chore: linting * chore: linting * chore: refactor * chore: match errors with real responses * chore: linting * chore: run implementation coverage script * refactor: easier, faster look ups with dicts * refactor: contact is now a child of contactlist * tests: contactlists return 200 if empty, contacts do not * chore: update botocore and run implementation coverage script * refactor: add matching *_contact methods to backend model so coverage script can detect them
This commit is contained in:
parent
65cf66dbcf
commit
6f170410e8
@ -358,11 +358,13 @@
|
||||
|
||||
## athena
|
||||
<details>
|
||||
<summary>26% implemented</summary>
|
||||
<summary>23% implemented</summary>
|
||||
|
||||
- [ ] batch_get_named_query
|
||||
- [ ] batch_get_prepared_statement
|
||||
- [ ] batch_get_query_execution
|
||||
- [ ] cancel_capacity_reservation
|
||||
- [ ] create_capacity_reservation
|
||||
- [X] create_data_catalog
|
||||
- [X] create_named_query
|
||||
- [ ] create_notebook
|
||||
@ -378,6 +380,8 @@
|
||||
- [ ] get_calculation_execution
|
||||
- [ ] get_calculation_execution_code
|
||||
- [ ] get_calculation_execution_status
|
||||
- [ ] get_capacity_assignment_configuration
|
||||
- [ ] get_capacity_reservation
|
||||
- [X] get_data_catalog
|
||||
- [ ] get_database
|
||||
- [X] get_named_query
|
||||
@ -393,6 +397,7 @@
|
||||
- [ ] import_notebook
|
||||
- [ ] list_application_dpu_sizes
|
||||
- [ ] list_calculation_executions
|
||||
- [ ] list_capacity_reservations
|
||||
- [X] list_data_catalogs
|
||||
- [ ] list_databases
|
||||
- [ ] list_engine_versions
|
||||
@ -406,6 +411,7 @@
|
||||
- [ ] list_table_metadata
|
||||
- [ ] list_tags_for_resource
|
||||
- [X] list_work_groups
|
||||
- [ ] put_capacity_assignment_configuration
|
||||
- [ ] start_calculation_execution
|
||||
- [X] start_query_execution
|
||||
- [ ] start_session
|
||||
@ -414,6 +420,7 @@
|
||||
- [ ] tag_resource
|
||||
- [ ] terminate_session
|
||||
- [ ] untag_resource
|
||||
- [ ] update_capacity_reservation
|
||||
- [ ] update_data_catalog
|
||||
- [ ] update_named_query
|
||||
- [ ] update_notebook
|
||||
@ -1453,8 +1460,9 @@
|
||||
|
||||
## datasync
|
||||
<details>
|
||||
<summary>13% implemented</summary>
|
||||
<summary>10% implemented</summary>
|
||||
|
||||
- [ ] add_storage_system
|
||||
- [X] cancel_task_execution
|
||||
- [ ] create_agent
|
||||
- [ ] create_location_efs
|
||||
@ -1472,6 +1480,7 @@
|
||||
- [X] delete_location
|
||||
- [X] delete_task
|
||||
- [ ] describe_agent
|
||||
- [ ] describe_discovery_job
|
||||
- [ ] describe_location_efs
|
||||
- [ ] describe_location_fsx_lustre
|
||||
- [ ] describe_location_fsx_ontap
|
||||
@ -1482,21 +1491,32 @@
|
||||
- [ ] describe_location_object_storage
|
||||
- [ ] describe_location_s3
|
||||
- [ ] describe_location_smb
|
||||
- [ ] describe_storage_system
|
||||
- [ ] describe_storage_system_resource_metrics
|
||||
- [ ] describe_storage_system_resources
|
||||
- [ ] describe_task
|
||||
- [ ] describe_task_execution
|
||||
- [ ] generate_recommendations
|
||||
- [ ] list_agents
|
||||
- [ ] list_discovery_jobs
|
||||
- [ ] list_locations
|
||||
- [ ] list_storage_systems
|
||||
- [ ] list_tags_for_resource
|
||||
- [ ] list_task_executions
|
||||
- [ ] list_tasks
|
||||
- [ ] remove_storage_system
|
||||
- [ ] start_discovery_job
|
||||
- [X] start_task_execution
|
||||
- [ ] stop_discovery_job
|
||||
- [ ] tag_resource
|
||||
- [ ] untag_resource
|
||||
- [ ] update_agent
|
||||
- [ ] update_discovery_job
|
||||
- [ ] update_location_hdfs
|
||||
- [ ] update_location_nfs
|
||||
- [ ] update_location_object_storage
|
||||
- [ ] update_location_smb
|
||||
- [ ] update_storage_system
|
||||
- [X] update_task
|
||||
- [ ] update_task_execution
|
||||
</details>
|
||||
@ -2831,7 +2851,7 @@
|
||||
|
||||
## emr-containers
|
||||
<details>
|
||||
<summary>42% implemented</summary>
|
||||
<summary>40% implemented</summary>
|
||||
|
||||
- [X] cancel_job_run
|
||||
- [ ] create_job_template
|
||||
@ -2844,6 +2864,7 @@
|
||||
- [ ] describe_job_template
|
||||
- [ ] describe_managed_endpoint
|
||||
- [X] describe_virtual_cluster
|
||||
- [ ] get_managed_endpoint_session_credentials
|
||||
- [X] list_job_runs
|
||||
- [ ] list_job_templates
|
||||
- [ ] list_managed_endpoints
|
||||
@ -3121,7 +3142,7 @@
|
||||
|
||||
## glue
|
||||
<details>
|
||||
<summary>26% implemented</summary>
|
||||
<summary>30% implemented</summary>
|
||||
|
||||
- [X] batch_create_partition
|
||||
- [ ] batch_delete_connection
|
||||
@ -3135,7 +3156,7 @@
|
||||
- [ ] batch_get_dev_endpoints
|
||||
- [X] batch_get_jobs
|
||||
- [X] batch_get_partition
|
||||
- [ ] batch_get_triggers
|
||||
- [X] batch_get_triggers
|
||||
- [ ] batch_get_workflows
|
||||
- [ ] batch_stop_job_run
|
||||
- [X] batch_update_partition
|
||||
@ -3162,7 +3183,7 @@
|
||||
- [ ] create_security_configuration
|
||||
- [ ] create_session
|
||||
- [X] create_table
|
||||
- [ ] create_trigger
|
||||
- [X] create_trigger
|
||||
- [ ] create_user_defined_function
|
||||
- [ ] create_workflow
|
||||
- [ ] delete_blueprint
|
||||
@ -3187,7 +3208,7 @@
|
||||
- [ ] delete_session
|
||||
- [X] delete_table
|
||||
- [X] delete_table_version
|
||||
- [ ] delete_trigger
|
||||
- [X] delete_trigger
|
||||
- [ ] delete_user_defined_function
|
||||
- [ ] delete_workflow
|
||||
- [ ] get_blueprint
|
||||
@ -3244,8 +3265,8 @@
|
||||
- [X] get_table_versions
|
||||
- [X] get_tables
|
||||
- [X] get_tags
|
||||
- [ ] get_trigger
|
||||
- [ ] get_triggers
|
||||
- [X] get_trigger
|
||||
- [X] get_triggers
|
||||
- [ ] get_unfiltered_partition_metadata
|
||||
- [ ] get_unfiltered_partitions_metadata
|
||||
- [ ] get_unfiltered_table_metadata
|
||||
@ -3272,7 +3293,7 @@
|
||||
- [ ] list_schemas
|
||||
- [ ] list_sessions
|
||||
- [ ] list_statements
|
||||
- [ ] list_triggers
|
||||
- [X] list_triggers
|
||||
- [ ] list_workflows
|
||||
- [ ] put_data_catalog_encryption_settings
|
||||
- [ ] put_resource_policy
|
||||
@ -3295,12 +3316,12 @@
|
||||
- [X] start_job_run
|
||||
- [ ] start_ml_evaluation_task_run
|
||||
- [ ] start_ml_labeling_set_generation_task_run
|
||||
- [ ] start_trigger
|
||||
- [X] start_trigger
|
||||
- [ ] start_workflow_run
|
||||
- [X] stop_crawler
|
||||
- [ ] stop_crawler_schedule
|
||||
- [ ] stop_session
|
||||
- [ ] stop_trigger
|
||||
- [X] stop_trigger
|
||||
- [ ] stop_workflow_run
|
||||
- [X] tag_resource
|
||||
- [X] untag_resource
|
||||
@ -3482,6 +3503,7 @@
|
||||
- [ ] list_publishing_destinations
|
||||
- [ ] list_tags_for_resource
|
||||
- [ ] list_threat_intel_sets
|
||||
- [ ] start_malware_scan
|
||||
- [ ] start_monitoring_members
|
||||
- [ ] stop_monitoring_members
|
||||
- [ ] tag_resource
|
||||
@ -4863,7 +4885,7 @@
|
||||
|
||||
## pinpoint
|
||||
<details>
|
||||
<summary>10% implemented</summary>
|
||||
<summary>9% implemented</summary>
|
||||
|
||||
- [X] create_app
|
||||
- [ ] create_campaign
|
||||
@ -4932,6 +4954,9 @@
|
||||
- [ ] get_journey_date_range_kpi
|
||||
- [ ] get_journey_execution_activity_metrics
|
||||
- [ ] get_journey_execution_metrics
|
||||
- [ ] get_journey_run_execution_activity_metrics
|
||||
- [ ] get_journey_run_execution_metrics
|
||||
- [ ] get_journey_runs
|
||||
- [ ] get_push_template
|
||||
- [ ] get_recommender_configuration
|
||||
- [ ] get_recommender_configurations
|
||||
@ -6412,6 +6437,98 @@
|
||||
- [X] verify_email_identity
|
||||
</details>
|
||||
|
||||
## sesv2
|
||||
<details>
|
||||
<summary>10% implemented</summary>
|
||||
|
||||
- [ ] batch_get_metric_data
|
||||
- [ ] create_configuration_set
|
||||
- [ ] create_configuration_set_event_destination
|
||||
- [X] create_contact
|
||||
- [X] create_contact_list
|
||||
- [ ] create_custom_verification_email_template
|
||||
- [ ] create_dedicated_ip_pool
|
||||
- [ ] create_deliverability_test_report
|
||||
- [ ] create_email_identity
|
||||
- [ ] create_email_identity_policy
|
||||
- [ ] create_email_template
|
||||
- [ ] create_import_job
|
||||
- [ ] delete_configuration_set
|
||||
- [ ] delete_configuration_set_event_destination
|
||||
- [X] delete_contact
|
||||
- [X] delete_contact_list
|
||||
- [ ] delete_custom_verification_email_template
|
||||
- [ ] delete_dedicated_ip_pool
|
||||
- [ ] delete_email_identity
|
||||
- [ ] delete_email_identity_policy
|
||||
- [ ] delete_email_template
|
||||
- [ ] delete_suppressed_destination
|
||||
- [ ] get_account
|
||||
- [ ] get_blacklist_reports
|
||||
- [ ] get_configuration_set
|
||||
- [ ] get_configuration_set_event_destinations
|
||||
- [X] get_contact
|
||||
- [X] get_contact_list
|
||||
- [ ] get_custom_verification_email_template
|
||||
- [ ] get_dedicated_ip
|
||||
- [ ] get_dedicated_ip_pool
|
||||
- [ ] get_dedicated_ips
|
||||
- [ ] get_deliverability_dashboard_options
|
||||
- [ ] get_deliverability_test_report
|
||||
- [ ] get_domain_deliverability_campaign
|
||||
- [ ] get_domain_statistics_report
|
||||
- [ ] get_email_identity
|
||||
- [ ] get_email_identity_policies
|
||||
- [ ] get_email_template
|
||||
- [ ] get_import_job
|
||||
- [ ] get_suppressed_destination
|
||||
- [ ] list_configuration_sets
|
||||
- [X] list_contact_lists
|
||||
- [X] list_contacts
|
||||
- [ ] list_custom_verification_email_templates
|
||||
- [ ] list_dedicated_ip_pools
|
||||
- [ ] list_deliverability_test_reports
|
||||
- [ ] list_domain_deliverability_campaigns
|
||||
- [ ] list_email_identities
|
||||
- [ ] list_email_templates
|
||||
- [ ] list_import_jobs
|
||||
- [ ] list_recommendations
|
||||
- [ ] list_suppressed_destinations
|
||||
- [ ] list_tags_for_resource
|
||||
- [ ] put_account_dedicated_ip_warmup_attributes
|
||||
- [ ] put_account_details
|
||||
- [ ] put_account_sending_attributes
|
||||
- [ ] put_account_suppression_attributes
|
||||
- [ ] put_account_vdm_attributes
|
||||
- [ ] put_configuration_set_delivery_options
|
||||
- [ ] put_configuration_set_reputation_options
|
||||
- [ ] put_configuration_set_sending_options
|
||||
- [ ] put_configuration_set_suppression_options
|
||||
- [ ] put_configuration_set_tracking_options
|
||||
- [ ] put_configuration_set_vdm_options
|
||||
- [ ] put_dedicated_ip_in_pool
|
||||
- [ ] put_dedicated_ip_warmup_attributes
|
||||
- [ ] put_deliverability_dashboard_option
|
||||
- [ ] put_email_identity_configuration_set_attributes
|
||||
- [ ] put_email_identity_dkim_attributes
|
||||
- [ ] put_email_identity_dkim_signing_attributes
|
||||
- [ ] put_email_identity_feedback_attributes
|
||||
- [ ] put_email_identity_mail_from_attributes
|
||||
- [ ] put_suppressed_destination
|
||||
- [ ] send_bulk_email
|
||||
- [ ] send_custom_verification_email
|
||||
- [X] send_email
|
||||
- [ ] tag_resource
|
||||
- [ ] test_render_email_template
|
||||
- [ ] untag_resource
|
||||
- [ ] update_configuration_set_event_destination
|
||||
- [ ] update_contact
|
||||
- [ ] update_contact_list
|
||||
- [ ] update_custom_verification_email_template
|
||||
- [ ] update_email_identity_policy
|
||||
- [ ] update_email_template
|
||||
</details>
|
||||
|
||||
## signer
|
||||
<details>
|
||||
<summary>23% implemented</summary>
|
||||
@ -7113,6 +7230,7 @@
|
||||
- omics
|
||||
- opensearchserverless
|
||||
- opsworkscm
|
||||
- osis
|
||||
- outposts
|
||||
- panorama
|
||||
- personalize-events
|
||||
@ -7152,7 +7270,6 @@
|
||||
- serverlessrepo
|
||||
- servicecatalog
|
||||
- servicecatalog-appregistry
|
||||
- sesv2
|
||||
- shield
|
||||
- simspaceweaver
|
||||
- sms
|
||||
|
@ -28,6 +28,8 @@ athena
|
||||
- [ ] batch_get_named_query
|
||||
- [ ] batch_get_prepared_statement
|
||||
- [ ] batch_get_query_execution
|
||||
- [ ] cancel_capacity_reservation
|
||||
- [ ] create_capacity_reservation
|
||||
- [X] create_data_catalog
|
||||
- [X] create_named_query
|
||||
- [ ] create_notebook
|
||||
@ -43,6 +45,8 @@ athena
|
||||
- [ ] get_calculation_execution
|
||||
- [ ] get_calculation_execution_code
|
||||
- [ ] get_calculation_execution_status
|
||||
- [ ] get_capacity_assignment_configuration
|
||||
- [ ] get_capacity_reservation
|
||||
- [X] get_data_catalog
|
||||
- [ ] get_database
|
||||
- [X] get_named_query
|
||||
@ -104,6 +108,7 @@ athena
|
||||
- [ ] import_notebook
|
||||
- [ ] list_application_dpu_sizes
|
||||
- [ ] list_calculation_executions
|
||||
- [ ] list_capacity_reservations
|
||||
- [X] list_data_catalogs
|
||||
- [ ] list_databases
|
||||
- [ ] list_engine_versions
|
||||
@ -117,6 +122,7 @@ athena
|
||||
- [ ] list_table_metadata
|
||||
- [ ] list_tags_for_resource
|
||||
- [X] list_work_groups
|
||||
- [ ] put_capacity_assignment_configuration
|
||||
- [ ] start_calculation_execution
|
||||
- [X] start_query_execution
|
||||
- [ ] start_session
|
||||
@ -125,6 +131,7 @@ athena
|
||||
- [ ] tag_resource
|
||||
- [ ] terminate_session
|
||||
- [ ] untag_resource
|
||||
- [ ] update_capacity_reservation
|
||||
- [ ] update_data_catalog
|
||||
- [ ] update_named_query
|
||||
- [ ] update_notebook
|
||||
|
@ -25,6 +25,7 @@ datasync
|
||||
|
||||
|start-h3| Implemented features for this service |end-h3|
|
||||
|
||||
- [ ] add_storage_system
|
||||
- [X] cancel_task_execution
|
||||
- [ ] create_agent
|
||||
- [ ] create_location_efs
|
||||
@ -42,6 +43,7 @@ datasync
|
||||
- [X] delete_location
|
||||
- [X] delete_task
|
||||
- [ ] describe_agent
|
||||
- [ ] describe_discovery_job
|
||||
- [ ] describe_location_efs
|
||||
- [ ] describe_location_fsx_lustre
|
||||
- [ ] describe_location_fsx_ontap
|
||||
@ -52,21 +54,32 @@ datasync
|
||||
- [ ] describe_location_object_storage
|
||||
- [ ] describe_location_s3
|
||||
- [ ] describe_location_smb
|
||||
- [ ] describe_storage_system
|
||||
- [ ] describe_storage_system_resource_metrics
|
||||
- [ ] describe_storage_system_resources
|
||||
- [ ] describe_task
|
||||
- [ ] describe_task_execution
|
||||
- [ ] generate_recommendations
|
||||
- [ ] list_agents
|
||||
- [ ] list_discovery_jobs
|
||||
- [ ] list_locations
|
||||
- [ ] list_storage_systems
|
||||
- [ ] list_tags_for_resource
|
||||
- [ ] list_task_executions
|
||||
- [ ] list_tasks
|
||||
- [ ] remove_storage_system
|
||||
- [ ] start_discovery_job
|
||||
- [X] start_task_execution
|
||||
- [ ] stop_discovery_job
|
||||
- [ ] tag_resource
|
||||
- [ ] untag_resource
|
||||
- [ ] update_agent
|
||||
- [ ] update_discovery_job
|
||||
- [ ] update_location_hdfs
|
||||
- [ ] update_location_nfs
|
||||
- [ ] update_location_object_storage
|
||||
- [ ] update_location_smb
|
||||
- [ ] update_storage_system
|
||||
- [X] update_task
|
||||
- [ ] update_task_execution
|
||||
|
||||
|
@ -38,6 +38,7 @@ emr-containers
|
||||
- [ ] describe_job_template
|
||||
- [ ] describe_managed_endpoint
|
||||
- [X] describe_virtual_cluster
|
||||
- [ ] get_managed_endpoint_session_credentials
|
||||
- [X] list_job_runs
|
||||
- [ ] list_job_templates
|
||||
- [ ] list_managed_endpoints
|
||||
|
@ -37,7 +37,7 @@ glue
|
||||
- [ ] batch_get_dev_endpoints
|
||||
- [X] batch_get_jobs
|
||||
- [X] batch_get_partition
|
||||
- [ ] batch_get_triggers
|
||||
- [X] batch_get_triggers
|
||||
- [ ] batch_get_workflows
|
||||
- [ ] batch_stop_job_run
|
||||
- [X] batch_update_partition
|
||||
@ -68,7 +68,7 @@ glue
|
||||
- [ ] create_security_configuration
|
||||
- [ ] create_session
|
||||
- [X] create_table
|
||||
- [ ] create_trigger
|
||||
- [X] create_trigger
|
||||
- [ ] create_user_defined_function
|
||||
- [ ] create_workflow
|
||||
- [ ] delete_blueprint
|
||||
@ -93,7 +93,7 @@ glue
|
||||
- [ ] delete_session
|
||||
- [X] delete_table
|
||||
- [X] delete_table_version
|
||||
- [ ] delete_trigger
|
||||
- [X] delete_trigger
|
||||
- [ ] delete_user_defined_function
|
||||
- [ ] delete_workflow
|
||||
- [ ] get_blueprint
|
||||
@ -162,8 +162,8 @@ glue
|
||||
- [X] get_table_versions
|
||||
- [X] get_tables
|
||||
- [X] get_tags
|
||||
- [ ] get_trigger
|
||||
- [ ] get_triggers
|
||||
- [X] get_trigger
|
||||
- [X] get_triggers
|
||||
- [ ] get_unfiltered_partition_metadata
|
||||
- [ ] get_unfiltered_partitions_metadata
|
||||
- [ ] get_unfiltered_table_metadata
|
||||
@ -190,7 +190,7 @@ glue
|
||||
- [ ] list_schemas
|
||||
- [ ] list_sessions
|
||||
- [ ] list_statements
|
||||
- [ ] list_triggers
|
||||
- [X] list_triggers
|
||||
- [ ] list_workflows
|
||||
- [ ] put_data_catalog_encryption_settings
|
||||
- [ ] put_resource_policy
|
||||
@ -213,12 +213,12 @@ glue
|
||||
- [X] start_job_run
|
||||
- [ ] start_ml_evaluation_task_run
|
||||
- [ ] start_ml_labeling_set_generation_task_run
|
||||
- [ ] start_trigger
|
||||
- [X] start_trigger
|
||||
- [ ] start_workflow_run
|
||||
- [X] stop_crawler
|
||||
- [ ] stop_crawler_schedule
|
||||
- [ ] stop_session
|
||||
- [ ] stop_trigger
|
||||
- [X] stop_trigger
|
||||
- [ ] stop_workflow_run
|
||||
- [X] tag_resource
|
||||
- [X] untag_resource
|
||||
|
@ -86,6 +86,7 @@ guardduty
|
||||
- [ ] list_publishing_destinations
|
||||
- [ ] list_tags_for_resource
|
||||
- [ ] list_threat_intel_sets
|
||||
- [ ] start_malware_scan
|
||||
- [ ] start_monitoring_members
|
||||
- [ ] stop_monitoring_members
|
||||
- [ ] tag_resource
|
||||
|
@ -98,6 +98,9 @@ pinpoint
|
||||
- [ ] get_journey_date_range_kpi
|
||||
- [ ] get_journey_execution_activity_metrics
|
||||
- [ ] get_journey_execution_metrics
|
||||
- [ ] get_journey_run_execution_activity_metrics
|
||||
- [ ] get_journey_run_execution_metrics
|
||||
- [ ] get_journey_runs
|
||||
- [ ] get_push_template
|
||||
- [ ] get_recommender_configuration
|
||||
- [ ] get_recommender_configurations
|
||||
|
116
docs/docs/services/sesv2.rst
Normal file
116
docs/docs/services/sesv2.rst
Normal file
@ -0,0 +1,116 @@
|
||||
.. _implementedservice_sesv2:
|
||||
|
||||
.. |start-h3| raw:: html
|
||||
|
||||
<h3>
|
||||
|
||||
.. |end-h3| raw:: html
|
||||
|
||||
</h3>
|
||||
|
||||
=====
|
||||
sesv2
|
||||
=====
|
||||
|
||||
.. autoclass:: moto.sesv2.models.SESV2Backend
|
||||
|
||||
|start-h3| Example usage |end-h3|
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
@mock_sesv2
|
||||
def test_sesv2_behaviour:
|
||||
boto3.client("sesv2")
|
||||
...
|
||||
|
||||
|
||||
|
||||
|start-h3| Implemented features for this service |end-h3|
|
||||
|
||||
- [ ] batch_get_metric_data
|
||||
- [ ] create_configuration_set
|
||||
- [ ] create_configuration_set_event_destination
|
||||
- [X] create_contact
|
||||
- [X] create_contact_list
|
||||
- [ ] create_custom_verification_email_template
|
||||
- [ ] create_dedicated_ip_pool
|
||||
- [ ] create_deliverability_test_report
|
||||
- [ ] create_email_identity
|
||||
- [ ] create_email_identity_policy
|
||||
- [ ] create_email_template
|
||||
- [ ] create_import_job
|
||||
- [ ] delete_configuration_set
|
||||
- [ ] delete_configuration_set_event_destination
|
||||
- [X] delete_contact
|
||||
- [X] delete_contact_list
|
||||
- [ ] delete_custom_verification_email_template
|
||||
- [ ] delete_dedicated_ip_pool
|
||||
- [ ] delete_email_identity
|
||||
- [ ] delete_email_identity_policy
|
||||
- [ ] delete_email_template
|
||||
- [ ] delete_suppressed_destination
|
||||
- [ ] get_account
|
||||
- [ ] get_blacklist_reports
|
||||
- [ ] get_configuration_set
|
||||
- [ ] get_configuration_set_event_destinations
|
||||
- [X] get_contact
|
||||
- [X] get_contact_list
|
||||
- [ ] get_custom_verification_email_template
|
||||
- [ ] get_dedicated_ip
|
||||
- [ ] get_dedicated_ip_pool
|
||||
- [ ] get_dedicated_ips
|
||||
- [ ] get_deliverability_dashboard_options
|
||||
- [ ] get_deliverability_test_report
|
||||
- [ ] get_domain_deliverability_campaign
|
||||
- [ ] get_domain_statistics_report
|
||||
- [ ] get_email_identity
|
||||
- [ ] get_email_identity_policies
|
||||
- [ ] get_email_template
|
||||
- [ ] get_import_job
|
||||
- [ ] get_suppressed_destination
|
||||
- [ ] list_configuration_sets
|
||||
- [X] list_contact_lists
|
||||
- [X] list_contacts
|
||||
- [ ] list_custom_verification_email_templates
|
||||
- [ ] list_dedicated_ip_pools
|
||||
- [ ] list_deliverability_test_reports
|
||||
- [ ] list_domain_deliverability_campaigns
|
||||
- [ ] list_email_identities
|
||||
- [ ] list_email_templates
|
||||
- [ ] list_import_jobs
|
||||
- [ ] list_recommendations
|
||||
- [ ] list_suppressed_destinations
|
||||
- [ ] list_tags_for_resource
|
||||
- [ ] put_account_dedicated_ip_warmup_attributes
|
||||
- [ ] put_account_details
|
||||
- [ ] put_account_sending_attributes
|
||||
- [ ] put_account_suppression_attributes
|
||||
- [ ] put_account_vdm_attributes
|
||||
- [ ] put_configuration_set_delivery_options
|
||||
- [ ] put_configuration_set_reputation_options
|
||||
- [ ] put_configuration_set_sending_options
|
||||
- [ ] put_configuration_set_suppression_options
|
||||
- [ ] put_configuration_set_tracking_options
|
||||
- [ ] put_configuration_set_vdm_options
|
||||
- [ ] put_dedicated_ip_in_pool
|
||||
- [ ] put_dedicated_ip_warmup_attributes
|
||||
- [ ] put_deliverability_dashboard_option
|
||||
- [ ] put_email_identity_configuration_set_attributes
|
||||
- [ ] put_email_identity_dkim_attributes
|
||||
- [ ] put_email_identity_dkim_signing_attributes
|
||||
- [ ] put_email_identity_feedback_attributes
|
||||
- [ ] put_email_identity_mail_from_attributes
|
||||
- [ ] put_suppressed_destination
|
||||
- [ ] send_bulk_email
|
||||
- [ ] send_custom_verification_email
|
||||
- [X] send_email
|
||||
- [ ] tag_resource
|
||||
- [ ] test_render_email_template
|
||||
- [ ] untag_resource
|
||||
- [ ] update_configuration_set_event_destination
|
||||
- [ ] update_contact
|
||||
- [ ] update_contact_list
|
||||
- [ ] update_custom_verification_email_template
|
||||
- [ ] update_email_identity_policy
|
||||
- [ ] update_email_template
|
||||
|
@ -109,10 +109,6 @@ ssm
|
||||
- [ ] get_automation_execution
|
||||
- [ ] get_calendar_state
|
||||
- [X] get_command_invocation
|
||||
|
||||
https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_GetCommandInvocation.html
|
||||
|
||||
|
||||
- [ ] get_connection_status
|
||||
- [ ] get_default_patch_baseline
|
||||
- [ ] get_deployable_patch_snapshot_for_instance
|
||||
@ -148,7 +144,7 @@ ssm
|
||||
- [ ] list_command_invocations
|
||||
- [X] list_commands
|
||||
|
||||
https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_ListCommands.html
|
||||
Pagination and the Filters-parameter is not yet implemented
|
||||
|
||||
|
||||
- [ ] list_compliance_items
|
||||
|
@ -150,6 +150,7 @@ mock_servicequotas = lazy_load(
|
||||
".servicequotas", "mock_servicequotas", boto3_name="service-quotas"
|
||||
)
|
||||
mock_ses = lazy_load(".ses", "mock_ses")
|
||||
mock_sesv2 = lazy_load(".sesv2", "mock_sesv2")
|
||||
mock_servicediscovery = lazy_load(".servicediscovery", "mock_servicediscovery")
|
||||
mock_signer = lazy_load(".signer", "mock_signer")
|
||||
mock_sns = lazy_load(".sns", "mock_sns")
|
||||
|
@ -158,6 +158,8 @@ backend_url_patterns = [
|
||||
("service-quotas", re.compile("https?://servicequotas\\.(.+)\\.amazonaws\\.com")),
|
||||
("ses", re.compile("https?://email\\.(.+)\\.amazonaws\\.com")),
|
||||
("ses", re.compile("https?://ses\\.(.+)\\.amazonaws\\.com")),
|
||||
("sesv2", re.compile("https?://ses\\.(.+)\\.amazonaws\\.com")),
|
||||
("sesv2", re.compile("https?://email\\.(.+)\\.amazonaws\\.com")),
|
||||
("signer", re.compile("https?://signer\\.(.+)\\.amazonaws\\.com")),
|
||||
("sns", re.compile("https?://sns\\.(.+)\\.amazonaws\\.com")),
|
||||
("sqs", re.compile("https?://(.*\\.)?(queue|sqs)\\.(.*\\.)?amazonaws\\.com")),
|
||||
|
@ -153,6 +153,8 @@ class DomainDispatcherApplication:
|
||||
path.startswith("/v20180820/") or "s3-control" in environ["HTTP_HOST"]
|
||||
):
|
||||
host = "s3control"
|
||||
elif service == "ses" and path.startswith("/v2/"):
|
||||
host = "sesv2"
|
||||
else:
|
||||
host = f"{service}.{region}.amazonaws.com"
|
||||
|
||||
|
5
moto/sesv2/__init__.py
Normal file
5
moto/sesv2/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""sesv2 module initialization; sets value for base decorator."""
|
||||
from .models import sesv2_backends
|
||||
from ..core.models import base_decorator
|
||||
|
||||
mock_sesv2 = base_decorator(sesv2_backends)
|
8
moto/sesv2/exceptions.py
Normal file
8
moto/sesv2/exceptions.py
Normal file
@ -0,0 +1,8 @@
|
||||
from moto.core.exceptions import JsonRESTError
|
||||
|
||||
|
||||
class NotFoundException(JsonRESTError):
|
||||
code = 404
|
||||
|
||||
def __init__(self, message: str):
|
||||
super().__init__("NotFoundException", message)
|
166
moto/sesv2/models.py
Normal file
166
moto/sesv2/models.py
Normal file
@ -0,0 +1,166 @@
|
||||
"""SESV2Backend class with methods for supported APIs."""
|
||||
|
||||
from datetime import datetime as dt
|
||||
from moto.core import BackendDict, BaseBackend, BaseModel
|
||||
from ..ses.models import ses_backends, Message, RawMessage
|
||||
from typing import Dict, List, Any
|
||||
from .exceptions import NotFoundException
|
||||
from moto.core.utils import iso_8601_datetime_with_milliseconds
|
||||
|
||||
|
||||
class Contact(BaseModel):
|
||||
def __init__(
|
||||
self,
|
||||
contact_list_name: str,
|
||||
email_address: str,
|
||||
topic_preferences: List[Dict[str, str]],
|
||||
unsubscribe_all: bool,
|
||||
) -> None:
|
||||
self.contact_list_name = contact_list_name
|
||||
self.email_address = email_address
|
||||
self.topic_default_preferences: List[Dict[str, str]] = []
|
||||
self.topic_preferences = topic_preferences
|
||||
self.unsubscribe_all = unsubscribe_all
|
||||
self.created_timestamp = iso_8601_datetime_with_milliseconds(dt.utcnow())
|
||||
self.last_updated_timestamp = iso_8601_datetime_with_milliseconds(dt.utcnow())
|
||||
|
||||
@property
|
||||
def response_object(self) -> Dict[str, Any]: # type: ignore[misc]
|
||||
return {
|
||||
"ContactListName": self.contact_list_name,
|
||||
"EmailAddress": self.email_address,
|
||||
"TopicDefaultPreferences": self.topic_default_preferences,
|
||||
"TopicPreferences": self.topic_preferences,
|
||||
"UnsubscribeAll": self.unsubscribe_all,
|
||||
"CreatedTimestamp": self.created_timestamp,
|
||||
"LastUpdatedTimestamp": self.last_updated_timestamp,
|
||||
}
|
||||
|
||||
|
||||
class ContactList(BaseModel):
|
||||
def __init__(
|
||||
self,
|
||||
contact_list_name: str,
|
||||
description: str,
|
||||
topics: List[Dict[str, str]],
|
||||
) -> None:
|
||||
self.contact_list_name = contact_list_name
|
||||
self.description = description
|
||||
self.topics = topics
|
||||
self.created_timestamp = iso_8601_datetime_with_milliseconds(dt.utcnow())
|
||||
self.last_updated_timestamp = iso_8601_datetime_with_milliseconds(dt.utcnow())
|
||||
self.contacts: Dict[str, Contact] = {}
|
||||
|
||||
def create_contact(self, contact_list_name: str, params: Dict[str, Any]) -> None:
|
||||
email_address = params["EmailAddress"]
|
||||
topic_preferences = (
|
||||
[] if "TopicPreferences" not in params else params["TopicPreferences"]
|
||||
)
|
||||
unsubscribe_all = (
|
||||
False if "UnsubscribeAll" not in params else params["UnsubscribeAll"]
|
||||
)
|
||||
new_contact = Contact(
|
||||
contact_list_name, email_address, topic_preferences, unsubscribe_all
|
||||
)
|
||||
self.contacts[email_address] = new_contact
|
||||
|
||||
def list_contacts(self) -> List[Contact]:
|
||||
return self.contacts.values() # type: ignore[return-value]
|
||||
|
||||
def get_contact(self, email: str) -> Contact:
|
||||
if email in self.contacts:
|
||||
return self.contacts[email]
|
||||
else:
|
||||
raise NotFoundException(f"{email} doesn't exist in List.")
|
||||
|
||||
def delete_contact(self, email: str) -> None:
|
||||
# delete if contact exists, otherwise get_contact will throw appropriate exception
|
||||
if self.get_contact(email):
|
||||
del self.contacts[email]
|
||||
|
||||
@property
|
||||
def response_object(self) -> Dict[str, Any]: # type: ignore[misc]
|
||||
return {
|
||||
"ContactListName": self.contact_list_name,
|
||||
"Description": self.description,
|
||||
"Topics": self.topics,
|
||||
"CreatedTimestamp": self.created_timestamp,
|
||||
"LastUpdatedTimestamp": self.last_updated_timestamp,
|
||||
}
|
||||
|
||||
|
||||
class SESV2Backend(BaseBackend):
|
||||
"""Implementation of SESV2 APIs, piggy back on v1 SES"""
|
||||
|
||||
def __init__(self, region_name: str, account_id: str):
|
||||
super().__init__(region_name, account_id)
|
||||
self.contacts: Dict[str, Contact] = {}
|
||||
self.contacts_lists: Dict[str, ContactList] = {}
|
||||
|
||||
def create_contact_list(self, params: Dict[str, Any]) -> None:
|
||||
name = params["ContactListName"]
|
||||
description = params.get("Description")
|
||||
topics = [] if "Topics" not in params else params["Topics"]
|
||||
new_list = ContactList(name, str(description), topics)
|
||||
self.contacts_lists[name] = new_list
|
||||
|
||||
def get_contact_list(self, contact_list_name: str) -> ContactList:
|
||||
if contact_list_name in self.contacts_lists:
|
||||
return self.contacts_lists[contact_list_name]
|
||||
else:
|
||||
raise NotFoundException(
|
||||
f"List with name: {contact_list_name} doesn't exist."
|
||||
)
|
||||
|
||||
def list_contact_lists(self) -> List[ContactList]:
|
||||
return self.contacts_lists.values() # type: ignore[return-value]
|
||||
|
||||
def delete_contact_list(self, name: str) -> None:
|
||||
if name in self.contacts_lists:
|
||||
del self.contacts_lists[name]
|
||||
else:
|
||||
raise NotFoundException(f"List with name: {name} doesn't exist")
|
||||
|
||||
def create_contact(self, contact_list_name: str, params: Dict[str, Any]) -> None:
|
||||
contact_list = self.get_contact_list(contact_list_name)
|
||||
contact_list.create_contact(contact_list_name, params)
|
||||
return
|
||||
|
||||
def get_contact(self, email: str, contact_list_name: str) -> Contact:
|
||||
contact_list = self.get_contact_list(contact_list_name)
|
||||
contact = contact_list.get_contact(email)
|
||||
return contact
|
||||
|
||||
def list_contacts(self, contact_list_name: str) -> List[Contact]:
|
||||
contact_list = self.get_contact_list(contact_list_name)
|
||||
contacts = contact_list.list_contacts()
|
||||
return contacts
|
||||
|
||||
def delete_contact(self, email: str, contact_list_name: str) -> None:
|
||||
contact_list = self.get_contact_list(contact_list_name)
|
||||
contact_list.delete_contact(email)
|
||||
return
|
||||
|
||||
def send_email(
|
||||
self, source: str, destinations: Dict[str, List[str]], subject: str, body: str
|
||||
) -> Message:
|
||||
v1_backend = ses_backends[self.account_id][self.region_name]
|
||||
message = v1_backend.send_email(
|
||||
source=source,
|
||||
destinations=destinations,
|
||||
subject=subject,
|
||||
body=body,
|
||||
)
|
||||
return message
|
||||
|
||||
def send_raw_email(
|
||||
self, source: str, destinations: List[str], raw_data: str
|
||||
) -> RawMessage:
|
||||
v1_backend = ses_backends[self.account_id][self.region_name]
|
||||
message = v1_backend.send_raw_email(
|
||||
source=source, destinations=destinations, raw_data=raw_data
|
||||
)
|
||||
return message
|
||||
|
||||
|
||||
sesv2_backends = BackendDict(SESV2Backend, "sesv2")
|
102
moto/sesv2/responses.py
Normal file
102
moto/sesv2/responses.py
Normal file
@ -0,0 +1,102 @@
|
||||
"""Handles incoming sesv2 requests, invokes methods, returns responses."""
|
||||
import json
|
||||
|
||||
from moto.core.responses import BaseResponse
|
||||
from .models import sesv2_backends
|
||||
from ..ses.responses import SEND_EMAIL_RESPONSE
|
||||
from .models import SESV2Backend
|
||||
from typing import List, Dict, Any
|
||||
from urllib.parse import unquote
|
||||
|
||||
|
||||
class SESV2Response(BaseResponse):
|
||||
"""Handler for SESV2 requests and responses."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__(service_name="sesv2")
|
||||
|
||||
@property
|
||||
def sesv2_backend(self) -> SESV2Backend:
|
||||
"""Return backend instance specific for this region."""
|
||||
return sesv2_backends[self.current_account][self.region]
|
||||
|
||||
def send_email(self) -> str:
|
||||
"""Piggy back on functionality from v1 mostly"""
|
||||
|
||||
params = get_params_dict(self.querystring)
|
||||
from_email_address = params.get("FromEmailAddress")
|
||||
destination = params.get("Destination")
|
||||
content = params.get("Content")
|
||||
if "Raw" in content:
|
||||
all_destinations: List[str] = []
|
||||
if "ToAddresses" in destination:
|
||||
all_destinations = all_destinations + destination["ToAddresses"]
|
||||
if "CcAddresses" in destination:
|
||||
all_destinations = all_destinations + destination["CcAddresses"]
|
||||
if "BccAddresses" in destination:
|
||||
all_destinations = all_destinations + destination["BccAddresses"]
|
||||
message = self.sesv2_backend.send_raw_email(
|
||||
source=from_email_address,
|
||||
destinations=all_destinations,
|
||||
raw_data=content["Raw"]["Data"],
|
||||
)
|
||||
elif "Simple" in content:
|
||||
message = self.sesv2_backend.send_email( # type: ignore
|
||||
source=from_email_address,
|
||||
destinations=destination,
|
||||
subject=content["Simple"]["Subject"]["Data"],
|
||||
body=content["Simple"]["Subject"]["Data"],
|
||||
)
|
||||
elif "Template" in content:
|
||||
raise NotImplementedError("Template functionality not ready")
|
||||
|
||||
# use v1 templates as response same in v1 and v2
|
||||
template = self.response_template(SEND_EMAIL_RESPONSE)
|
||||
return template.render(message=message)
|
||||
|
||||
def create_contact_list(self) -> str:
|
||||
params = get_params_dict(self.data)
|
||||
self.sesv2_backend.create_contact_list(params)
|
||||
return json.dumps({})
|
||||
|
||||
def get_contact_list(self) -> str:
|
||||
contact_list_name = self._get_param("ContactListName")
|
||||
contact_list = self.sesv2_backend.get_contact_list(contact_list_name)
|
||||
return json.dumps(contact_list.response_object)
|
||||
|
||||
def list_contact_lists(self) -> str:
|
||||
contact_lists = self.sesv2_backend.list_contact_lists()
|
||||
return json.dumps(dict(ContactLists=[c.response_object for c in contact_lists]))
|
||||
|
||||
def delete_contact_list(self) -> str:
|
||||
name = self._get_param("ContactListName")
|
||||
self.sesv2_backend.delete_contact_list(name)
|
||||
return json.dumps({})
|
||||
|
||||
def create_contact(self) -> str:
|
||||
contact_list_name = self._get_param("ContactListName")
|
||||
params = get_params_dict(self.data)
|
||||
self.sesv2_backend.create_contact(contact_list_name, params)
|
||||
return json.dumps({})
|
||||
|
||||
def get_contact(self) -> str:
|
||||
email = unquote(self._get_param("EmailAddress"))
|
||||
contact_list_name = self._get_param("ContactListName")
|
||||
contact = self.sesv2_backend.get_contact(email, contact_list_name)
|
||||
return json.dumps(contact.response_object)
|
||||
|
||||
def list_contacts(self) -> str:
|
||||
contact_list_name = self._get_param("ContactListName")
|
||||
contacts = self.sesv2_backend.list_contacts(contact_list_name)
|
||||
return json.dumps(dict(Contacts=[c.response_object for c in contacts]))
|
||||
|
||||
def delete_contact(self) -> str:
|
||||
email = self._get_param("EmailAddress")
|
||||
contact_list_name = self._get_param("ContactListName")
|
||||
self.sesv2_backend.delete_contact(unquote(email), contact_list_name)
|
||||
return json.dumps({})
|
||||
|
||||
|
||||
def get_params_dict(odict: Dict[str, Any]) -> Any:
|
||||
# parsing of these params is nasty, hopefully there is a tidier way
|
||||
return json.loads(list(dict(odict.items()).keys())[0])
|
19
moto/sesv2/urls.py
Normal file
19
moto/sesv2/urls.py
Normal file
@ -0,0 +1,19 @@
|
||||
"""sesv2 base URL and path."""
|
||||
from .responses import SESV2Response
|
||||
|
||||
url_bases = [
|
||||
r"https?://email\.(.+)\.amazonaws\.com",
|
||||
]
|
||||
|
||||
|
||||
response = SESV2Response()
|
||||
|
||||
|
||||
url_paths = {
|
||||
"{0}/v2/email/outbound-emails$": response.dispatch,
|
||||
"{0}/v2/email/contact-lists/(?P<name>[^/]+)$": response.dispatch,
|
||||
"{0}/v2/email/contact-lists/(?P<name>[^/]+)/contacts$": response.dispatch,
|
||||
"{0}/v2/email/contact-lists/(?P<name>[^/]+)/contacts/(?P<email>[^/]+)$": response.dispatch,
|
||||
"{0}/v2/email/contact-lists$": response.dispatch,
|
||||
"{0}/v2/.*$": response.dispatch,
|
||||
}
|
@ -61,6 +61,7 @@ def calculate_extended_implementation_coverage():
|
||||
operation_names = [
|
||||
xform_name(op) for op in real_client.meta.service_model.operation_names
|
||||
]
|
||||
|
||||
for op in operation_names:
|
||||
if moto_client and op in dir(moto_client):
|
||||
implemented[op] = getattr(moto_client, op)
|
||||
|
0
tests/test_sesv2/__init__.py
Normal file
0
tests/test_sesv2/__init__.py
Normal file
13
tests/test_sesv2/test_server.py
Normal file
13
tests/test_sesv2/test_server.py
Normal file
@ -0,0 +1,13 @@
|
||||
"""Test different server responses."""
|
||||
|
||||
import moto.server as server
|
||||
|
||||
|
||||
def test_sesv2_list():
|
||||
backend = server.create_backend_app("sesv2")
|
||||
test_client = backend.test_client()
|
||||
|
||||
resp = test_client.get("/v2/email/contact-lists")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.data == b'{"ContactLists": []}'
|
323
tests/test_sesv2/test_sesv2.py
Normal file
323
tests/test_sesv2/test_sesv2.py
Normal file
@ -0,0 +1,323 @@
|
||||
import boto3
|
||||
from botocore.exceptions import ClientError
|
||||
import pytest
|
||||
from moto import mock_sesv2, mock_ses
|
||||
from ..test_ses.test_ses_boto3 import get_raw_email
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def ses_v1():
|
||||
"""Use this for API calls which exist in v1 but not in v2"""
|
||||
with mock_ses():
|
||||
yield boto3.client("ses", region_name="us-east-1")
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_send_email(ses_v1): # pylint: disable=redefined-outer-name
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
kwargs = dict(
|
||||
FromEmailAddress="test@example.com",
|
||||
Destination={
|
||||
"ToAddresses": ["test_to@example.com"],
|
||||
"CcAddresses": ["test_cc@example.com"],
|
||||
"BccAddresses": ["test_bcc@example.com"],
|
||||
},
|
||||
Content={
|
||||
"Simple": {
|
||||
"Subject": {"Data": "test subject"},
|
||||
"Body": {"Text": {"Data": "test body"}},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
# Execute
|
||||
with pytest.raises(ClientError) as e:
|
||||
conn.send_email(**kwargs)
|
||||
assert e.value.response["Error"]["Code"] == "MessageRejected"
|
||||
|
||||
ses_v1.verify_domain_identity(Domain="example.com")
|
||||
conn.send_email(**kwargs)
|
||||
send_quota = ses_v1.get_send_quota()
|
||||
|
||||
# Verify
|
||||
sent_count = int(send_quota["SentLast24Hours"])
|
||||
assert sent_count == 3
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_send_raw_email(ses_v1): # pylint: disable=redefined-outer-name
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
message = get_raw_email()
|
||||
destination = {
|
||||
"ToAddresses": [x.strip() for x in message["To"].split(",")],
|
||||
}
|
||||
kwargs = dict(
|
||||
FromEmailAddress=message["From"],
|
||||
Destination=destination,
|
||||
Content={"Raw": {"Data": message.as_bytes()}},
|
||||
)
|
||||
|
||||
# Execute
|
||||
ses_v1.verify_email_identity(EmailAddress="test@example.com")
|
||||
conn.send_email(**kwargs)
|
||||
|
||||
# Verify
|
||||
send_quota = ses_v1.get_send_quota()
|
||||
sent_count = int(send_quota["SentLast24Hours"])
|
||||
assert sent_count == 2
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_create_contact_list():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
|
||||
# Execute
|
||||
conn.create_contact_list(
|
||||
ContactListName=contact_list_name,
|
||||
)
|
||||
result = conn.list_contact_lists()
|
||||
|
||||
# Verify
|
||||
assert len(result["ContactLists"]) == 1
|
||||
assert result["ContactLists"][0]["ContactListName"] == contact_list_name
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_list_contact_lists():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
|
||||
# Execute
|
||||
result = conn.list_contact_lists()
|
||||
|
||||
# Verify
|
||||
assert result["ContactLists"] == []
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_get_contact_list():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
|
||||
# Execute
|
||||
with pytest.raises(ClientError) as e:
|
||||
conn.get_contact_list(ContactListName=contact_list_name)
|
||||
assert e.value.response["Error"]["Code"] == "NotFoundException"
|
||||
assert (
|
||||
e.value.response["Error"]["Message"]
|
||||
== f"List with name: {contact_list_name} doesn't exist."
|
||||
)
|
||||
|
||||
conn.create_contact_list(
|
||||
ContactListName=contact_list_name,
|
||||
)
|
||||
result = conn.get_contact_list(ContactListName=contact_list_name)
|
||||
|
||||
# Verify
|
||||
assert result["ContactListName"] == contact_list_name
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_delete_contact_list():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
|
||||
# Execute
|
||||
with pytest.raises(ClientError) as e:
|
||||
conn.delete_contact_list(ContactListName=contact_list_name)
|
||||
assert e.value.response["Error"]["Code"] == "NotFoundException"
|
||||
conn.create_contact_list(
|
||||
ContactListName=contact_list_name,
|
||||
)
|
||||
result = conn.list_contact_lists()
|
||||
assert len(result["ContactLists"]) == 1
|
||||
conn.delete_contact_list(
|
||||
ContactListName=contact_list_name,
|
||||
)
|
||||
result = conn.list_contact_lists()
|
||||
|
||||
# Verify
|
||||
assert len(result["ContactLists"]) == 0
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_list_contacts():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
conn.create_contact_list(
|
||||
ContactListName=contact_list_name,
|
||||
)
|
||||
|
||||
# Execute
|
||||
result = conn.list_contacts(ContactListName=contact_list_name)
|
||||
|
||||
# Verify
|
||||
assert result["Contacts"] == []
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_create_contact_no_contact_list():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
email = "test@example.com"
|
||||
|
||||
# Execute
|
||||
with pytest.raises(ClientError) as e:
|
||||
conn.create_contact(
|
||||
ContactListName=contact_list_name,
|
||||
EmailAddress=email,
|
||||
)
|
||||
|
||||
# Verify
|
||||
assert e.value.response["Error"]["Code"] == "NotFoundException"
|
||||
assert (
|
||||
e.value.response["Error"]["Message"]
|
||||
== f"List with name: {contact_list_name} doesn't exist."
|
||||
)
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_create_contact():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
email = "test@example.com"
|
||||
conn.create_contact_list(
|
||||
ContactListName=contact_list_name,
|
||||
)
|
||||
|
||||
# Execute
|
||||
conn.create_contact(
|
||||
ContactListName=contact_list_name,
|
||||
EmailAddress=email,
|
||||
)
|
||||
|
||||
result = conn.list_contacts(ContactListName=contact_list_name)
|
||||
|
||||
# Verify
|
||||
assert len(result["Contacts"]) == 1
|
||||
assert result["Contacts"][0]["EmailAddress"] == email
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_get_contact_no_contact_list():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
email = "test@example.com"
|
||||
|
||||
# Execute
|
||||
with pytest.raises(ClientError) as e:
|
||||
conn.get_contact(ContactListName=contact_list_name, EmailAddress=email)
|
||||
|
||||
# Verify
|
||||
assert e.value.response["Error"]["Code"] == "NotFoundException"
|
||||
assert (
|
||||
e.value.response["Error"]["Message"]
|
||||
== f"List with name: {contact_list_name} doesn't exist."
|
||||
)
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_get_contact():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
email = "test@example.com"
|
||||
conn.create_contact_list(
|
||||
ContactListName=contact_list_name,
|
||||
)
|
||||
# Execute
|
||||
|
||||
conn.create_contact(
|
||||
ContactListName=contact_list_name,
|
||||
EmailAddress=email,
|
||||
)
|
||||
result = conn.get_contact(ContactListName=contact_list_name, EmailAddress=email)
|
||||
|
||||
# Verify
|
||||
assert result["ContactListName"] == contact_list_name
|
||||
assert result["EmailAddress"] == email
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_get_contact_no_contact():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
email = "test@example.com"
|
||||
conn.create_contact_list(
|
||||
ContactListName=contact_list_name,
|
||||
)
|
||||
# Execute
|
||||
with pytest.raises(ClientError) as e:
|
||||
conn.get_contact(ContactListName=contact_list_name, EmailAddress=email)
|
||||
|
||||
# Verify
|
||||
assert e.value.response["Error"]["Code"] == "NotFoundException"
|
||||
assert e.value.response["Error"]["Message"] == f"{email} doesn't exist in List."
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_delete_contact_no_contact_list():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
email = "test@example.com"
|
||||
|
||||
# Execute
|
||||
with pytest.raises(ClientError) as e:
|
||||
conn.delete_contact(ContactListName=contact_list_name, EmailAddress=email)
|
||||
|
||||
# Verify
|
||||
assert e.value.response["Error"]["Code"] == "NotFoundException"
|
||||
assert (
|
||||
e.value.response["Error"]["Message"]
|
||||
== f"List with name: {contact_list_name} doesn't exist."
|
||||
)
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_delete_contact_no_contact():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
email = "test@example.com"
|
||||
|
||||
# Execute
|
||||
conn.create_contact_list(ContactListName=contact_list_name)
|
||||
|
||||
with pytest.raises(ClientError) as e:
|
||||
conn.delete_contact(ContactListName=contact_list_name, EmailAddress=email)
|
||||
|
||||
# Verify
|
||||
assert e.value.response["Error"]["Code"] == "NotFoundException"
|
||||
assert e.value.response["Error"]["Message"] == f"{email} doesn't exist in List."
|
||||
|
||||
|
||||
@mock_sesv2
|
||||
def test_delete_contact():
|
||||
# Setup
|
||||
conn = boto3.client("sesv2", region_name="us-east-1")
|
||||
contact_list_name = "test2"
|
||||
email = "test@example.com"
|
||||
|
||||
# Execute
|
||||
conn.create_contact_list(ContactListName=contact_list_name)
|
||||
conn.create_contact(ContactListName=contact_list_name, EmailAddress=email)
|
||||
result = conn.list_contacts(ContactListName=contact_list_name)
|
||||
assert len(result["Contacts"]) == 1
|
||||
|
||||
conn.delete_contact(ContactListName=contact_list_name, EmailAddress=email)
|
||||
result = conn.list_contacts(ContactListName=contact_list_name)
|
||||
|
||||
# Verify
|
||||
assert len(result["Contacts"]) == 0
|
Loading…
Reference in New Issue
Block a user