示例#1
0
 def _revert_virtual_host_style(self, request: HttpRequest):
     # extract the bucket name from the host part of the request
     bucket_name = request.host.split(".")[0]
     # split the url and put the bucket name at the front
     parts = urlsplit(request.url)
     path_parts = parts.path.split("/")
     path_parts = [bucket_name] + path_parts
     path_parts = [part for part in path_parts if part]
     path = "/" + "/".join(path_parts) or "/"
     # set the path with the bucket name in the front at the request
     # TODO directly modifying the request can cause issues with our handler chain, instead clone the HTTP request
     request.path = path
     request.raw_path = path
示例#2
0
def test_query_parser_non_flattened_list_structure_changed_name():
    """Simple test with a non-flattened list structure where the name of the list differs from the shape's name
    (CloudWatch PutMetricData)."""
    parser = QueryRequestParser(load_service("cloudwatch"))
    request = HttpRequest(
        body=to_bytes(
            "Action=PutMetricData&"
            "Version=2010-08-01&"
            "Namespace=TestNamespace&"
            "MetricData.member.1.MetricName=buffers&"
            "MetricData.member.1.Unit=Bytes&"
            "MetricData.member.1.Value=231434333&"
            "MetricData.member.1.Dimensions.member.1.Name=InstanceType&"
            "MetricData.member.1.Dimensions.member.1.Value=m1.small&"
            "AUTHPARAMS"
        ),
        method="POST",
        headers={},
        path="",
    )
    operation, params = parser.parse(request)
    assert operation.name == "PutMetricData"
    assert params == {
        "MetricData": [
            {
                "Dimensions": [{"Name": "InstanceType", "Value": "m1.small"}],
                "MetricName": "buffers",
                "Unit": "Bytes",
                "Value": 231434333.0,
            }
        ],
        "Namespace": "TestNamespace",
    }
示例#3
0
def test_query_parser_flattened_list_structure():
    """Simple test with a flattened list of structures."""
    parser = QueryRequestParser(load_service("sqs"))
    request = HttpRequest(
        body=to_bytes(
            "Action=DeleteMessageBatch&"
            "Version=2012-11-05&"
            "QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&"
            "DeleteMessageBatchRequestEntry.1.Id=bar&"
            "DeleteMessageBatchRequestEntry.1.ReceiptHandle=foo&"
            "DeleteMessageBatchRequestEntry.2.Id=bar&"
            "DeleteMessageBatchRequestEntry.2.ReceiptHandle=foo"),
        method="POST",
        headers={},
        path="",
    )
    operation, params = parser.parse(request)
    assert operation.name == "DeleteMessageBatch"
    assert params == {
        "QueueUrl":
        "http://localhost:4566/000000000000/tf-acc-test-queue",
        "Entries": [{
            "Id": "bar",
            "ReceiptHandle": "foo"
        }, {
            "Id": "bar",
            "ReceiptHandle": "foo"
        }],
    }
示例#4
0
def test_dispatch_missing_method_returns_internal_failure():
    table: DispatchTable = {}

    sqs_service = load_service("sqs")
    skeleton = Skeleton(sqs_service, table)

    context = RequestContext()
    context.account = "test"
    context.region = "us-west-1"
    context.service = sqs_service
    context.request = HttpRequest(
        **{
            "method": "POST",
            "path": "/",
            "body":
            "Action=DeleteQueue&Version=2012-11-05&QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue",
            "headers": _get_sqs_request_headers(),
        })

    result = skeleton.invoke(context)
    # Use the parser from botocore to parse the serialized response
    response_parser = create_parser(sqs_service.protocol)
    parsed_response = response_parser.parse(
        result.to_readonly_response_dict(),
        sqs_service.operation_model("SendMessage").output_shape)
    assert "Error" in parsed_response
    assert parsed_response["Error"] == {
        "Code":
        "InternalFailure",
        "Message":
        "API action 'DeleteQueue' for service 'sqs' not yet implemented",
    }
