Skip to content

Commit da8d246

Browse files
committed
enable error rename by protocols
1 parent 95613e0 commit da8d246

File tree

12 files changed

+191
-10
lines changed

12 files changed

+191
-10
lines changed

docs/source-1.0/spec/aws/aws-json-1_1-protocol.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,28 @@ The following example defines a service that requires the use of
137137
.. |protocol test link| replace:: https://github.com/awslabs/smithy/tree/main/smithy-aws-protocol-tests/model/awsJson1_1
138138
.. include:: aws-json.rst.template
139139

140+
141+
--------------------
142+
Error shape renaming
143+
--------------------
144+
145+
By default, Smithy permits renaming shapes to disambiguate shape ID conflicts in
146+
the :ref:`service closure <service-closure>` via the ``rename`` property.
147+
148+
However, services using ``awsJson1_1`` protocol are not
149+
allowed to rename error shapes (shapes with :ref:`error trait <error-trait>` applied).
150+
151+
Client-side implementation relies on the response body field ``code`` or ``__type`` to resolve the error type.
152+
Server-side implementation of ``awsJson1_1`` protocol will only send the ``shape name`` for the response body field.
153+
154+
When there are conflicting shape IDs ``smithy.service#ServiceError`` and ``smithy.other#ServiceError``,
155+
server-side will only send shape name ``ServiceError``. Client-side implementation will not be able to resolve
156+
the correct error type without the ``namespace``.
157+
158+
Server-side implementation of ``awsJson1_1`` protocol doesn't handle or send renamed shape names.
159+
As a result, renaming will not resolve the conflicting shape IDs issue, and hence it is not permitted.
160+
161+
The resolution should be to avoid having conflicting error shape IDs.
162+
163+
140164
.. _`Application-Layer Protocol Negotiation (ALPN) Protocol ID`: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids

docs/source-1.0/spec/core/model.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,9 +1141,9 @@ The service shape supports the following properties:
11411141
shape name or the name of a non-renamed shape contained in the
11421142
service.
11431143
* Member shapes MAY NOT be renamed.
1144-
* Resource, operation, and shapes marked with the :ref:`error-trait`
1145-
MAY NOT be renamed. Renaming shapes is intended for incidental naming
1146-
conflicts, not for renaming the fundamental concepts of a service.
1144+
* Resource and operation MAY NOT be renamed. Renaming shapes is intended
1145+
for incidental naming conflicts, not for renaming the fundamental concepts
1146+
of a service.
11471147
* Shapes from other namespaces marked as :ref:`private <private-trait>`
11481148
MAY be renamed.
11491149
* A rename MUST use a name that is case-sensitively different from the

docs/source-2.0/aws/protocols/aws-json-1_1-protocol.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,28 @@ The following example defines a service that requires the use of
128128
.. |protocol test link| replace:: https://github.com/awslabs/smithy/tree/main/smithy-aws-protocol-tests/model/awsJson1_1
129129
.. include:: aws-json.rst.template
130130

131+
132+
--------------------
133+
Error shape renaming
134+
--------------------
135+
136+
By default, Smithy permits renaming shapes to disambiguate shape ID conflicts in
137+
the :ref:`service closure <service-closure>` via the ``rename`` property.
138+
139+
However, services using ``awsJson1_1`` protocol are not
140+
allowed to rename error shapes (shapes with :ref:`error trait <error-trait>` applied).
141+
142+
According to ``Operation error serialization``,
143+
client-side implementation relies on the response body field ``code`` or ``__type`` to resolve the error type.
144+
Server-side implementation of ``awsJson1_1`` protocol will only send the ``shape name`` for the response body field.
145+
146+
When there are conflicting shape IDs ``smithy.service#ServiceError`` and ``smithy.other#ServiceError``,
147+
server-side will only send the shape name ``ServiceError``. Client-side implementation will not be able to resolve
148+
the correct error type without the ``namespace``.
149+
150+
Server-side implementation of ``awsJson1_1`` protocol doesn't handle or send renamed shape names.
151+
As a result, renaming will not resolve the conflicting shape IDs issue, and hence it is not permitted.
152+
153+
The resolution should be to avoid having conflicting error shape IDs.
154+
131155
.. _`Application-Layer Protocol Negotiation (ALPN) Protocol ID`: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids

