diff --git a/codegen/gradle/libs.versions.toml b/codegen/gradle/libs.versions.toml index ef016432e..0bd58efd1 100644 --- a/codegen/gradle/libs.versions.toml +++ b/codegen/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] junit5 = "6.1.1" -smithy = "1.71.0" +smithy = "1.72.1" mockito = "5.23.0" test-logger-plugin = "4.0.0" spotbugs = "6.0.22" diff --git a/packages/smithy-aws-core/.changes/next-release/smithy-aws-core-bugfix-2fbf8d3495794ec58ff3ff88e0a3caeb.json b/packages/smithy-aws-core/.changes/next-release/smithy-aws-core-bugfix-2fbf8d3495794ec58ff3ff88e0a3caeb.json new file mode 100644 index 000000000..592fe9080 --- /dev/null +++ b/packages/smithy-aws-core/.changes/next-release/smithy-aws-core-bugfix-2fbf8d3495794ec58ff3ff88e0a3caeb.json @@ -0,0 +1,4 @@ +{ + "type": "bugfix", + "description": "Fixed REST JSON modeled error resolution by matching error identifiers by shape name when wire and modeled namespaces differ. ([#742](https://github.com/smithy-lang/smithy-python/pull/742))" +} diff --git a/packages/smithy-aws-core/src/smithy_aws_core/aio/protocols.py b/packages/smithy-aws-core/src/smithy_aws_core/aio/protocols.py index afaac61eb..68d2c2d01 100644 --- a/packages/smithy-aws-core/src/smithy_aws_core/aio/protocols.py +++ b/packages/smithy-aws-core/src/smithy_aws_core/aio/protocols.py @@ -166,6 +166,17 @@ def content_type(self) -> str: def error_identifier(self) -> HTTPErrorIdentifier: return self._error_identifier + def _resolve_error_id( + self, + *, + operation: APIOperation[Any, Any], + error_id: ShapeID, + ) -> ShapeID: + for error_schema in operation.error_schemas: + if error_schema.id.name == error_id.name: + return error_schema.id + return error_id + def create_event_publisher[ OperationInput: SerializeableShape, OperationOutput: DeserializeableShape, diff --git a/packages/smithy-http/.changes/next-release/smithy-http-enhancement-906ae181a50149ab9b8715cf77d02b86.json b/packages/smithy-http/.changes/next-release/smithy-http-enhancement-906ae181a50149ab9b8715cf77d02b86.json new file mode 100644 index 000000000..09e417061 --- /dev/null +++ b/packages/smithy-http/.changes/next-release/smithy-http-enhancement-906ae181a50149ab9b8715cf77d02b86.json @@ -0,0 +1,4 @@ +{ + "type": "enhancement", + "description": "Enabled HTTP binding protocols to resolve modeled response errors when wire identifiers do not exactly match registered shape IDs. ([#742](https://github.com/smithy-lang/smithy-python/pull/742))" +} diff --git a/packages/smithy-http/src/smithy_http/aio/protocols.py b/packages/smithy-http/src/smithy_http/aio/protocols.py index af32cee16..b52637300 100644 --- a/packages/smithy-http/src/smithy_http/aio/protocols.py +++ b/packages/smithy-http/src/smithy_http/aio/protocols.py @@ -23,6 +23,7 @@ from smithy_core.prelude import DOCUMENT from smithy_core.schemas import APIOperation from smithy_core.serializers import SerializeableShape +from smithy_core.shapes import ShapeID from smithy_core.traits import EndpointTrait, HTTPTrait from ..deserializers import HTTPResponseDeserializer @@ -185,15 +186,26 @@ async def _create_error( error_id = self.error_identifier.identify( operation=operation, response=response ) + if error_id is not None and error_id not in error_registry: + error_id = self._resolve_error_id( + operation=operation, + error_id=error_id, + ) if error_id is None and self._matches_content_type(response): if isinstance(response_body, bytearray): response_body = bytes(response_body) deserializer = self.payload_codec.create_deserializer(source=response_body) document = deserializer.read_document(schema=DOCUMENT) + document_error_id = document.discriminator + if document_error_id not in error_registry: + document_error_id = self._resolve_error_id( + operation=operation, + error_id=document_error_id, + ) - if document.discriminator in error_registry: - error_id = document.discriminator + if document_error_id in error_registry: + error_id = document_error_id if isinstance(response_body, SeekableBytesReader): response_body.seek(0) @@ -236,6 +248,15 @@ async def _create_error( is_retry_safe=is_throttle or is_timeout or None, ) + def _resolve_error_id( + self, + *, + operation: APIOperation[Any, Any], + error_id: ShapeID, + ) -> ShapeID: + """Resolve a response discriminator to its modeled error shape ID.""" + return error_id + def _matches_content_type(self, response: HTTPResponse) -> bool: if "content-type" not in response.fields: return False