Skip to content

Commit 0f8cb89

Browse files
committed
enable error rename by protocols
1 parent 0b9d0e3 commit 0f8cb89

File tree

10 files changed

+190
-10
lines changed

10 files changed

+190
-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 the ``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 shapes 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 the ``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 shapes 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: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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.List;
20+
import java.util.Map;
21+
import java.util.Optional;
22+
import java.util.Set;
23+
import java.util.stream.Collectors;
24+
import software.amazon.smithy.aws.traits.protocols.AwsJson1_1Trait;
25+
import software.amazon.smithy.aws.traits.protocols.AwsProtocolTrait;
26+
import software.amazon.smithy.model.Model;
27+
import software.amazon.smithy.model.shapes.ServiceShape;
28+
import software.amazon.smithy.model.shapes.ShapeId;
29+
import software.amazon.smithy.model.traits.ErrorTrait;
30+
import software.amazon.smithy.model.validation.AbstractValidator;
31+
import software.amazon.smithy.model.validation.ValidationEvent;
32+
import software.amazon.smithy.utils.SetUtils;
33+
import software.amazon.smithy.utils.SmithyInternalApi;
34+
35+
@SmithyInternalApi
36+
public final class ErrorRenameValidator extends AbstractValidator {
37+
38+
/**
39+
* By default, service-side implementation of AWS Protocols send the error shape ID on the wire,
40+
* and clients can use it to resolve the error type.
41+
*
42+
* However, some protocols implementation only sends the shape name. E.g. Aws JSON 1.1
43+
* If there are conflicting shape IDs e.g. smithy.service#ServiceError and smithy.other#ServiceError,
44+
* clients are unable to resolve the error type with the shape name alone.
45+
*
46+
* In addition, Server-side implementation of these protocols don't handle or send renamed shape names.
47+
*
48+
* Hence, error shape renaming are not supported for these protocols.
49+
*/
50+
private static final Set<Class<? extends AwsProtocolTrait>> UNSUPPORTED_PROTOCOLS =
51+
SetUtils.of(AwsJson1_1Trait.class);
52+
53+
@Override
54+
public List<ValidationEvent> validate(Model model) {
55+
List<ValidationEvent> events = new ArrayList<>();
56+
for (ServiceShape shape : model.getServiceShapes()) {
57+
validate(model, shape, events);
58+
}
59+
return events;
60+
}
61+
62+
private void validate(Model model, ServiceShape service, List<ValidationEvent> events) {
63+
final Map<ShapeId, String> renames = service.getRename();
64+
65+
if (renames.isEmpty()) {
66+
return;
67+
}
68+
69+
Set<String> unSupportedProtocols = getUnsupportedProtocols(service);
70+
71+
if (unSupportedProtocols.isEmpty()) {
72+
return;
73+
}
74+
75+
renames.keySet().stream()
76+
.map(model::getShape)
77+
.filter(Optional::isPresent)
78+
.map(Optional::get)
79+
.filter(shape -> shape.hasTrait(ErrorTrait.class))
80+
.forEach(shape -> {
81+
ShapeId from = shape.getId();
82+
String to = renames.get(from);
83+
events.add(error(service, String.format(
84+
"Service attempts to rename an error shape from `%s` to \"%s\"; "
85+
+ "Service protocols %s do not support error renaming.",
86+
from, to, unSupportedProtocols)));
87+
});
88+
}
89+
90+
private Set<String> getUnsupportedProtocols(ServiceShape service) {
91+
return service.getAllTraits().values().stream()
92+
.filter(trait -> UNSUPPORTED_PROTOCOLS.contains(trait.getClass()))
93+
.map(trait -> trait.toShapeId().getName())
94+
.collect(Collectors.toSet());
95+
}
96+
}

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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
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
2+
[ERROR] smithy.example#MyService: Service attempts to rename an error shape from `smithy.example#ServerError` to "ServerDown"; Service protocols [awsJson1_1] do not support error renaming. | ErrorRename
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
"smithy.example#ServerError": "ServerDown"
18+
}
19+
}
20+
21+
operation SayHello {
22+
input: SayHelloInput,
23+
output: SayHelloOutput,
24+
errors: [BadGreeting, ServerError]
25+
}
26+
27+
@input
28+
structure SayHelloInput {}
29+
30+
@output
31+
structure SayHelloOutput {}
32+
33+
@error("client")
34+
structure BadGreeting {}
35+
36+
@error("server")
37+
structure ServerError {}

smithy-model/src/main/java/software/amazon/smithy/model/validation/validators/ServiceValidator.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import software.amazon.smithy.model.shapes.Shape;
3636
import software.amazon.smithy.model.shapes.ShapeId;
3737
import software.amazon.smithy.model.shapes.SimpleShape;
38-
import software.amazon.smithy.model.traits.ErrorTrait;
3938
import software.amazon.smithy.model.traits.Trait;
4039
import software.amazon.smithy.model.validation.AbstractValidator;
4140
import software.amazon.smithy.model.validation.Severity;
@@ -156,8 +155,6 @@ private List<ValidationEvent> validateRenames(ServiceShape service, Map<ShapeId,
156155
private Optional<String> getInvalidRenameReason(Shape shape) {
157156
if (shape.isMemberShape() || shape.isResourceShape() || shape.isOperationShape()) {
158157
return Optional.of(shape.getType() + "s cannot be renamed");
159-
} else if (shape.hasTrait(ErrorTrait.class)) {
160-
return Optional.of("errors cannot be renamed");
161158
} else {
162159
return Optional.empty();
163160
}
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
[ERROR] smithy.example#MyService: Service attempts to rename a structure shape from `smithy.example#BadGreeting` to "ThisDoesNotWork"; errors cannot be renamed | Service

0 commit comments

Comments
 (0)