[SNS] Mock sending directly SMS (#3253)
* [SNS] Mock sending directly SMS Proper behaviour when publishing to PhoneNumber is sending message directly to this number, without any topic or previous confirmation. https://docs.aws.amazon.com/sns/latest/dg/sns-mobile-phone-number-as-subscriber.html * Fix arguments order * Omit checking local backend when tests in server mode
This commit is contained in:
parent
0317602ae5
commit
8a551a9754
@ -202,7 +202,7 @@ class SESBackend(BaseBackend):
|
||||
if sns_topic is not None:
|
||||
message = self.__generate_feedback__(msg_type)
|
||||
if message:
|
||||
sns_backends[region].publish(sns_topic, message)
|
||||
sns_backends[region].publish(message, arn=sns_topic)
|
||||
|
||||
def send_raw_email(self, source, destinations, raw_data, region):
|
||||
if source is not None:
|
||||
|
@ -35,6 +35,7 @@ from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
|
||||
|
||||
DEFAULT_PAGE_SIZE = 100
|
||||
MAXIMUM_MESSAGE_LENGTH = 262144 # 256 KiB
|
||||
MAXIMUM_SMS_MESSAGE_BYTES = 1600 # Amazon limit for a single publish SMS action
|
||||
|
||||
|
||||
class Topic(CloudFormationModel):
|
||||
@ -365,6 +366,7 @@ class SNSBackend(BaseBackend):
|
||||
self.platform_endpoints = {}
|
||||
self.region_name = region_name
|
||||
self.sms_attributes = {}
|
||||
self.sms_messages = OrderedDict()
|
||||
self.opt_out_numbers = [
|
||||
"+447420500600",
|
||||
"+447420505401",
|
||||
@ -432,12 +434,6 @@ class SNSBackend(BaseBackend):
|
||||
except KeyError:
|
||||
raise SNSNotFoundError("Topic with arn {0} not found".format(arn))
|
||||
|
||||
def get_topic_from_phone_number(self, number):
|
||||
for subscription in self.subscriptions.values():
|
||||
if subscription.protocol == "sms" and subscription.endpoint == number:
|
||||
return subscription.topic.arn
|
||||
raise SNSNotFoundError("Could not find valid subscription")
|
||||
|
||||
def set_topic_attribute(self, topic_arn, attribute_name, attribute_value):
|
||||
topic = self.get_topic(topic_arn)
|
||||
setattr(topic, attribute_name, attribute_value)
|
||||
@ -501,11 +497,27 @@ class SNSBackend(BaseBackend):
|
||||
else:
|
||||
return self._get_values_nexttoken(self.subscriptions, next_token)
|
||||
|
||||
def publish(self, arn, message, subject=None, message_attributes=None):
|
||||
def publish(
|
||||
self,
|
||||
message,
|
||||
arn=None,
|
||||
phone_number=None,
|
||||
subject=None,
|
||||
message_attributes=None,
|
||||
):
|
||||
if subject is not None and len(subject) > 100:
|
||||
# Note that the AWS docs around length are wrong: https://github.com/spulec/moto/issues/1503
|
||||
raise ValueError("Subject must be less than 100 characters")
|
||||
|
||||
if phone_number:
|
||||
# This is only an approximation. In fact, we should try to use GSM-7 or UCS-2 encoding to count used bytes
|
||||
if len(message) > MAXIMUM_SMS_MESSAGE_BYTES:
|
||||
raise ValueError("SMS message must be less than 1600 bytes")
|
||||
|
||||
message_id = six.text_type(uuid.uuid4())
|
||||
self.sms_messages[message_id] = (phone_number, message)
|
||||
return message_id
|
||||
|
||||
if len(message) > MAXIMUM_MESSAGE_LENGTH:
|
||||
raise InvalidParameterValue(
|
||||
"An error occurred (InvalidParameter) when calling the Publish operation: Invalid parameter: Message too long"
|
||||
|
@ -6,7 +6,7 @@ from collections import defaultdict
|
||||
from moto.core.responses import BaseResponse
|
||||
from moto.core.utils import camelcase_to_underscores
|
||||
from .models import sns_backends
|
||||
from .exceptions import SNSNotFoundError, InvalidParameterValue
|
||||
from .exceptions import InvalidParameterValue
|
||||
from .utils import is_e164
|
||||
|
||||
|
||||
@ -327,6 +327,7 @@ class SNSResponse(BaseResponse):
|
||||
|
||||
message_attributes = self._parse_message_attributes()
|
||||
|
||||
arn = None
|
||||
if phone_number is not None:
|
||||
# Check phone is correct syntax (e164)
|
||||
if not is_e164(phone_number):
|
||||
@ -336,18 +337,6 @@ class SNSResponse(BaseResponse):
|
||||
),
|
||||
dict(status=400),
|
||||
)
|
||||
|
||||
# Look up topic arn by phone number
|
||||
try:
|
||||
arn = self.backend.get_topic_from_phone_number(phone_number)
|
||||
except SNSNotFoundError:
|
||||
return (
|
||||
self._error(
|
||||
"ParameterValueInvalid",
|
||||
"Could not find topic associated with phone number",
|
||||
),
|
||||
dict(status=400),
|
||||
)
|
||||
elif target_arn is not None:
|
||||
arn = target_arn
|
||||
else:
|
||||
@ -357,7 +346,11 @@ class SNSResponse(BaseResponse):
|
||||
|
||||
try:
|
||||
message_id = self.backend.publish(
|
||||
arn, message, subject=subject, message_attributes=message_attributes
|
||||
message,
|
||||
arn=arn,
|
||||
phone_number=phone_number,
|
||||
subject=subject,
|
||||
message_attributes=message_attributes,
|
||||
)
|
||||
except ValueError as err:
|
||||
error_response = self._error("InvalidParameter", str(err))
|
||||
|
@ -11,8 +11,9 @@ import sure # noqa
|
||||
import responses
|
||||
from botocore.exceptions import ClientError
|
||||
from nose.tools import assert_raises
|
||||
from moto import mock_sns, mock_sqs
|
||||
from moto import mock_sns, mock_sqs, settings
|
||||
from moto.core import ACCOUNT_ID
|
||||
from moto.sns import sns_backend
|
||||
|
||||
MESSAGE_FROM_SQS_TEMPLATE = (
|
||||
'{\n "Message": "%s",\n "MessageId": "%s",\n "Signature": "EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc=",\n "SignatureVersion": "1",\n "SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",\n "Subject": "my subject",\n "Timestamp": "2015-01-01T12:00:00.000Z",\n "TopicArn": "arn:aws:sns:%s:'
|
||||
@ -223,36 +224,31 @@ def test_publish_to_sqs_msg_attr_number_type():
|
||||
@mock_sns
|
||||
def test_publish_sms():
|
||||
client = boto3.client("sns", region_name="us-east-1")
|
||||
client.create_topic(Name="some-topic")
|
||||
resp = client.create_topic(Name="some-topic")
|
||||
arn = resp["TopicArn"]
|
||||
|
||||
client.subscribe(TopicArn=arn, Protocol="sms", Endpoint="+15551234567")
|
||||
|
||||
result = client.publish(PhoneNumber="+15551234567", Message="my message")
|
||||
|
||||
result.should.contain("MessageId")
|
||||
if not settings.TEST_SERVER_MODE:
|
||||
sns_backend.sms_messages.should.have.key(result["MessageId"]).being.equal(
|
||||
("+15551234567", "my message")
|
||||
)
|
||||
|
||||
|
||||
@mock_sns
|
||||
def test_publish_bad_sms():
|
||||
client = boto3.client("sns", region_name="us-east-1")
|
||||
client.create_topic(Name="some-topic")
|
||||
resp = client.create_topic(Name="some-topic")
|
||||
arn = resp["TopicArn"]
|
||||
|
||||
client.subscribe(TopicArn=arn, Protocol="sms", Endpoint="+15551234567")
|
||||
|
||||
try:
|
||||
# Test invalid number
|
||||
# Test invalid number
|
||||
with assert_raises(ClientError) as cm:
|
||||
client.publish(PhoneNumber="NAA+15551234567", Message="my message")
|
||||
except ClientError as err:
|
||||
err.response["Error"]["Code"].should.equal("InvalidParameter")
|
||||
cm.exception.response["Error"]["Code"].should.equal("InvalidParameter")
|
||||
cm.exception.response["Error"]["Message"].should.contain("not meet the E164")
|
||||
|
||||
try:
|
||||
# Test not found number
|
||||
client.publish(PhoneNumber="+44001234567", Message="my message")
|
||||
except ClientError as err:
|
||||
err.response["Error"]["Code"].should.equal("ParameterValueInvalid")
|
||||
# Test to long ASCII message
|
||||
with assert_raises(ClientError) as cm:
|
||||
client.publish(PhoneNumber="+15551234567", Message="a" * 1601)
|
||||
cm.exception.response["Error"]["Code"].should.equal("InvalidParameter")
|
||||
cm.exception.response["Error"]["Message"].should.contain("must be less than 1600")
|
||||
|
||||
|
||||
@mock_sqs
|
||||
|
Loading…
Reference in New Issue
Block a user