From 940b351387d8de02d543fcdd9ea9e18f69c5957e Mon Sep 17 00:00:00 2001 From: Sherman Hui <11592023+shermanhui@users.noreply.github.com> Date: Sun, 31 Oct 2021 02:30:50 -0700 Subject: [PATCH] feat: add update_receipt_rule to ses mock (#4490) --- IMPLEMENTATION_COVERAGE.md | 32 +--- moto/ses/models.py | 13 ++ moto/ses/responses.py | 16 ++ tests/test_ses/test_ses_boto3.py | 313 +++++++++++++++++++++++++++++++ 4 files changed, 346 insertions(+), 28 deletions(-) diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index aa8117373..2c7b05e21 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -1087,7 +1087,6 @@ - [ ] bundle_instance - [ ] cancel_bundle_task - [ ] cancel_capacity_reservation -- [ ] cancel_capacity_reservation_fleets - [ ] cancel_conversion_task - [ ] cancel_export_task - [ ] cancel_import_task @@ -1099,7 +1098,6 @@ - [X] copy_image - [X] copy_snapshot - [ ] create_capacity_reservation -- [ ] create_capacity_reservation_fleet - [X] create_carrier_gateway - [ ] create_client_vpn_endpoint - [ ] create_client_vpn_route @@ -1231,7 +1229,6 @@ - [X] describe_availability_zones - [ ] describe_bundle_tasks - [ ] describe_byoip_cidrs -- [ ] describe_capacity_reservation_fleets - [ ] describe_capacity_reservations - [X] describe_carrier_gateways - [ ] describe_classic_link_instances @@ -1411,8 +1408,6 @@ - [ ] get_transit_gateway_prefix_list_references - [ ] get_transit_gateway_route_table_associations - [ ] get_transit_gateway_route_table_propagations -- [ ] get_vpn_connection_device_sample_configuration -- [ ] get_vpn_connection_device_types - [ ] import_client_vpn_client_certificate_revocation_list - [ ] import_image - [ ] import_instance @@ -1422,7 +1417,6 @@ - [ ] modify_address_attribute - [ ] modify_availability_zone_group - [ ] modify_capacity_reservation -- [ ] modify_capacity_reservation_fleet - [ ] modify_client_vpn_endpoint - [ ] modify_default_credit_specification - [ ] modify_ebs_default_kms_key_id @@ -1535,7 +1529,7 @@ ## ecr
-74% implemented +76% implemented - [ ] batch_check_layer_availability - [X] batch_delete_image @@ -1546,7 +1540,6 @@ - [X] delete_registry_policy - [X] delete_repository - [X] delete_repository_policy -- [ ] describe_image_replication_status - [X] describe_image_scan_findings - [X] describe_images - [X] describe_registry @@ -1667,7 +1660,7 @@ ## eks
-35% implemented +37% implemented - [ ] associate_encryption_config - [ ] associate_identity_provider_config @@ -1679,7 +1672,6 @@ - [X] delete_cluster - [X] delete_fargate_profile - [X] delete_nodegroup -- [ ] deregister_cluster - [ ] describe_addon - [ ] describe_addon_versions - [X] describe_cluster @@ -1695,7 +1687,6 @@ - [X] list_nodegroups - [ ] list_tags_for_resource - [ ] list_updates -- [ ] register_cluster - [ ] tag_resource - [ ] untag_resource - [ ] update_addon @@ -2614,7 +2605,6 @@ - [X] list_topic_rules - [ ] list_v2_logging_levels - [ ] list_violation_events -- [ ] put_verification_state_on_violation - [ ] register_ca_certificate - [X] register_certificate - [X] register_certificate_without_ca @@ -2990,7 +2980,6 @@ - [ ] batch_stop - [ ] batch_update_schedule - [ ] cancel_input_device_transfer -- [ ] claim_device - [X] create_channel - [X] create_input - [ ] create_input_security_group @@ -3808,7 +3797,6 @@ - [ ] create_presigned_notebook_instance_url - [ ] create_processing_job - [ ] create_project -- [ ] create_studio_lifecycle_config - [X] create_training_job - [ ] create_transform_job - [ ] create_trial @@ -3847,7 +3835,6 @@ - [X] delete_notebook_instance_lifecycle_config - [ ] delete_pipeline - [ ] delete_project -- [ ] delete_studio_lifecycle_config - [ ] delete_tags - [ ] delete_trial - [ ] delete_trial_component @@ -3893,7 +3880,6 @@ - [ ] describe_pipeline_execution - [ ] describe_processing_job - [ ] describe_project -- [ ] describe_studio_lifecycle_config - [ ] describe_subscribed_workteam - [X] describe_training_job - [ ] describe_transform_job @@ -3952,7 +3938,6 @@ - [ ] list_pipelines - [ ] list_processing_jobs - [ ] list_projects -- [ ] list_studio_lifecycle_configs - [ ] list_subscribed_workteams - [ ] list_tags - [X] list_training_jobs @@ -3966,7 +3951,6 @@ - [ ] put_model_package_group_policy - [ ] register_devices - [ ] render_ui_template -- [ ] retry_pipeline_execution - [ ] search - [ ] send_pipeline_execution_step_failure - [ ] send_pipeline_execution_step_success @@ -4040,7 +4024,7 @@ ## ses
-29% implemented +30% implemented - [ ] clone_receipt_rule_set - [X] create_configuration_set @@ -4107,7 +4091,7 @@ - [ ] update_configuration_set_sending_enabled - [ ] update_configuration_set_tracking_options - [ ] update_custom_verification_email_template -- [ ] update_receipt_rule +- [X] update_receipt_rule - [X] update_template - [ ] verify_domain_dkim - [ ] verify_domain_identity @@ -4554,7 +4538,6 @@
- accessanalyzer -- account - acm-pca - alexaforbusiness - amp @@ -4581,7 +4564,6 @@ - chime-sdk-identity - chime-sdk-messaging - cloud9 -- cloudcontrol - clouddirectory - cloudfront - cloudhsm @@ -4629,7 +4611,6 @@ - fsx - gamelift - globalaccelerator -- grafana - greengrass - greengrassv2 - groundstation @@ -4655,7 +4636,6 @@ - iotwireless - ivs - kafka -- kafkaconnect - kendra - kinesis-video-media - kinesis-video-signaling @@ -4694,10 +4674,8 @@ - network-firewall - networkmanager - nimble -- opensearch - opsworkscm - outposts -- panorama - personalize - personalize-events - personalize-runtime @@ -4752,11 +4730,9 @@ - timestream-query - transfer - translate -- voice-id - waf - waf-regional - wellarchitected -- wisdom - workdocs - worklink - workmail diff --git a/moto/ses/models.py b/moto/ses/models.py index 478b72993..c13a052bd 100644 --- a/moto/ses/models.py +++ b/moto/ses/models.py @@ -438,5 +438,18 @@ class SESBackend(BaseBackend): else: raise RuleDoesNotExist("Invalid Rule Name.") + def update_receipt_rule(self, rule_set_name, rule): + rule_set = self.receipt_rule_set.get(rule_set_name) + + if rule_set is None: + raise RuleSetDoesNotExist(f"Rule set does not exist: {rule_set_name}") + + for i, receipt_rule in enumerate(rule_set): + if receipt_rule["name"] == rule["name"]: + rule_set[i] = rule + break + else: + raise RuleDoesNotExist(f"Rule does not exist: {rule['name']}") + ses_backend = SESBackend() diff --git a/moto/ses/responses.py b/moto/ses/responses.py index 43474cbf4..ad49cd924 100644 --- a/moto/ses/responses.py +++ b/moto/ses/responses.py @@ -253,6 +253,15 @@ class EmailResponse(BaseResponse): template = self.response_template(DESCRIBE_RECEIPT_RULE) return template.render(rule=rule) + def update_receipt_rule(self): + rule_set_name = self._get_param("RuleSetName") + rule = self._get_dict_param("Rule.") + + ses_backend.update_receipt_rule(rule_set_name, rule) + + template = self.response_template(UPDATE_RECEIPT_RULE) + return template.render() + VERIFY_EMAIL_IDENTITY = """ @@ -549,3 +558,10 @@ DESCRIBE_RECEIPT_RULE = """ + + + 15e0ef1a-9bf2-11e1-9279-01ab88cf109a + +""" diff --git a/tests/test_ses/test_ses_boto3.py b/tests/test_ses/test_ses_boto3.py index 1ce32a1f2..c3eaae3fd 100644 --- a/tests/test_ses/test_ses_boto3.py +++ b/tests/test_ses/test_ses_boto3.py @@ -2,6 +2,7 @@ import json import boto3 from botocore.exceptions import ClientError +from botocore.exceptions import ParamValidationError from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText import pytest @@ -578,6 +579,318 @@ def test_describe_receipt_rule(): error.value.response["Error"]["Code"].should.equal("RuleDoesNotExist") +@mock_ses +def test_update_receipt_rule(): + conn = boto3.client("ses", region_name="us-east-1") + rule_set_name = "testRuleSet" + conn.create_receipt_rule_set(RuleSetName=rule_set_name) + + rule_name = "testRule" + conn.create_receipt_rule( + RuleSetName=rule_set_name, + Rule={ + "Name": rule_name, + "Enabled": False, + "TlsPolicy": "Optional", + "Recipients": ["test@email.com", "test2@email.com"], + "Actions": [ + { + "S3Action": { + "TopicArn": "string", + "BucketName": "testBucketName", + "ObjectKeyPrefix": "testObjectKeyPrefix", + "KmsKeyArn": "string", + }, + "BounceAction": { + "TopicArn": "string", + "SmtpReplyCode": "string", + "StatusCode": "string", + "Message": "string", + "Sender": "string", + }, + } + ], + "ScanEnabled": False, + }, + ) + + update_receipt_rule_response = conn.update_receipt_rule( + RuleSetName=rule_set_name, + Rule={ + "Name": rule_name, + "Enabled": True, + "TlsPolicy": "Optional", + "Recipients": ["test@email.com"], + "Actions": [ + { + "S3Action": { + "TopicArn": "string", + "BucketName": "testBucketName", + "ObjectKeyPrefix": "testObjectKeyPrefix", + "KmsKeyArn": "string", + }, + "BounceAction": { + "TopicArn": "string", + "SmtpReplyCode": "string", + "StatusCode": "string", + "Message": "string", + "Sender": "string", + }, + } + ], + "ScanEnabled": False, + }, + ) + + update_receipt_rule_response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + updated_rule_description = conn.describe_receipt_rule( + RuleSetName=rule_set_name, RuleName=rule_name + ) + + updated_rule_description["Rule"]["Name"].should.equal(rule_name) + updated_rule_description["Rule"]["Enabled"].should.equal(True) + len(updated_rule_description["Rule"]["Recipients"]).should.equal(1) + updated_rule_description["Rule"]["Recipients"][0].should.equal("test@email.com") + + len(updated_rule_description["Rule"]["Actions"]).should.equal(1) + updated_rule_description["Rule"]["Actions"][0].should.have.key("S3Action") + updated_rule_description["Rule"]["Actions"][0].should.have.key("BounceAction") + + with pytest.raises(ClientError) as error: + conn.update_receipt_rule( + RuleSetName="invalidRuleSetName", + Rule={ + "Name": rule_name, + "Enabled": True, + "TlsPolicy": "Optional", + "Recipients": ["test@email.com"], + "Actions": [ + { + "S3Action": { + "TopicArn": "string", + "BucketName": "testBucketName", + "ObjectKeyPrefix": "testObjectKeyPrefix", + "KmsKeyArn": "string", + }, + "BounceAction": { + "TopicArn": "string", + "SmtpReplyCode": "string", + "StatusCode": "string", + "Message": "string", + "Sender": "string", + }, + } + ], + "ScanEnabled": False, + }, + ) + + error.value.response["Error"]["Code"].should.equal("RuleSetDoesNotExist") + error.value.response["Error"]["Message"].should.equal( + "Rule set does not exist: invalidRuleSetName" + ) + + with pytest.raises(ClientError) as error: + conn.update_receipt_rule( + RuleSetName=rule_set_name, + Rule={ + "Name": "invalidRuleName", + "Enabled": True, + "TlsPolicy": "Optional", + "Recipients": ["test@email.com"], + "Actions": [ + { + "S3Action": { + "TopicArn": "string", + "BucketName": "testBucketName", + "ObjectKeyPrefix": "testObjectKeyPrefix", + "KmsKeyArn": "string", + }, + "BounceAction": { + "TopicArn": "string", + "SmtpReplyCode": "string", + "StatusCode": "string", + "Message": "string", + "Sender": "string", + }, + } + ], + "ScanEnabled": False, + }, + ) + + error.value.response["Error"]["Code"].should.equal("RuleDoesNotExist") + error.value.response["Error"]["Message"].should.equal( + "Rule does not exist: invalidRuleName" + ) + + with pytest.raises(ParamValidationError) as error: + conn.update_receipt_rule( + RuleSetName=rule_set_name, + Rule={ + "Enabled": True, + "TlsPolicy": "Optional", + "Recipients": ["test@email.com"], + "Actions": [ + { + "S3Action": { + "TopicArn": "string", + "BucketName": "testBucketName", + "ObjectKeyPrefix": "testObjectKeyPrefix", + "KmsKeyArn": "string", + }, + "BounceAction": { + "TopicArn": "string", + "SmtpReplyCode": "string", + "StatusCode": "string", + "Message": "string", + "Sender": "string", + }, + } + ], + "ScanEnabled": False, + }, + ) + + str(error.value).should.contain( + 'Parameter validation failed:\nMissing required parameter in Rule: "Name"' + ) + + +@mock_ses +def test_update_receipt_rule_actions(): + conn = boto3.client("ses", region_name="us-east-1") + rule_set_name = "testRuleSet" + conn.create_receipt_rule_set(RuleSetName=rule_set_name) + + rule_name = "testRule" + conn.create_receipt_rule( + RuleSetName=rule_set_name, + Rule={ + "Name": rule_name, + "Enabled": False, + "TlsPolicy": "Optional", + "Recipients": ["test@email.com", "test2@email.com"], + "Actions": [ + { + "S3Action": { + "TopicArn": "string", + "BucketName": "testBucketName", + "ObjectKeyPrefix": "testObjectKeyPrefix", + "KmsKeyArn": "string", + }, + "BounceAction": { + "TopicArn": "string", + "SmtpReplyCode": "string", + "StatusCode": "string", + "Message": "string", + "Sender": "string", + }, + } + ], + "ScanEnabled": False, + }, + ) + + update_receipt_rule_response = conn.update_receipt_rule( + RuleSetName=rule_set_name, + Rule={ + "Name": rule_name, + "Enabled": False, + "TlsPolicy": "Optional", + "Recipients": ["test@email.com", "test2@email.com"], + "Actions": [ + { + "S3Action": { + "TopicArn": "newString", + "BucketName": "updatedTestBucketName", + "ObjectKeyPrefix": "updatedTestObjectKeyPrefix", + "KmsKeyArn": "newString", + }, + "BounceAction": { + "TopicArn": "newString", + "SmtpReplyCode": "newString", + "StatusCode": "newString", + "Message": "newString", + "Sender": "newString", + }, + } + ], + "ScanEnabled": False, + }, + ) + + update_receipt_rule_response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + updated_rule_description = conn.describe_receipt_rule( + RuleSetName=rule_set_name, RuleName=rule_name + ) + + len(updated_rule_description["Rule"]["Actions"]).should.equal(1) + updated_rule_description["Rule"]["Actions"][0].should.have.key("S3Action") + + updated_rule_description["Rule"]["Actions"][0]["S3Action"].should.have.key( + "TopicArn" + ).being.equal("newString") + updated_rule_description["Rule"]["Actions"][0]["S3Action"].should.have.key( + "BucketName" + ).being.equal("updatedTestBucketName") + updated_rule_description["Rule"]["Actions"][0]["S3Action"].should.have.key( + "ObjectKeyPrefix" + ).being.equal("updatedTestObjectKeyPrefix") + updated_rule_description["Rule"]["Actions"][0]["S3Action"].should.have.key( + "KmsKeyArn" + ).being.equal("newString") + + updated_rule_description["Rule"]["Actions"][0].should.have.key("BounceAction") + + updated_rule_description["Rule"]["Actions"][0]["BounceAction"].should.have.key( + "TopicArn" + ).being.equal("newString") + updated_rule_description["Rule"]["Actions"][0]["BounceAction"].should.have.key( + "SmtpReplyCode" + ).being.equal("newString") + updated_rule_description["Rule"]["Actions"][0]["BounceAction"].should.have.key( + "StatusCode" + ).being.equal("newString") + updated_rule_description["Rule"]["Actions"][0]["BounceAction"].should.have.key( + "Message" + ).being.equal("newString") + updated_rule_description["Rule"]["Actions"][0]["BounceAction"].should.have.key( + "Sender" + ).being.equal("newString") + + with pytest.raises(ParamValidationError) as error: + conn.update_receipt_rule( + RuleSetName=rule_set_name, + Rule={ + "Name": rule_name, + "Enabled": False, + "TlsPolicy": "Optional", + "Recipients": ["test@email.com", "test2@email.com"], + "Actions": [ + { + "S3Action": { + "TopicArn": "newString", + "ObjectKeyPrefix": "updatedTestObjectKeyPrefix", + "KmsKeyArn": "newString", + }, + "BounceAction": { + "TopicArn": "newString", + "StatusCode": "newString", + }, + } + ], + "ScanEnabled": False, + }, + ) + + assert (str(error.value)).should.contain( + 'Parameter validation failed:\nMissing required parameter in Rule.Actions[0].S3Action: "BucketName"\nMissing required parameter in Rule.Actions[0].BounceAction: "SmtpReplyCode"\nMissing required parameter in Rule.Actions[0].BounceAction: "Message"\nMissing required parameter in Rule.Actions[0].BounceAction: "Sender"' + ) + + @mock_ses def test_create_ses_template(): conn = boto3.client("ses", region_name="us-east-1")