docs/source-2.0/spec/service-types.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ The service shape supports the following properties:
6262
shape name or the name of a non-renamed shape contained in the
6363
service.
6464
* Member shapes MAY NOT be renamed.
65-
* Resource, operation, and shapes marked with the :ref:`error-trait`
66-
MAY NOT be renamed. Renaming shapes is intended for incidental naming
67-
conflicts, not for renaming the fundamental concepts of a service.
65+
* Resource and operation MAY NOT be renamed. Renaming shapes is intended
66+
for incidental naming conflicts, not for renaming the fundamental concepts
67+
of a service.
6868
* Shapes from other namespaces marked as :ref:`private <private-trait>`
6969
MAY be renamed.
7070
* A rename MUST use a name that is case-sensitively different from the
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.smithy.aws.traits;
17+
18+
import java.util.ArrayList;
19+
import java.util.HashMap;
20+
import java.util.List;
21+
import java.util.Map;
22+
import java.util.Set;
23+
import java.util.stream.Collectors;
24+
import software.amazon.smithy.aws.traits.protocols.AwsProtocolTrait;
25+
import software.amazon.smithy.model.Model;
26+
import software.amazon.smithy.model.knowledge.NeighborProviderIndex;
27+
import software.amazon.smithy.model.neighbor.Walker;
28+
import software.amazon.smithy.model.shapes.ServiceShape;
29+
import software.amazon.smithy.model.shapes.Shape;
30+
import software.amazon.smithy.model.shapes.ShapeId;
31+
import software.amazon.smithy.model.traits.ErrorTrait;
32+
import software.amazon.smithy.model.validation.AbstractValidator;
33+
import software.amazon.smithy.model.validation.ValidationEvent;
34+
35+
public class ErrorRenameValidator extends AbstractValidator {
36+
@Override
37+
public List<ValidationEvent> validate(Model model) {
38+
List<ValidationEvent> events = new ArrayList<>();
39+
for (ServiceShape shape : model.getServiceShapes()) {
40+
validate(model, shape, events);
41+
}
42+
return events;
43+
}
44+
45+
private void validate(Model model, ServiceShape service, List<ValidationEvent> events) {
46+
Walker walker = new Walker(NeighborProviderIndex.of(model).getProvider());
47+
Map<ShapeId, Shape> closure = new HashMap<>();
48+
walker.iterateShapes(service).forEachRemaining(shape -> closure.put(shape.getId(), shape));
49+
events.addAll(validateErrorRenames(service, closure));
50+
}
51+
52+
private List<ValidationEvent> validateErrorRenames(ServiceShape service, Map<ShapeId, Shape> closure) {
53+
List<ValidationEvent> events = new ArrayList<>();
54+
55+
if (service.getRename().isEmpty()) {
56+
return events;
57+
}
58+
59+
// Collect names of protocols which do not send shape ID on the wire
60+
Set<String> protocolsNotSupportingErrorRename = service.getAllTraits().values().stream()
61+
.filter(trait -> trait instanceof AwsProtocolTrait)
62+
.filter(trait -> ((AwsProtocolTrait) trait).sendErrorShapeNameOverWire())
63+
.map(trait -> trait.toShapeId().getName())
64+
.collect(Collectors.toSet());
65+
66+
if (protocolsNotSupportingErrorRename.isEmpty()) {
67+
return events;
68+
}
69+
70+
for (Map.Entry<ShapeId, String> rename : service.getRename().entrySet()) {
71+
ShapeId from = rename.getKey();
72+
String to = rename.getValue();
73+
Shape shapeToRename = closure.get(from);
74+
75+
if (shapeToRename.hasTrait(ErrorTrait.class)) {
76+
events.add(error(service, String.format(
77+
"Service attempts to rename an error shape from `%s` to \"%s\"; "
78+
+ "Service protocols %s do not support error renaming.",
79+
from, to, protocolsNotSupportingErrorRename)));
80+
}
81+
}
82+
83+
return events;
84+
}
85+
}

smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/protocols/AwsJson1_1Trait.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ private AwsJson1_1Trait(Builder builder) {
3232
super(ID, builder);
3333
}
3434

35+
@Override
36+
public boolean sendErrorShapeNameOverWire() {
37+
return true;
38+
}
39+
3540
public static Builder builder() {
3641
return new Builder();
3742
}

smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/protocols/AwsProtocolTrait.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ protected Node createNode() {
8080
return builder.build();
8181
}
8282

83+
/**
84+
* By default, service-side implementation of AWS Protocols send the error shape ID on the wire,
85+
* and clients can use it to resolve the error type.
86+
*
87+
* However, this method can be overridden by legacy protocols implementation which only sends the shape name.
88+
*
89+
* @return a boolean indicating whether protocol sends fully qualified error shape ID on the wire
90+
*/
91+
public boolean sendErrorShapeNameOverWire() {
92+
return false;
93+
}
94+
8395
/**
8496
* Builder for creating a {@code AwsProtocolTrait}.
8597
*

smithy-aws-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ software.amazon.smithy.aws.traits.tagging.TagEnabledServiceValidator
1010
software.amazon.smithy.aws.traits.tagging.TaggableResourceValidator
1111
software.amazon.smithy.aws.traits.tagging.TagResourcePropertyTypeValidator
1212
software.amazon.smithy.aws.traits.tagging.TagResourcePropertyNameValidator
13+
software.amazon.smithy.aws.traits.ErrorRenameValidator
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[ERROR] smithy.example#MyService: Service attempts to rename an error shape from `smithy.example#BadGreeting` to "ThisDoesNotWork"; Service protocols [awsJson1_1] do not support error renaming. | ErrorRename
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
$version: "2.0"
2+
3+
namespace smithy.example
4+
5+
use aws.protocols#awsJson1_1
6+
use aws.protocols#restXml
7+
8+
@awsJson1_1
9+
@restXml
10+
service MyService {
11+
version: "1",
12+
operations: [
13+
SayHello,
14+
],
15+
rename: {
16+
"smithy.example#BadGreeting": "ThisDoesNotWork"
17+
}
18+
}
19+
20+
operation SayHello {
21+
input: SayHelloInput,
22+
output: SayHelloOutput,
23+
errors: [BadGreeting]
24+
}
25+
26+
@input
27+
structure SayHelloInput {}
28+
29+
@output
30+
structure SayHelloOutput {}
31+
32+
@error("client")
33+
structure BadGreeting {}

0 commit comments

Comments
 (0)