From ba8a2ccfc5c2babbd3876be05fcd5796b7ee607c Mon Sep 17 00:00:00 2001 From: Terry Cain Date: Tue, 19 Sep 2017 23:54:13 +0100 Subject: [PATCH] Implemented CheckIfPhoneNumberIsOptedOut + Tests + Error code --- moto/sns/responses.py | 41 +++++++++++++++++++++++++++++--- tests/test_sns/test_sms_boto3.py | 29 ++++++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/moto/sns/responses.py b/moto/sns/responses.py index 9ffe298af..f06f10816 100644 --- a/moto/sns/responses.py +++ b/moto/sns/responses.py @@ -9,11 +9,17 @@ from .models import sns_backends class SNSResponse(BaseResponse): + SMS_ATTR_REGEX = re.compile(r'^attributes\.entry\.(?P\d+)\.(?Pkey|value)$') + OPT_OUT_PHONE_NUMBER_REGEX = re.compile(r'^\+?\d+$') @property def backend(self): return sns_backends[self.region] + def _error(self, code, message, sender='Sender'): + template = self.response_template(ERROR_RESPONSE) + return template.render(code=code, message=message, sender=sender) + def _get_attributes(self): attributes = self._get_list_prefix('Attributes.entry') return dict( @@ -462,15 +468,13 @@ class SNSResponse(BaseResponse): return template.render() def set_sms_attributes(self): - attr_regex = re.compile(r'^attributes\.entry\.(?P\d+)\.(?Pkey|value)$') - # attributes.entry.1.key # attributes.entry.1.value # to # 1: {key:X, value:Y} temp_dict = defaultdict(dict) for key, value in self.querystring.items(): - match = attr_regex.match(key) + match = self.SMS_ATTR_REGEX.match(key) if match is not None: temp_dict[match.group('index')][match.group('type')] = value[0] @@ -502,6 +506,19 @@ class SNSResponse(BaseResponse): template = self.response_template(GET_SMS_ATTRIBUTES_TEMPLATE) return template.render(attributes=result) + def check_if_phone_number_is_opted_out(self): + number = self._get_param('phoneNumber') + if self.OPT_OUT_PHONE_NUMBER_REGEX.match(number) is None: + error_response = self._error( + code='InvalidParameter', + message='Invalid parameter: PhoneNumber Reason: input incorrectly formatted' + ) + return error_response, dict(status=400) + + # There should be a nicer way to set if a nubmer has opted out + template = self.response_template(CHECK_IF_OPTED_OUT_TEMPLATE) + return template.render(opt_out=str(number.endswith('99')).lower()) + CREATE_TOPIC_TEMPLATE = """ @@ -824,3 +841,21 @@ GET_SMS_ATTRIBUTES_TEMPLATE = """ + + {{ opt_out }} + + + 287f9554-8db3-5e66-8abc-c76f0186db7e + +""" + +ERROR_RESPONSE = """ + + {{ sender }} + {{ code }} + {{ message }} + + 9dd01905-5012-5f99-8663-4b3ecd0dfaef +""" \ No newline at end of file diff --git a/tests/test_sns/test_sms_boto3.py b/tests/test_sns/test_sms_boto3.py index 220a7530a..185b3e43c 100644 --- a/tests/test_sns/test_sms_boto3.py +++ b/tests/test_sns/test_sms_boto3.py @@ -3,6 +3,8 @@ import boto3 import sure # noqa from moto import mock_sns +from botocore.exceptions import ClientError +from nose.tools import assert_raises @mock_sns @@ -30,3 +32,30 @@ def test_get_sms_attributes_filtered(): response['attributes'].should.contain('DefaultSMSType') response['attributes'].should_not.contain('test') response['attributes']['DefaultSMSType'].should.equal('Transactional') + + +@mock_sns +def test_check_not_opted_out(): + conn = boto3.client('sns', region_name='us-east-1') + response = conn.check_if_phone_number_is_opted_out(phoneNumber='+447428545375') + + response.should.contain('isOptedOut') + response['isOptedOut'].should.be(False) + + +@mock_sns +def test_check_opted_out(): # Ends in 99 so is opted out + conn = boto3.client('sns', region_name='us-east-1') + response = conn.check_if_phone_number_is_opted_out(phoneNumber='+447428545399') + + response.should.contain('isOptedOut') + response['isOptedOut'].should.be(True) + + +@mock_sns +def test_check_opted_out_invalid(): + conn = boto3.client('sns', region_name='us-east-1') + + # Invalid phone number + with assert_raises(ClientError): + conn.check_if_phone_number_is_opted_out(phoneNumber='+44742LALALA')