Repository: localstack/localstack

Issue:
maxReceiveCount is returned as a string
`maxReceiveCount` is not always converted to an integer when reading the attributes. If the redrivePolicy is set using `set-attributes` after the queue is created then `maxReceiveCount` is returned as a string. However, if the redrivePolicy is set during queue creation (i.e. using `create-queue` then `maxReceiveCount` is returned as an integer as expected.

**Step to reproduce:**

- create a queue `foo`
`awslocal sqs create-queue --queue-name foo`
- create a DLQ `foo-DLQ`
`awslocal sqs create-queue --queue-name foo-DLQ`
- create an `foo-attributes.json` with following content
`
{
  "RedrivePolicy": "{\"deadLetterTargetArn\":\"arn:aws:sqs:elasticmq:000000000000:foo-DLQ\",\"maxReceiveCount\":\"10\"}"
}
`
- update queue `foo` attributes
`awslocal sqs set-queue-attributes --queue-url http://localhost:4576/queue/foo --attributes file://foo-attributes.json`
- get queue `foo` attributes
`awslocal sqs get-queue-attributes --queue-url http://localhost:4576/queue/foo --attribute-names All`

**Current result:**
`maxReceiveCount` is returned as a string

    {
        "Attributes": {
            "ApproximateNumberOfMessagesNotVisible": "0",
            "RedrivePolicy": "{\"deadLetterTargetArn\":\"arn:aws:sqs:elasticmq:000000000000:foo-DLQ\",\"maxReceiveCount\":\"10\"}",
            "MessageRetentionPeriod": "259200",
            "ApproximateNumberOfMessagesDelayed": "0",
            "MaximumMessageSize": "131072",
            "CreatedTimestamp": "1574356609",
            "ApproximateNumberOfMessages": "0",
            "ReceiveMessageWaitTimeSeconds": "0",
            "DelaySeconds": "0",
            "VisibilityTimeout": "30",
            "LastModifiedTimestamp": "1574356609",
            "QueueArn": "arn:aws:sqs:us-east-1:000000000000:foo"
        }
    }

**Expected behavior:**
`maxReceiveCount` is returned as an integer

    {
        "Attributes": {
            "ApproximateNumberOfMessagesNotVisible": "0",
            "RedrivePolicy": "{\"deadLetterTargetArn\":\"arn:aws:sqs:elasticmq:000000000000:foo-DLQ\",\"maxReceiveCount\":10}",
            "MessageRetentionPeriod": "259200",
            "ApproximateNumberOfMessagesDelayed": "0",
            "MaximumMessageSize": "131072",
            "CreatedTimestamp": "1574356609",
            "ApproximateNumberOfMessages": "0",
            "ReceiveMessageWaitTimeSeconds": "0",
            "DelaySeconds": "0",
            "VisibilityTimeout": "30",
            "LastModifiedTimestamp": "1574356609",
            "QueueArn": "arn:aws:sqs:us-east-1:000000000000:foo"
            }
    }

**Versions:**
- affected: 0.10.3, 0.10.4, 0.10.5
- non affected: 0.9.0, 0.10.0, 0.10.2



Discussion:

Resolving patch:
diff --git a/localstack/services/sqs/sqs_listener.py b/localstack/services/sqs/sqs_listener.py
--- a/localstack/services/sqs/sqs_listener.py
+++ b/localstack/services/sqs/sqs_listener.py
@@ -1,5 +1,6 @@
 import re
 import uuid
+import json
 import xmltodict
 from moto.sqs.utils import parse_message_attributes
 from moto.sqs.models import Message, TRANSPORT_TYPE_ENCODINGS
@@ -36,6 +37,7 @@
                          'ContentBasedDeduplication', 'KmsMasterKeyId', 'KmsDataKeyReusePeriodSeconds',
                          'CreatedTimestamp', 'LastModifiedTimestamp', 'FifoQueue',
                          'ApproximateNumberOfMessages', 'ApproximateNumberOfMessagesNotVisible']