示例#5
0
def test_dispatch_common_service_exception():
    def delete_queue(_context: RequestContext, _request: ServiceRequest):
        raise CommonServiceException("NonExistentQueue", "No such queue")

    table: DispatchTable = {}
    table["DeleteQueue"] = delete_queue

    sqs_service = load_service("sqs")
    skeleton = Skeleton(sqs_service, table)

    context = RequestContext()
    context.account = "test"
    context.region = "us-west-1"
    context.service = sqs_service
    context.request = HttpRequest(
        **{
            "method": "POST",
            "path": "/",
            "body":
            "Action=DeleteQueue&Version=2012-11-05&QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue",
            "headers": _get_sqs_request_headers(),
        })
    result = skeleton.invoke(context)

    # Use the parser from botocore to parse the serialized response
    response_parser = create_parser(sqs_service.protocol)
    parsed_response = response_parser.parse(
        result.to_readonly_response_dict(),
        sqs_service.operation_model("SendMessage").output_shape)

    assert "Error" in parsed_response
    assert parsed_response["Error"] == {
        "Code": "NonExistentQueue",
        "Message": "No such queue",
    }
示例#6
0
def _botocore_parser_integration_test(service: str,
                                      action: str,
                                      method: str = None,
                                      request_uri: str = None,
                                      headers: dict = None,
                                      expected: dict = None,
                                      **kwargs):
    # Load the appropriate service
    service = load_service(service)
    # Use the serializer from botocore to serialize the request params
    serializer = create_serializer(service.protocol)

    serialized_request = serializer.serialize_to_request(
        kwargs, service.operation_model(action))
    body = serialized_request["body"]

    if service.protocol in ["query", "ec2"]:
        # Serialize the body as query parameter
        body = urlencode(serialized_request["body"])

    # Use our parser to parse the serialized body
    parser = create_parser(service)
    operation_model, parsed_request = parser.parse(
        HttpRequest(method=method or "GET",
                    path=request_uri or "",
                    headers=headers,
                    body=body))

    # Check if the result is equal to the given "expected" dict or the kwargs (if "expected" has not been set)
    assert parsed_request == (expected or kwargs)
示例#7
0
 def parse(self, request: HttpRequest) -> Tuple[OperationModel, Any]:
     body = request.get_data(as_text=True)
     instance = parse_qs(body, keep_blank_values=True)
     if not instance:
         # if the body does not contain any information, fallback to the actual query parameters
         instance = request.args
     # The query parsing returns a list for each entry in the dict (this is how HTTP handles lists in query params).
     # However, the AWS Query format does not have any duplicates.
     # Therefore we take the first element of each entry in the dict.
     instance = {k: self._get_first(v) for k, v in instance.items()}
     if "Action" not in instance:
         raise ProtocolParserError(
             f"Operation detection failed. "
             f"Missing Action in request for query-protocol service {self.service}."
         )
     action = instance["Action"]
     try:
         operation: OperationModel = self.service.operation_model(action)
     except OperationNotFoundError as e:
         raise OperationNotFoundParserError(
             f"Operation detection failed."
             f"Operation {action} could not be found for service {self.service}."
         ) from e
     # There are no uri params in the query protocol (all ops are POST on "/")
     uri_params = {}
     input_shape: StructureShape = operation.input_shape
     parsed = self._parse_shape(request, input_shape, instance, uri_params)
     if parsed is None:
         return operation, {}
     return operation, parsed
示例#8
0
def test_query_parser_non_flattened_list_structure():
    """Simple test with a non-flattened list structure (CloudFormation CreateChangeSet)."""
    parser = QueryRequestParser(load_service("cloudformation"))
    request = HttpRequest(
        body=to_bytes(
            "Action=CreateChangeSet&"
            "ChangeSetName=SampleChangeSet&"
            "Parameters.member.1.ParameterKey=KeyName&"
            "Parameters.member.1.UsePreviousValue=true&"
            "Parameters.member.2.ParameterKey=Purpose&"
            "Parameters.member.2.ParameterValue=production&"
            "StackName=arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack/1a2345b6-0000-00a0-a123-00abc0abc000&"
            "UsePreviousTemplate=true&"
            "Version=2010-05-15&"
            "X-Amz-Algorithm=AWS4-HMAC-SHA256&"
            "X-Amz-Credential=[Access-key-ID-and-scope]&"
            "X-Amz-Date=20160316T233349Z&"
            "X-Amz-SignedHeaders=content-type;host&"
            "X-Amz-Signature=[Signature]"
        ),
        method="POST",
        headers={},
        path="",
    )
    operation, params = parser.parse(request)
    assert operation.name == "CreateChangeSet"
    assert params == {
        "StackName": "arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack/1a2345b6-0000-00a0-a123-00abc0abc000",
        "UsePreviousTemplate": True,
        "Parameters": [
            {"ParameterKey": "KeyName", "UsePreviousValue": True},
            {"ParameterKey": "Purpose", "ParameterValue": "production"},
        ],
        "ChangeSetName": "SampleChangeSet",
    }
