Skip to content

Commit

Permalink
Implement Rpcv2TraitValidator and tests (#1613)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
crisidev and kstich committed Aug 1, 2023
1 parent 15c3a97 commit cc3eda2
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ protected Node createNode() {
builder.withMember(EVENT_STREAM_HTTP, Node.fromStrings(getEventStreamHttp()));
}

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

return builder.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package software.amazon.smithy.protocols.traits;

import java.util.ArrayList;
import java.util.List;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.validation.AbstractValidator;
import software.amazon.smithy.model.validation.ValidationEvent;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
* Validates models implementing the {@code Rpcv2Trait} against its constraints by:
*
* - Ensuring that every entry in {@code eventStreamHttp} also appears in the {@code http} property
* of a protocol trait.
* - Ensuring that there is at least one value for the {@code format} property.
* - Ensuring all the {@code format} property values are valid and supported.
*/
@SmithyInternalApi
public final class Rpcv2TraitValidator extends AbstractValidator {

private static final List<String> VALID_FORMATS = ListUtils.of("cbor");

@Override
public List<ValidationEvent> validate(Model model) {
List<ValidationEvent> events = new ArrayList<>();
for (ServiceShape serviceShape : model.getServiceShapesWithTrait(Rpcv2Trait.class)) {
events.addAll(validateService(serviceShape));
}
return events;
}

private List<ValidationEvent> validateService(ServiceShape service) {
List<ValidationEvent> events = new ArrayList<>();

Rpcv2Trait protocolTrait = service.expectTrait(Rpcv2Trait.class);

List<String> invalid = new ArrayList<>(protocolTrait.getEventStreamHttp());
invalid.removeAll(protocolTrait.getHttp());
if (!invalid.isEmpty()) {
events.add(error(service, protocolTrait,
String.format("The following values of the `eventStreamHttp` property do "
+ "not also appear in the `http` property of the %s protocol "
+ "trait: %s", protocolTrait.toShapeId(), invalid)));
}

List<String> formats = new ArrayList<>(protocolTrait.getFormat());
// Validate there is at least one element in the format property.
if (formats.size() == 0) {
events.add(error(service, protocolTrait, String.format(
"The `format` property for the %s protocol must have at least 1 element",
protocolTrait.toShapeId())));
}
// All the user specified wire formats must be valid.
formats.removeAll(VALID_FORMATS);
if (!formats.isEmpty()) {
events.add(error(service, protocolTrait,
String.format(
"The following values of the `format` property for the %s protocol "
+ "are not supported: %s",
protocolTrait.toShapeId(), formats)));;
}

return events;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
software.amazon.smithy.protocols.traits.Rpcv2TraitValidator
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.Trait;
import software.amazon.smithy.model.traits.TraitFactory;

import java.util.Optional;

public class Rpcv2TraitTest {

@Test
public void loadsTraitWithDefaults() {
Node node = Node.objectNode();
Node node = Node.objectNode().withMember("format", Node.fromStrings("cbor"));
TraitFactory provider = TraitFactory.createServiceFactory();
Optional<Trait> trait =
provider.createTrait(Rpcv2Trait.ID, ShapeId.from("ns.foo#foo"), node);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package software.amazon.smithy.protocols.traits;

import java.util.concurrent.Callable;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import software.amazon.smithy.model.validation.testrunner.SmithyTestCase;
import software.amazon.smithy.model.validation.testrunner.SmithyTestSuite;

public class TestRunnerTest {
@ParameterizedTest(name = "{0}")
@MethodSource("source")
public void testRunner(String filename, Callable<SmithyTestCase.Result> callable)
throws Exception {
callable.call();
}

public static Stream<?> source() {
return SmithyTestSuite.defaultParameterizedTestSource(TestRunnerTest.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[ERROR] smithy.example#InvalidService1: The `format` property for the smithy.protocols#rpcv2 protocol must have at least 1 element | Rpcv2Trait
[ERROR] smithy.example#InvalidService2: The following values of the `format` property for the smithy.protocols#rpcv2 protocol are not supported: [invalid-wire-format] | Rpcv2Trait
[ERROR] smithy.example#InvalidService3: The following values of the `format` property for the smithy.protocols#rpcv2 protocol are not supported: [invalid-wire-format] | Rpcv2Trait
[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 number Diff line number Diff line change
@@ -0,0 +1,30 @@
$version: "2.0"

namespace smithy.example

use smithy.protocols#rpcv2

@rpcv2(format: ["cbor"])
service ValidService1 {
version: "2023-02-10"
}

@rpcv2(format: [])
service InvalidService1 {
version: "2023-02-10"
}

@rpcv2(format: ["invalid-wire-format"])
service InvalidService2 {
version: "2023-02-10"
}

@rpcv2(format: ["cbor", "invalid-wire-format"])
service InvalidService3 {
version: "2023-02-10"
}

@rpcv2(format: ["invalid-wire-format1", "invalid-wire-format2"])
service InvalidService4 {
version: "2023-02-10"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[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
[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
[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 number Diff line number Diff line change
@@ -0,0 +1,40 @@
$version: "2.0"

namespace smithy.example

use smithy.protocols#rpcv2

@rpcv2(http: ["h2", "http/1.1"], eventStreamHttp: ["h2"], format: ["cbor"])
service ValidService1 {
version: "2023-02-10"
}

@rpcv2(http: ["h2"], eventStreamHttp: ["h2"], format: ["cbor"])
service ValidService2 {
version: "2023-02-10"
}

@rpcv2(http: [], eventStreamHttp: [], format: ["cbor"])
service ValidService3 {
version: "2023-02-10"
}

@rpcv2(http: ["http/1.1"], eventStreamHttp: [], format: ["cbor"])
service ValidService4 {
version: "2023-02-10"
}

@rpcv2(eventStreamHttp: ["http/1.1"], format: ["cbor"])
service InvalidService1 {
version: "2023-02-10"
}

@rpcv2(http: ["h2"], eventStreamHttp: ["http/1.1"], format: ["cbor"])
service InvalidService2 {
version: "2023-02-10"
}

@rpcv2(http: ["h2"], eventStreamHttp: ["h2", "http/1.1", "h2c"], format: ["cbor"])
service InvalidService3 {
version: "2023-02-10"
}

0 comments on commit cc3eda2

Please sign in to comment.