+
 UNSUPPORTED_ATTRIBUTE_NAMES = [
     'DelaySeconds', 'MaximumMessageSize', 'MessageRetentionPeriod', 'Policy', 'RedrivePolicy',
     'ContentBasedDeduplication', 'KmsMasterKeyId', 'KmsDataKeyReusePeriodSeconds']
@@ -291,7 +293,19 @@ def _send_message(self, path, data, req_data, headers):
     def _set_queue_attributes(self, queue_url, req_data):
         attrs = self._format_attributes(req_data)
         # select only the attributes in UNSUPPORTED_ATTRIBUTE_NAMES
-        local_attrs = dict([(k, v) for k, v in attrs.items() if k in UNSUPPORTED_ATTRIBUTE_NAMES])
+        local_attrs = {}
+        for k, v in attrs.items():
+            if k in UNSUPPORTED_ATTRIBUTE_NAMES:
+                try:
+                    _v = json.loads(v)
+                    if isinstance(_v, dict):
+                        if 'maxReceiveCount' in _v:
+                            _v['maxReceiveCount'] = int(_v['maxReceiveCount'])
+
+                    local_attrs.update(dict({k: json.dumps(_v)}))
+                except Exception:
+                    local_attrs.update(dict({k: v}))
+
         QUEUE_ATTRIBUTES[queue_url] = QUEUE_ATTRIBUTES.get(queue_url) or {}
         QUEUE_ATTRIBUTES[queue_url].update(local_attrs)
         forward_attrs = dict([(k, v) for k, v in attrs.items() if k not in UNSUPPORTED_ATTRIBUTE_NAMES])


Test patch:
diff --git a/tests/integration/test_sqs.py b/tests/integration/test_sqs.py
--- a/tests/integration/test_sqs.py
+++ b/tests/integration/test_sqs.py
@@ -201,21 +201,40 @@ def test_set_queue_attribute_at_creation(self):
     def test_get_specific_queue_attribute_response(self):
         queue_name = 'queue-%s' % short_uid()
 
-        # Two attributes unsupported by ElasticMq
+        dead_queue_url = self.client.create_queue(QueueName='newQueue')['QueueUrl']
+        supported_attribute_get = self.client.get_queue_attributes(QueueUrl=dead_queue_url,
+                                                                   AttributeNames=['QueueArn'])
+
+        self.assertTrue('QueueArn' in supported_attribute_get['Attributes'].keys())
+        dead_queue_arn = supported_attribute_get['Attributes']['QueueArn']
+
+        _redrive_policy = {
+            'deadLetterTargetArn': dead_queue_arn,
+            'maxReceiveCount': '10'
+        }
+
         attributes = {
             'MessageRetentionPeriod': '604800',
             'DelaySeconds': '10',
+            'RedrivePolicy': json.dumps(_redrive_policy)
         }
 
         queue_url = self.client.create_queue(QueueName=queue_name, Attributes=attributes)['QueueUrl']
-        unsupported_attribute_get = self.client.get_queue_attributes(QueueUrl=queue_url,
-                                                                     AttributeNames=['MessageRetentionPeriod'])
+        unsupported_attribute_get = self.client.get_queue_attributes(
+            QueueUrl=queue_url,
+            AttributeNames=['MessageRetentionPeriod', 'RedrivePolicy']
+        )
         supported_attribute_get = self.client.get_queue_attributes(QueueUrl=queue_url,
                                                                    AttributeNames=['QueueArn'])
         # assertion
         self.assertTrue('MessageRetentionPeriod' in unsupported_attribute_get['Attributes'].keys())
         self.assertEqual('604800', unsupported_attribute_get['Attributes']['MessageRetentionPeriod'])
         self.assertTrue('QueueArn' in supported_attribute_get['Attributes'].keys())
+        self.assertTrue('RedrivePolicy' in unsupported_attribute_get['Attributes'].keys())
+
+        redrive_policy = json.loads(unsupported_attribute_get['Attributes']['RedrivePolicy'])
+        self.assertTrue(isinstance(redrive_policy['maxReceiveCount'], int))
 
         # cleanup
         self.client.delete_queue(QueueUrl=queue_url)
+        self.client.delete_queue(QueueUrl=dead_queue_url)