示例#9
0
def test_s3_virtual_host_addressing():
    """Test the parsing of a map with the location trait 'headers'."""
    request = HttpRequest(
        method="PUT", headers={"host": s3_utils.get_bucket_hostname("test-bucket")}
    )
    parser = create_parser(load_service("s3"))
    parsed_operation_model, parsed_request = parser.parse(request)
    assert parsed_operation_model.name == "CreateBucket"
    assert "Bucket" in parsed_request
    assert parsed_request["Bucket"] == "test-bucket"
示例#10
0
    def forward_request(self, method, path, data, headers):
        request = HttpRequest(
            method=method,
            path=path,
            headers=headers,
            body=data,
        )

        context = self.create_request_context(request)
        response = self.skeleton.invoke(context)
        return self.to_server_response(response)
示例#11
0
def test_restjson_parser_path_params_with_slashes():
    parser = RestJSONRequestParser(load_service("qldb"))
    resource_arn = "arn:aws:qldb:eu-central-1:000000000000:ledger/c-c67c827a"
    request = HttpRequest(
        body=b"",
        method="GET",
        headers={},
        path=f"/tags/{resource_arn}",
    )
    operation, params = parser.parse(request)
    assert operation.name == "ListTagsForResource"
    assert params == {"ResourceArn": resource_arn}
示例#12
0
    def parse(self, request: HttpRequest) -> Tuple[OperationModel, Any]:
        body = request.get_data(as_text=True)
        instance = parse_qs(body, keep_blank_values=True)
        # The query parsing returns a list for each entry in the dict (this is how HTTP handles lists in query params).
        # However, the AWS Query format does not have any duplicates.
        # Therefore we take the first element of each entry in the dict.
        instance = {k: self._get_first(v) for k, v in instance.items()}
        operation: OperationModel = self.service.operation_model(
            instance["Action"])
        input_shape: StructureShape = operation.input_shape

        return operation, self._parse_shape(request, input_shape, instance)
示例#13
0
    def forward_request(self, method, path, data, headers):
        split_url = urlsplit(path)
        request = HttpRequest(
            method=method,
            path=split_url.path,
            query_string=split_url.query,
            headers=headers,
            body=data,
        )

        context = self.create_request_context(request)
        response = self.skeleton.invoke(context)
        return self.to_server_response(response)
示例#14
0
 def _parse_body_as_json(self, request: HttpRequest) -> dict:
     body_contents = request.data
     if not body_contents:
         return {}
     if request.mimetype.startswith("application/x-amz-cbor"):
         try:
             return cbor2.loads(body_contents)
         except ValueError as e:
             raise ProtocolParserError("HTTP body could not be parsed as CBOR.") from e
     else:
         try:
             return request.get_json(force=True)
         except BadRequest as e:
             raise ProtocolParserError("HTTP body could not be parsed as JSON.") from e
示例#15
0
def test_parser_error_on_protocol_error():
    """Test that the parser raises a ProtocolParserError in case of invalid data to parse."""
    parser = QueryRequestParser(load_service("sqs"))
    request = HttpRequest(
        body=to_bytes(
            "Action=UnknownOperation&Version=2012-11-05&"
            "QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&"
            "MessageBody=%7B%22foo%22%3A+%22bared%22%7D&"
            "DelaySeconds=2"
        ),
        method="POST",
        headers={},
        path="",
    )
    with pytest.raises(ProtocolParserError):
        parser.parse(request)
