Example #1
0
 def _serialize_error(
     self,
     error: ServiceException,
     code: str,
     sender_fault: bool,
     serialized: HttpResponse,
     shape: Shape,
     operation_model: OperationModel,
 ) -> None:
     # EC2 errors look like:
     # <Response>
     #   <Errors>
     #     <Error>
     #       <Code>InvalidInstanceID.Malformed</Code>
     #       <Message>Invalid id: "1343124"</Message>
     #     </Error>
     #   </Errors>
     #   <RequestID>12345</RequestID>
     # </Response>
     # This is different from QueryParser in that it's RequestID, not RequestId
     # and that the Error tag is in an enclosing Errors tag.
     attr = ({
         "xmlns": operation_model.metadata.get("xmlNamespace")
     } if "xmlNamespace" in operation_model.metadata else None)
     root = ETree.Element("Errors", attr)
     error_tag = ETree.SubElement(root, "Error")
     self._add_error_tags(code, error, error_tag, sender_fault)
     request_id = ETree.SubElement(root, "RequestID")
     request_id.text = gen_amzn_requestid_long()
     serialized.data = self._encode_payload(
         ETree.tostring(root, encoding=self.DEFAULT_ENCODING))
Example #2
0
    def _serialize_response(
        self,
        parameters: dict,
        response: HttpResponse,
        shape: Optional[Shape],
        shape_members: dict,
        operation_model: OperationModel,
    ) -> None:
        """
        Serializes the given parameters as XML.

        :param parameters: The user input params
        :param response: The final serialized HttpResponse
        :param shape: Describes the expected output shape (can be None in case of an "empty" response)
        :param shape_members: The members of the output struct shape
        :param operation_model: The specification of the operation of which the response is serialized here
        :return: None - the given `serialized` dict is modified
        """
        # TODO implement the handling of location traits (where "location" is "header", "headers")

        payload_member = shape.serialization.get("payload") if shape is not None else None
        if payload_member is not None and shape_members[payload_member].type_name in [
            "blob",
            "string",
        ]:
            # If it's streaming, then the body is just the value of the payload.
            body_payload = parameters.get(payload_member, b"")
            body_payload = self._encode_payload(body_payload)
            response.data = body_payload
        elif payload_member is not None:
            # If there's a payload member, we serialized that member to the body.
            body_params = parameters.get(payload_member)
            if body_params is not None:
                response.data = self._encode_payload(
                    self._serialize_body_params(
                        body_params, shape_members[payload_member], operation_model
                    )
                )
        else:
            # Otherwise, we use the "traditional" way of serializing the whole parameters dict recursively.
            response.data = self._encode_payload(
                self._serialize_body_params(parameters, shape, operation_model)
            )
Example #3
0
 def _serialize_error(
     self,
     error: ServiceException,
     code: str,
     sender_fault: bool,
     serialized: HttpResponse,
     shape: Shape,
     operation_model: OperationModel,
 ) -> None:
     # TODO handle error shapes with members
     # Check if we need to add a namespace
     attr = ({
         "xmlns": operation_model.metadata.get("xmlNamespace")
     } if "xmlNamespace" in operation_model.metadata else {})
     root = ETree.Element("ErrorResponse", attr)
     error_tag = ETree.SubElement(root, "Error")
     self._add_error_tags(code, error, error_tag, sender_fault)
     request_id = ETree.SubElement(root, "RequestId")
     request_id.text = gen_amzn_requestid_long()
     serialized.data = self._encode_payload(
         ETree.tostring(root, encoding=self.DEFAULT_ENCODING))
Example #4
0
 def _serialize_error(
     self,
     error: ServiceException,
     code: str,
     sender_fault: bool,
     serialized: HttpResponse,
     shape: Shape,
     operation_model: OperationModel,
 ) -> None:
     # It wouldn't be a spec if there wouldn't be any exceptions.
     # S3 errors look differently than other service's errors.
     if operation_model.name == "s3":
         attr = ({
             "xmlns": operation_model.metadata.get("xmlNamespace")
         } if "xmlNamespace" in operation_model.metadata else None)
         root = ETree.Element("Error", attr)
         self._add_error_tags(code, error, root, sender_fault)
         request_id = ETree.SubElement(root, "RequestId")
         request_id.text = gen_amzn_requestid_long()
         serialized.data = self._encode_payload(
             ETree.tostring(root, encoding=self.DEFAULT_ENCODING))
     else:
         super()._serialize_error(error, code, sender_fault, serialized,
                                  shape, operation_model)
Example #5
0
    def _serialize_response(
        self,
        parameters: dict,
        response: HttpResponse,
        shape: Optional[Shape],
        shape_members: dict,
        operation_model: OperationModel,
    ) -> None:
        json_version = operation_model.metadata.get("jsonVersion")
        if json_version is not None:
            response.headers["Content-Type"] = "application/x-amz-json-%s" % json_version

        if shape is None:
            response.set_json({})
            return

        # first, serialize everything into a dictionary
        data = {}
        self._serialize(data, parameters, shape)

        if shape.serialization is None:
            # if there are no special serialization instructions then just set the data dict as JSON payload
            response.set_json(data)
            return

        # otherwise, move data attributes into their appropriate locations
        # (some shapes have a serialization dict, but don't actually have attributes with special locations)
        body = {}
        for name in shape_members:
            member_shape = shape_members[name]
            key = member_shape.serialization.get("name", name)
            if key not in data:
                # ignores optional keys
                continue
            value = data[key]

            location = member_shape.serialization.get("location")

            if not location:
                body[key] = value
            elif location == "header":
                response.headers[key] = value
            elif location == "headers":
                response.headers.update(value)
            elif location == "statusCode":
                # statusCode is quite rare, and it looks like it's always just an int shape, so taking a shortcut here
                response.status_code = int(value)
            else:
                raise ValueError("unknown location %s" % location)

        # the shape defines a specific attribute that should be serialized as payload
        payload_key = shape.serialization.get("payload")
        if not payload_key:
            response.set_json(body)
            return
        payload_shape = shape.members[payload_key]

        if payload_key not in body:
            LOG.warning("missing payload attribute %s in body", payload_key)

        value = body.pop(payload_key, None)

        if body:
            # in principle, if a payload attribute is specified in the shape, all other attributes should be in other
            # locations, so this is just a logging guard to warn if that assumption is violated
            LOG.warning("dangling attributes in body: %s", body.keys())

        # a payload can be a string, blob, or structure: https://gist.github.com/thrau/39fd20b437f8719ffc361ad9a908c0c6
        if payload_shape.type_name == "structure":
            response.set_json(value if value is not None else {})
        elif payload_shape.type_name == "blob":
            # payloads blobs are not base64 encoded, but the `_serialize` recursion doesn't know that
            response.data = base64.b64decode(value) if value is not None else b""
        else:
            response.data = value if value is not None else b""