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))
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) )
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))
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)
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""