示例#16
0
def _botocore_parser_integration_test(
    service: str, action: str, headers: dict = None, expected: dict = None, **kwargs
):
    # Load the appropriate service
    service = load_service(service)
    # Use the serializer from botocore to serialize the request params
    serializer = create_serializer(service.protocol)

    operation_model = service.operation_model(action)
    serialized_request = serializer.serialize_to_request(kwargs, operation_model)
    prepare_request_dict(serialized_request, "")
    split_url = urlsplit(serialized_request.get("url"))
    path = split_url.path
    query_string = split_url.query
    body = serialized_request["body"]
    # use custom headers (if provided), or headers from serialized request as default
    headers = serialized_request.get("headers") if headers is None else headers

    if service.protocol in ["query", "ec2"]:
        # Serialize the body as query parameter
        body = urlencode(serialized_request["body"])

    # Use our parser to parse the serialized body
    parser = create_parser(service)
    parsed_operation_model, parsed_request = parser.parse(
        HttpRequest(
            method=serialized_request.get("method") or "GET",
            path=path,
            query_string=to_str(query_string),
            headers=headers,
            body=body,
        )
    )

    # Check if the determined operation_model is correct
    assert parsed_operation_model == operation_model

    # Check if the result is equal to the given "expected" dict or the kwargs (if "expected" has not been set)
    expected = expected or kwargs
    # The parser adds None for none-existing members on purpose. Remove those for the assert
    expected = {key: value for key, value in expected.items() if value is not None}
    parsed_request = {key: value for key, value in parsed_request.items() if value is not None}
    assert parsed_request == expected
示例#17
0
def test_query_parser():
    """Basic test for the QueryParser with a simple example (SQS SendMessage request)."""
    parser = QueryRequestParser(load_service("sqs"))
    request = HttpRequest(
        body=to_bytes(
            "Action=SendMessage&Version=2012-11-05&"
            "QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&"
            "MessageBody=%7B%22foo%22%3A+%22bared%22%7D&"
            "DelaySeconds=2"),
        method="POST",
        headers={},
        path="",
    )
    operation, params = parser.parse(request)
    assert operation.name == "SendMessage"
    assert params == {
        "QueueUrl": "http://localhost:4566/000000000000/tf-acc-test-queue",
        "MessageBody": '{"foo": "bared"}',
        "DelaySeconds": 2,
    }
示例#18
0
        def _set_request_props(request: HttpRequest, path: str, host: str):
            """Sets the HTTP request's path and host and clears the cache in the request object."""
            request.path = path
            request.headers["Host"] = host

            try:
                # delete the werkzeug request property cache that depends on path, but make sure all of them are
                # initialized first, otherwise `del` will raise a key error
                request.host = None  # noqa
                request.url = None  # noqa
                request.base_url = None  # noqa
                request.full_path = None  # noqa
                request.host_url = None  # noqa
                request.root_url = None  # noqa
                del request.host  # noqa
                del request.url  # noqa
                del request.base_url  # noqa
                del request.full_path  # noqa
                del request.host_url  # noqa
                del request.root_url  # noqa
            except AttributeError:
                pass
示例#19
0
def test_skeleton_e2e_sqs_send_message():
    sqs_service = load_service("sqs")
    skeleton = Skeleton(sqs_service, TestSqsApi())
    context = RequestContext()
    context.account = "test"
    context.region = "us-west-1"
    context.service = sqs_service
    context.request = HttpRequest(
        **{
            "method": "POST",
            "path": "/",
            "body":
            "Action=SendMessage&Version=2012-11-05&QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&MessageBody=%7B%22foo%22%3A+%22bared%22%7D&DelaySeconds=2",
            "headers": _get_sqs_request_headers(),
        })
    result = skeleton.invoke(context)

    # Use the parser from botocore to parse the serialized response
    response_parser = create_parser(sqs_service.protocol)
    parsed_response = response_parser.parse(
        result.to_readonly_response_dict(),
        sqs_service.operation_model("SendMessage").output_shape)

    # Test the ResponseMetadata and delete it afterwards
    assert "ResponseMetadata" in parsed_response
    assert "RequestId" in parsed_response["ResponseMetadata"]
    assert len(parsed_response["ResponseMetadata"]["RequestId"]) == 52
    assert "HTTPStatusCode" in parsed_response["ResponseMetadata"]
    assert parsed_response["ResponseMetadata"]["HTTPStatusCode"] == 200
    del parsed_response["ResponseMetadata"]

    # Compare the (remaining) actual payload
    assert parsed_response == {
        "MD5OfMessageBody": "String",
        "MD5OfMessageAttributes": "String",
        "MD5OfMessageSystemAttributes": "String",
        "MessageId": "String",
        "SequenceNumber": "String",
    }
