Repository: getmoto/moto

Issue:
SNS Message Attribute numbertype Validation fails on 1.3.14 (worked on 1.3.13)
When Publishing an SNS Message with an attribute of Number Type and a stringvalue, v1.3.14 now fails. Worked fine with 1.3.13. On python 3.7, boto3-1.10.18

The main error is: `botocore.exceptions.ClientError: An error occurred (InvalidParameterValue) when calling the Publish operation: The message attribute 'retries' must contain non-empty message attribute value for message attribute type 'N'.`

This is the message attribute block I'm using for my object:
```
"Attributes": {
   "retries": {"DataType": "Number", "StringValue": '0'},
   "event_type": {"DataType": "String", "StringValue": EVENT_TYPE},
},
```

Here's the stack back from the attempt:
```
self = <botocore.client.SNS object at 0x1212961d0>, operation_name = 'Publish'
api_params = {'Message': '{"program_id": "random-guid-goes-here", "filename": "HCSCFlu2019.json", "retries": "0", "phone_number": "...r', 'StringValue': '0'}}, 'Subject': 'EnrollmentPipeline', 'TopicArn': 'arn:aws:sns:us-east-2:123456789012:test_topic'}

    def _make_api_call(self, operation_name, api_params):
        operation_model = self._service_model.operation_model(operation_name)
        service_name = self._service_model.service_name
        history_recorder.record('API_CALL', {
            'service': service_name,
            'operation': operation_name,
            'params': api_params,
        })
        if operation_model.deprecated:
            logger.debug('Warning: %s.%s() is deprecated',
                         service_name, operation_name)
        request_context = {
            'client_region': self.meta.region_name,
            'client_config': self.meta.config,
            'has_streaming_input': operation_model.has_streaming_input,
            'auth_type': operation_model.auth_type,
        }
        request_dict = self._convert_to_request_dict(
            api_params, operation_model, context=request_context)
    
        service_id = self._service_model.service_id.hyphenize()
        handler, event_response = self.meta.events.emit_until_response(
            'before-call.{service_id}.{operation_name}'.format(
                service_id=service_id,
                operation_name=operation_name),
            model=operation_model, params=request_dict,
            request_signer=self._request_signer, context=request_context)
    
        if event_response is not None:
            http, parsed_response = event_response
        else:
            http, parsed_response = self._make_request(
                operation_model, request_dict, request_context)
    
        self.meta.events.emit(
            'after-call.{service_id}.{operation_name}'.format(
                service_id=service_id,
                operation_name=operation_name),
            http_response=http, parsed=parsed_response,
            model=operation_model, context=request_context
        )
    
        if http.status_code >= 300:
            error_code = parsed_response.get("Error", {}).get("Code")
            error_class = self.exceptions.from_code(error_code)
>           raise error_class(parsed_response, operation_name)
E           botocore.exceptions.ClientError: An error occurred (InvalidParameterValue) when calling the Publish operation: The message attribute 'retries' must contain non-empty message attribute value for message attribute type 'N'.

/usr/local/lib/python3.7/site-packages/botocore/client.py:661: ClientError
```


Discussion:

Resolving patch:
diff --git a/moto/sns/responses.py b/moto/sns/responses.py
--- a/moto/sns/responses.py
+++ b/moto/sns/responses.py
@@ -77,7 +77,7 @@ def _parse_message_attributes(self, prefix="", value_namespace="Value."):
                     transform_value = value["StringValue"]
             elif "BinaryValue" in value:
                 transform_value = value["BinaryValue"]
-            if not transform_value:
+            if transform_value == "":
                 raise InvalidParameterValue(
                     "The message attribute '{0}' must contain non-empty "
                     "message attribute value for message attribute "


Test patch:
diff --git a/tests/test_sns/test_publishing_boto3.py b/tests/test_sns/test_publishing_boto3.py
--- a/tests/test_sns/test_publishing_boto3.py
+++ b/tests/test_sns/test_publishing_boto3.py
@@ -173,6 +173,27 @@ def test_publish_to_sqs_msg_attr_byte_value():
     )
 
 
+@mock_sqs
+@mock_sns
+def test_publish_to_sqs_msg_attr_number_type():
+    sns = boto3.resource("sns", region_name="us-east-1")
+    topic = sns.create_topic(Name="test-topic")
+    sqs = boto3.resource("sqs", region_name="us-east-1")
+    queue = sqs.create_queue(QueueName="test-queue")
+    topic.subscribe(Protocol="sqs", Endpoint=queue.attributes["QueueArn"])
+
+    topic.publish(
+        Message="test message",
+        MessageAttributes={"retries": {"DataType": "Number", "StringValue": "0"}},
+    )
+
+    message = json.loads(queue.receive_messages()[0].body)
+    message["Message"].should.equal("test message")
+    message["MessageAttributes"].should.equal(
+        {"retries": {"Type": "Number", "Value": 0}}
+    )
+
+
 @mock_sns
 def test_publish_sms():
     client = boto3.client("sns", region_name="us-east-1")