@@ -1426,6 +1426,8 @@ def _serialize_content_type(
14261426
14271427
14281428class BaseCBORResponseSerializer (ResponseSerializer ):
1429+ SUPPORTED_MIME_TYPES = [APPLICATION_CBOR , APPLICATION_AMZ_CBOR_1_1 ]
1430+
14291431 UNSIGNED_INT_MAJOR_TYPE = 0
14301432 NEGATIVE_INT_MAJOR_TYPE = 1
14311433 BLOB_MAJOR_TYPE = 2
@@ -1661,8 +1663,6 @@ class CBORResponseSerializer(BaseCBORResponseSerializer):
16611663 from the ``JSONResponseSerializer``
16621664 """
16631665
1664- SUPPORTED_MIME_TYPES = [APPLICATION_CBOR , APPLICATION_AMZ_CBOR_1_1 ]
1665-
16661666 TIMESTAMP_FORMAT = "unixtimestamp"
16671667
16681668 def _serialize_error (
@@ -1737,6 +1737,114 @@ def _prepare_additional_traits_in_response(
17371737 return response
17381738
17391739
1740+ class BaseRpcV2Serializer (ResponseSerializer ):
1741+ """
1742+ The BaseRpcV2Serializer performs the basic logic for the RPC V2 response serialization.
1743+ The only variance between the various RPCv2 protocols is the way the body is serialized for regular responses,
1744+ and the way they will encode exceptions.
1745+ """
1746+
1747+ def _serialize_response (
1748+ self ,
1749+ parameters : dict ,
1750+ response : Response ,
1751+ shape : Shape | None ,
1752+ shape_members : dict ,
1753+ operation_model : OperationModel ,
1754+ mime_type : str ,
1755+ request_id : str ,
1756+ ) -> None :
1757+ response .content_type = mime_type
1758+ response .set_response (
1759+ self ._serialize_body_params (parameters , shape , operation_model , mime_type , request_id )
1760+ )
1761+
1762+ def _serialize_body_params (
1763+ self ,
1764+ params : dict ,
1765+ shape : Shape ,
1766+ operation_model : OperationModel ,
1767+ mime_type : str ,
1768+ request_id : str ,
1769+ ) -> bytes | None :
1770+ raise NotImplementedError
1771+
1772+
1773+ class RpcV2CBORSerializer (BaseRpcV2Serializer , BaseCBORResponseSerializer ):
1774+ """
1775+ The RpcV2CBORSerializer implements the CBOR body serialization part for the RPC v2 protocol, and implements the
1776+ specific exception serialization.
1777+ https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html
1778+ """
1779+
1780+ # the Smithy spec defines that only `application/cbor` is supported for RPC v2 CBOR
1781+ SUPPORTED_MIME_TYPES = [APPLICATION_CBOR ]
1782+ # TODO: check the timestamp format for RpcV2CBOR, which might be different than Kinesis CBOR
1783+ TIMESTAMP_FORMAT = "unixtimestamp"
1784+
1785+ def _serialize_body_params (
1786+ self ,
1787+ params : dict ,
1788+ shape : Shape ,
1789+ operation_model : OperationModel ,
1790+ mime_type : str ,
1791+ request_id : str ,
1792+ ) -> bytes | None :
1793+ body = bytearray ()
1794+ self ._serialize_data_item (body , params , shape )
1795+ return bytes (body )
1796+
1797+ def _serialize_error (
1798+ self ,
1799+ error : ServiceException ,
1800+ response : Response ,
1801+ shape : StructureShape ,
1802+ operation_model : OperationModel ,
1803+ mime_type : str ,
1804+ request_id : str ,
1805+ ) -> None :
1806+ body = bytearray ()
1807+ response .content_type = mime_type # can only be 'application/cbor'
1808+ # TODO: the Botocore parser is able to look at the `x-amzn-query-error` header for the RpcV2 CBOR protocol
1809+ # we'll need to investigate which services need it
1810+ # Responses for the rpcv2Cbor protocol SHOULD NOT contain the X-Amzn-ErrorType header.
1811+ # Type information is always serialized in the payload. This is different than `json` protocol
1812+
1813+ if shape :
1814+ # FIXME: we need to manually add the `__type` field to the shape as it is not part of the specs
1815+ # think about a better way, this is very hacky
1816+ # Error responses in the rpcv2Cbor protocol MUST be serialized identically to standard responses with one
1817+ # additional component to distinguish which error is contained: a body field named __type.
1818+ shape_copy = copy .deepcopy (shape )
1819+ shape_copy .members ["__type" ] = StringShape (
1820+ shape_name = "__type" , shape_model = {"type" : "string" }
1821+ )
1822+ remaining_params = {"__type" : error .code }
1823+
1824+ for member_name in shape_copy .members :
1825+ if hasattr (error , member_name ):
1826+ remaining_params [member_name ] = getattr (error , member_name )
1827+ # Default error message fields can sometimes have different casing in the specs
1828+ elif member_name .lower () in ["code" , "message" ] and hasattr (
1829+ error , member_name .lower ()
1830+ ):
1831+ remaining_params [member_name ] = getattr (error , member_name .lower ())
1832+
1833+ self ._serialize_data_item (body , remaining_params , shape_copy , None )
1834+
1835+ response .set_response (bytes (body ))
1836+
1837+ def _prepare_additional_traits_in_response (
1838+ self , response : Response , operation_model : OperationModel , request_id : str
1839+ ):
1840+ response .headers ["x-amzn-requestid" ] = request_id
1841+ response .headers ["Smithy-Protocol" ] = "rpc-v2-cbor"
1842+ response = super ()._prepare_additional_traits_in_response (
1843+ response , operation_model , request_id
1844+ )
1845+ return response
1846+
1847+
17401848class S3ResponseSerializer (RestXMLResponseSerializer ):
17411849 """
17421850 The ``S3ResponseSerializer`` adds some minor logic to handle S3 specific peculiarities with the error response
@@ -2101,7 +2209,10 @@ def create_serializer(
21012209 "rest-json" : RestJSONResponseSerializer ,
21022210 "rest-xml" : RestXMLResponseSerializer ,
21032211 "ec2" : EC2ResponseSerializer ,
2104- "cbor" : CBORResponseSerializer ,
2212+ "smithy-rpc-v2-cbor" : RpcV2CBORSerializer ,
2213+ # TODO: implement multi-protocol support for Kinesis, so that it can uses the `cbor` protocol and remove
2214+ # CBOR handling from JSONResponseSerializer
2215+ # "cbor": CBORResponseSerializer,
21052216 }
21062217 # TODO: do we want to add a check if the user-defined protocol is part of the available ones in the ServiceModel?
21072218 # or should it be checked once
0 commit comments