示例#20
0
def _botocore_parser_integration_test(service: str,
                                      action: str,
                                      headers: dict = None,
                                      expected: dict = None,
                                      **kwargs):
    # Load the appropriate service
    service = load_service(service)
    # Use the serializer from botocore to serialize the request params
    serializer = create_serializer(service.protocol)

    operation_model = service.operation_model(action)
    serialized_request = serializer.serialize_to_request(
        kwargs, operation_model)
    body = serialized_request["body"]
    query_string = urlencode(serialized_request.get("query_string") or "",
                             doseq=False)
    # use custom headers (if provided), or headers from serialized request as default
    headers = serialized_request.get("headers") if headers is None else headers

    if service.protocol in ["query", "ec2"]:
        # Serialize the body as query parameter
        body = urlencode(serialized_request["body"])

    # Use our parser to parse the serialized body
    parser = create_parser(service)
    parsed_operation_model, parsed_request = parser.parse(
        HttpRequest(
            method=serialized_request.get("method") or "GET",
            path=serialized_request.get("url_path") or "",
            query_string=query_string,
            headers=headers,
            body=body,
        ))

    # Check if the determined operation_model is correct
    assert parsed_operation_model == operation_model

    # Check if the result is equal to the given "expected" dict or the kwargs (if "expected" has not been set)
    assert parsed_request == (expected or kwargs)
示例#21
0
def test_query_parser_non_flattened_map():
    """Simple test with a flattened map (SQS SetQueueAttributes request)."""
    parser = QueryRequestParser(load_service("sns"))
    request = HttpRequest(
        body=to_bytes(
            "Action=SetEndpointAttributes&"
            "EndpointArn=arn%3Aaws%3Asns%3Aus-west-2%3A123456789012%3Aendpoint%2FGCM%2Fgcmpushapp%2F5e3e9847-3183-3f18-a7e8-671c3a57d4b3&"
            "Attributes.entry.1.key=CustomUserData&"
            "Attributes.entry.1.value=My+custom+userdata&"
            "Version=2010-03-31&"
            "AUTHPARAMS"
        ),
        method="POST",
        headers={},
        path="",
    )
    operation, params = parser.parse(request)
    assert operation.name == "SetEndpointAttributes"
    assert params == {
        "Attributes": {"CustomUserData": "My custom userdata"},
        "EndpointArn": "arn:aws:sns:us-west-2:123456789012:endpoint/GCM/gcmpushapp/5e3e9847-3183-3f18-a7e8-671c3a57d4b3",
    }
示例#22
0
def test_skeleton_e2e_sqs_send_message_not_implemented():
    sqs_service = load_service("sqs")
    skeleton = Skeleton(sqs_service, TestSqsApiNotImplemented())
    context = RequestContext()
    context.account = "test"
    context.region = "us-west-1"
    context.service = sqs_service
    context.request = HttpRequest(
        **{
            "method": "POST",
            "path": "/",
            "body":
            "Action=SendMessage&Version=2012-11-05&QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&MessageBody=%7B%22foo%22%3A+%22bared%22%7D&DelaySeconds=2",
            "headers": _get_sqs_request_headers(),
        })
    result = skeleton.invoke(context)

    # Use the parser from botocore to parse the serialized response
    response_parser = create_parser(sqs_service.protocol)
    parsed_response = response_parser.parse(
        result.to_readonly_response_dict(),
        sqs_service.operation_model("SendMessage").output_shape)

    # Test the ResponseMetadata
    assert "ResponseMetadata" in parsed_response
    assert "RequestId" in parsed_response["ResponseMetadata"]
    assert len(parsed_response["ResponseMetadata"]["RequestId"]) == 52
    assert "HTTPStatusCode" in parsed_response["ResponseMetadata"]
    assert parsed_response["ResponseMetadata"]["HTTPStatusCode"] == 501

    # Compare the (remaining) actual eror payload
    assert "Error" in parsed_response
    assert parsed_response["Error"] == {
        "Code":
        "InternalFailure",
        "Message":
        "API action 'SendMessage' for service 'sqs' not yet implemented",
    }
