Skip to content

Commit ca2a5d3

Browse files
crisidevkstich
andcommitted
Implement Rpcv2TraitValidator and tests (#1613)
* Implement Rpcv2TraitValidator and tests * Add missing license * Update smithy-protocols-traits/src/main/java/software/amazon/smithy/protocols/traits/Rpcv2TraitValidator.java Co-authored-by: Kevin Stich <[email protected]> * Address comments from PR * Make sure format is always set * Fix typo * Fix comment * Fix comment * Fix trailing whitespace --------- Co-authored-by: Kevin Stich <[email protected]>
1 parent 3eeaab2 commit ca2a5d3

File tree

9 files changed

+196
-5
lines changed

9 files changed

+196
-5
lines changed

smithy-protocols-traits/src/main/java/software/amazon/smithy/protocols/traits/Rpcv2Trait.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ protected Node createNode() {
8585
builder.withMember(EVENT_STREAM_HTTP, Node.fromStrings(getEventStreamHttp()));
8686
}
8787

88-
if (!getFormat().isEmpty()) {
89-
builder.withMember(FORMAT, Node.fromStrings(getFormat()));
90-
}
88+
// Build the format property even if it's empty as the {@code Rpcv2TraitValidator}
89+
// will take care of validating it.
90+
builder.withMember(FORMAT, Node.fromStrings(getFormat()));
9191

9292
return builder.build();
9393
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
5+
* in compliance with the License. A copy of the License is located at
6+
*
7+
* http://aws.amazon.com/apache2.0
8+
*
9+
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package software.amazon.smithy.protocols.traits;
15+
16+
import java.util.ArrayList;
17+
import java.util.List;
18+
import software.amazon.smithy.model.Model;
19+
import software.amazon.smithy.model.shapes.ServiceShape;
20+
import software.amazon.smithy.model.validation.AbstractValidator;
21+
import software.amazon.smithy.model.validation.ValidationEvent;
22+
import software.amazon.smithy.utils.ListUtils;
23+
import software.amazon.smithy.utils.SmithyInternalApi;
24+
25+
/**
26+
* Validates models implementing the {@code Rpcv2Trait} against its constraints by:
27+
*
28+
* - Ensuring that every entry in {@code eventStreamHttp} also appears in the {@code http} property
29+
* of a protocol trait.
30+
* - Ensuring that there is at least one value for the {@code format} property.
31+
* - Ensuring all the {@code format} property values are valid and supported.
32+
*/
33+
@SmithyInternalApi
34+
public final class Rpcv2TraitValidator extends AbstractValidator {
35+
36+
private static final List<String> VALID_FORMATS = ListUtils.of("cbor");
37+
38+
@Override
39+
public List<ValidationEvent> validate(Model model) {
40+
List<ValidationEvent> events = new ArrayList<>();
41+
for (ServiceShape serviceShape : model.getServiceShapesWithTrait(Rpcv2Trait.class)) {
42+
events.addAll(validateService(serviceShape));
43+
}
44+
return events;
45+
}
46+
47+
private List<ValidationEvent> validateService(ServiceShape service) {
48+
List<ValidationEvent> events = new ArrayList<>();
49+
50+
Rpcv2Trait protocolTrait = service.expectTrait(Rpcv2Trait.class);
51+
52+
List<String> invalid = new ArrayList<>(protocolTrait.getEventStreamHttp());
53+
invalid.removeAll(protocolTrait.getHttp());
54+
if (!invalid.isEmpty()) {
55+
events.add(error(service, protocolTrait,
56+
String.format("The following values of the `eventStreamHttp` property do "
57+
+ "not also appear in the `http` property of the %s protocol "
58+
+ "trait: %s", protocolTrait.toShapeId(), invalid)));
59+
}
60+
61+
List<String> formats = new ArrayList<>(protocolTrait.getFormat());
62+
// Validate there is at least one element in the format property.
63+
if (formats.size() == 0) {
64+
events.add(error(service, protocolTrait, String.format(
65+
"The `format` property for the %s protocol must have at least 1 element",
66+
protocolTrait.toShapeId())));
67+
}
68+
// All the user specified wire formats must be valid.
69+
formats.removeAll(VALID_FORMATS);
70+
if (!formats.isEmpty()) {
71+
events.add(error(service, protocolTrait,
72+
String.format(
73+
"The following values of the `format` property for the %s protocol "
74+
+ "are not supported: %s",
75+
protocolTrait.toShapeId(), formats)));;
76+
}
77+
78+
return events;
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
software.amazon.smithy.protocols.traits.Rpcv2TraitValidator

smithy-protocols-traits/src/test/java/software/amazon/smithy/protocols/traits/Rpcv2TraitTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,13 @@
1919
import software.amazon.smithy.model.shapes.ShapeId;
2020
import software.amazon.smithy.model.traits.Trait;
2121
import software.amazon.smithy.model.traits.TraitFactory;
22-
2322
import java.util.Optional;
2423

2524
public class Rpcv2TraitTest {
2625

2726
@Test
2827
public void loadsTraitWithDefaults() {
29-
Node node = Node.objectNode();
28+
Node node = Node.objectNode().withMember("format", Node.fromStrings("cbor"));
3029
TraitFactory provider = TraitFactory.createServiceFactory();
3130
Optional<Trait> trait =
3231
provider.createTrait(Rpcv2Trait.ID, ShapeId.from("ns.foo#foo"), node);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
5+
* in compliance with the License. A copy of the License is located at
6+
*
7+
* http://aws.amazon.com/apache2.0
8+
*
9+
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package software.amazon.smithy.protocols.traits;
15+
16+
import java.util.concurrent.Callable;
17+
import java.util.stream.Stream;
18+
import org.junit.jupiter.params.ParameterizedTest;
19+
import org.junit.jupiter.params.provider.MethodSource;
20+
import software.amazon.smithy.model.validation.testrunner.SmithyTestCase;
21+
import software.amazon.smithy.model.validation.testrunner.SmithyTestSuite;
22+
23+
public class TestRunnerTest {
24+
@ParameterizedTest(name = "{0}")
25+
@MethodSource("source")
26+
public void testRunner(String filename, Callable<SmithyTestCase.Result> callable)
27+
throws Exception {
28+
callable.call();
29+
}
30+
31+
public static Stream<?> source() {
32+
return SmithyTestSuite.defaultParameterizedTestSource(TestRunnerTest.class);
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[ERROR] smithy.example#InvalidService1: The `format` property for the smithy.protocols#rpcv2 protocol must have at least 1 element | Rpcv2Trait
2+
[ERROR] smithy.example#InvalidService2: The following values of the `format` property for the smithy.protocols#rpcv2 protocol are not supported: [invalid-wire-format] | Rpcv2Trait
3+
[ERROR] smithy.example#InvalidService3: The following values of the `format` property for the smithy.protocols#rpcv2 protocol are not supported: [invalid-wire-format] | Rpcv2Trait
4+
[ERROR] smithy.example#InvalidService4: The following values of the `format` property for the smithy.protocols#rpcv2 protocol are not supported: [invalid-wire-format1, invalid-wire-format2] | Rpcv2Trait
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
$version: "2.0"
2+
3+
namespace smithy.example
4+
5+
use smithy.protocols#rpcv2
6+
7+
@rpcv2(format: ["cbor"])
8+
service ValidService1 {
9+
version: "2023-02-10"
10+
}
11+
12+
@rpcv2(format: [])
13+
service InvalidService1 {
14+
version: "2023-02-10"
15+
}
16+
17+
@rpcv2(format: ["invalid-wire-format"])
18+
service InvalidService2 {
19+
version: "2023-02-10"
20+
}
21+
22+
@rpcv2(format: ["cbor", "invalid-wire-format"])
23+
service InvalidService3 {
24+
version: "2023-02-10"
25+
}
26+
27+
@rpcv2(format: ["invalid-wire-format1", "invalid-wire-format2"])
28+
service InvalidService4 {
29+
version: "2023-02-10"
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[ERROR] smithy.example#InvalidService1: The following values of the `eventStreamHttp` property do not also appear in the `http` property of the smithy.protocols#rpcv2 protocol trait: [http/1.1] | Rpcv2Trait
2+
[ERROR] smithy.example#InvalidService2: The following values of the `eventStreamHttp` property do not also appear in the `http` property of the smithy.protocols#rpcv2 protocol trait: [http/1.1] | Rpcv2Trait
3+
[ERROR] smithy.example#InvalidService3: The following values of the `eventStreamHttp` property do not also appear in the `http` property of the smithy.protocols#rpcv2 protocol trait: [http/1.1, h2c] | Rpcv2Trait
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
$version: "2.0"
2+
3+
namespace smithy.example
4+
5+
use smithy.protocols#rpcv2
6+
7+
@rpcv2(http: ["h2", "http/1.1"], eventStreamHttp: ["h2"], format: ["cbor"])
8+
service ValidService1 {
9+
version: "2023-02-10"
10+
}
11+
12+
@rpcv2(http: ["h2"], eventStreamHttp: ["h2"], format: ["cbor"])
13+
service ValidService2 {
14+
version: "2023-02-10"
15+
}
16+
17+
@rpcv2(http: [], eventStreamHttp: [], format: ["cbor"])
18+
service ValidService3 {
19+
version: "2023-02-10"
20+
}
21+
22+
@rpcv2(http: ["http/1.1"], eventStreamHttp: [], format: ["cbor"])
23+
service ValidService4 {
24+
version: "2023-02-10"
25+
}
26+
27+
@rpcv2(eventStreamHttp: ["http/1.1"], format: ["cbor"])
28+
service InvalidService1 {
29+
version: "2023-02-10"
30+
}
31+
32+
@rpcv2(http: ["h2"], eventStreamHttp: ["http/1.1"], format: ["cbor"])
33+
service InvalidService2 {
34+
version: "2023-02-10"
35+
}
36+
37+
@rpcv2(http: ["h2"], eventStreamHttp: ["h2", "http/1.1", "h2c"], format: ["cbor"])
38+
service InvalidService3 {
39+
version: "2023-02-10"
40+
}

0 commit comments

Comments
 (0)