示例#23
0
def test_parser_error_on_unknown_error():
    """Test that the parser raises a UnknownParserError in case of an unknown exception."""
    parser = QueryRequestParser(load_service("sqs"))

    request = HttpRequest(
        body=to_bytes(
            "Action=SendMessage&Version=2012-11-05&"
            "QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&"
            "MessageBody=%7B%22foo%22%3A+%22bared%22%7D&"
            "DelaySeconds=2"
        ),
        method="POST",
        headers={},
        path="",
    )

    # An unknown error is obviously hard to trigger (because we would fix it if we would know of a way to trigger it),
    # therefore we patch a function to raise an unexpected error
    def raise_error(*args, **kwargs):
        raise NotImplementedError()

    parser._process_member = raise_error
    with pytest.raises(UnknownParserError):
        parser.parse(request)
示例#24
0
def create_http_request(aws_request: AWSPreparedRequest) -> HttpRequest:
    # create HttpRequest from AWSRequest
    split_url = urlsplit(aws_request.url)
    host = split_url.netloc.split(":")
    if len(host) == 1:
        server = (to_str(host[0]), None)
    elif len(host) == 2:
        server = (to_str(host[0]), int(host[1]))
    else:
        raise ValueError

    # prepare the RequestContext
    headers = Headers()
    for k, v in aws_request.headers.items():
        headers[k] = v

    return HttpRequest(
        method=aws_request.method,
        path=split_url.path,
        query_string=split_url.query,
        headers=headers,
        body=aws_request.body,
        server=server,
    )
示例#25
0
def test_query_parser_flattened_map():
    """Simple test with a flattened map (SQS SetQueueAttributes request)."""
    parser = QueryRequestParser(load_service("sqs"))
    request = HttpRequest(
        body=to_bytes(
            "Action=SetQueueAttributes&Version=2012-11-05&"
            "QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&"
            "Attribute.1.Name=DelaySeconds&"
            "Attribute.1.Value=10&"
            "Attribute.2.Name=MaximumMessageSize&"
            "Attribute.2.Value=131072&"
            "Attribute.3.Name=MessageRetentionPeriod&"
            "Attribute.3.Value=259200&"
            "Attribute.4.Name=ReceiveMessageWaitTimeSeconds&"
            "Attribute.4.Value=20&"
            "Attribute.5.Name=RedrivePolicy&"
            "Attribute.5.Value=%7B%22deadLetterTargetArn%22%3A%22arn%3Aaws%3Asqs%3Aus-east-1%3A80398EXAMPLE%3AMyDeadLetterQueue%22%2C%22maxReceiveCount%22%3A%221000%22%7D&"
            "Attribute.6.Name=VisibilityTimeout&Attribute.6.Value=60"
        ),
        method="POST",
        headers={},
        path="",
    )
    operation, params = parser.parse(request)
    assert operation.name == "SetQueueAttributes"
    assert params == {
        "QueueUrl": "http://localhost:4566/000000000000/tf-acc-test-queue",
        "Attributes": {
            "DelaySeconds": "10",
            "MaximumMessageSize": "131072",
            "MessageRetentionPeriod": "259200",
            "ReceiveMessageWaitTimeSeconds": "20",
            "RedrivePolicy": '{"deadLetterTargetArn":"arn:aws:sqs:us-east-1:80398EXAMPLE:MyDeadLetterQueue","maxReceiveCount":"1000"}',
            "VisibilityTimeout": "60",
        },
    }