diff --git a/config/spotbugs/filter.xml b/config/spotbugs/filter.xml index 89d72fb1a01..095a160b568 100644 --- a/config/spotbugs/filter.xml +++ b/config/spotbugs/filter.xml @@ -136,4 +136,10 @@ + + + + + + diff --git a/settings.gradle b/settings.gradle index 0cb9efda768..8fd75a22602 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,3 +28,4 @@ include ":smithy-waiters" include ":smithy-aws-cloudformation-traits" include ":smithy-aws-cloudformation" include ":smithy-validation-model" +include ":smithy-rules-engine" diff --git a/smithy-rules-engine/build.gradle b/smithy-rules-engine/build.gradle new file mode 100644 index 00000000000..35563660bc0 --- /dev/null +++ b/smithy-rules-engine/build.gradle @@ -0,0 +1,26 @@ +/* + * Copyright 2022 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. + */ + +description = "Smithy rules engine Language and traits" + +ext { + displayName = "Smithy :: Rules Engine" + moduleName = "software.amazon.smithy.rulesengine" +} + +dependencies { + api project(":smithy-model") + api project(":smithy-utils") +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/package-info.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/package-info.java new file mode 100644 index 00000000000..e39243df030 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright 2022 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. + */ + +/** + * Smithy Rules Engine. + */ +@SmithyUnstableApi +package software.amazon.smithy.rulesengine; + +import software.amazon.smithy.utils.SmithyUnstableApi; diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ClientContextParamDefinition.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ClientContextParamDefinition.java new file mode 100644 index 00000000000..594f10524d7 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ClientContextParamDefinition.java @@ -0,0 +1,94 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import java.util.Objects; +import java.util.Optional; +import software.amazon.smithy.model.shapes.ShapeType; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyUnstableApi; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * A service client context parameter definition. + */ +@SmithyUnstableApi +public final class ClientContextParamDefinition implements ToSmithyBuilder { + private final ShapeType type; + private final String documentation; + + private ClientContextParamDefinition(Builder builder) { + this.type = SmithyBuilder.requiredState("type", builder.type); + this.documentation = builder.documentation; + } + + public static Builder builder() { + return new Builder(); + } + + public ShapeType getType() { + return type; + } + + public Optional getDocumentation() { + return Optional.ofNullable(documentation); + } + + public Builder toBuilder() { + return builder() + .type(type) + .documentation(documentation); + } + + @Override + public int hashCode() { + return Objects.hash(getType(), getDocumentation()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ClientContextParamDefinition that = (ClientContextParamDefinition) o; + return getType() == that.getType() && Objects.equals(getDocumentation(), that.getDocumentation()); + } + + public static final class Builder implements SmithyBuilder { + private ShapeType type; + private String documentation; + + private Builder() { + } + + public Builder type(ShapeType type) { + this.type = type; + return this; + } + + public Builder documentation(String documentation) { + this.documentation = documentation; + return this; + } + + public ClientContextParamDefinition build() { + return new ClientContextParamDefinition(this); + } + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ClientContextParamsTrait.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ClientContextParamsTrait.java new file mode 100644 index 00000000000..f61533e70d1 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ClientContextParamsTrait.java @@ -0,0 +1,122 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import java.util.LinkedHashMap; +import java.util.Map; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.NodeMapper; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.AbstractTrait; +import software.amazon.smithy.model.traits.AbstractTraitBuilder; +import software.amazon.smithy.model.traits.Trait; +import software.amazon.smithy.utils.BuilderRef; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyUnstableApi; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * Indicates that the named rule-set parameters that should be configurable + * on the service client using the specified smithy types. + */ +@SmithyUnstableApi +public final class ClientContextParamsTrait extends AbstractTrait implements ToSmithyBuilder { + public static final ShapeId ID = ShapeId.from("smithy.rules#clientContextParams"); + + private final Map parameters; + + public ClientContextParamsTrait(Builder builder) { + super(ID, builder.getSourceLocation()); + this.parameters = builder.parameters.copy(); + } + + public static Builder builder() { + return new Builder(); + } + + public Map getParameters() { + return parameters; + } + + @Override + protected Node createNode() { + NodeMapper mapper = new NodeMapper(); + return mapper.serialize(this.getParameters()).expectObjectNode(); + } + + @Override + public SmithyBuilder toBuilder() { + return builder() + .sourceLocation(getSourceLocation()) + .parameters(getParameters()); + } + + public static final class Provider extends AbstractTrait.Provider { + public Provider() { + super(ID); + } + + @Override + public Trait createTrait(ShapeId target, Node value) { + NodeMapper mapper = new NodeMapper(); + + Map parameters = new LinkedHashMap<>(); + value.expectObjectNode().getMembers().forEach((stringNode, node) -> { + parameters.put(stringNode.getValue(), mapper.deserialize(node, ClientContextParamDefinition.class)); + }); + + ClientContextParamsTrait trait = builder() + .parameters(parameters) + .sourceLocation(value) + .build(); + trait.setNodeCache(value); + return trait; + } + } + + public static final class Builder extends AbstractTraitBuilder { + private final BuilderRef> parameters = BuilderRef.forOrderedMap(); + + private Builder() { + } + + public Builder parameters(Map parameters) { + this.parameters.clear(); + this.parameters.get().putAll(parameters); + return this; + } + + public Builder putParameter(String name, ClientContextParamDefinition definition) { + this.parameters.get().put(name, definition); + return this; + } + + public Builder removeParameter(String name) { + this.parameters.get().remove(name); + return this; + } + + public Builder clearParameters() { + this.parameters.clear(); + return this; + } + + @Override + public ClientContextParamsTrait build() { + return new ClientContextParamsTrait(this); + } + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ContextIndex.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ContextIndex.java new file mode 100644 index 00000000000..30c2e544868 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ContextIndex.java @@ -0,0 +1,90 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import java.lang.ref.WeakReference; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.knowledge.KnowledgeIndex; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.OperationShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.utils.MapUtils; +import software.amazon.smithy.utils.Pair; +import software.amazon.smithy.utils.SmithyUnstableApi; + +/** + * Resolves an indexes the context parameters in the model. + */ +@SmithyUnstableApi +public final class ContextIndex implements KnowledgeIndex { + private final WeakReference model; + + public ContextIndex(Model model) { + this.model = new WeakReference<>(model); + } + + public static ContextIndex of(Model model) { + return model.getKnowledge(ContextIndex.class, ContextIndex::new); + } + + /** + * Gets the mapping of context parameter names and corresponding {@link ClientContextParamDefinition} to be + * generated on the service client's configuration. + * + * @param service The service shape. + * @return The mapping of context parameter names to {@link ClientContextParamDefinition}. + */ + public Optional getClientContextParams(Shape service) { + return service.getTrait(ClientContextParamsTrait.class); + } + + /** + * Gets the static context parameter names and their {@link StaticContextParamDefinition} to be set for the given + * operation. + * + * @param operation The operation shape. + * @return The mapping of context parameter names to the static {@link Node} value to be set. + */ + public Optional getStaticContextParams(Shape operation) { + return operation.getTrait(StaticContextParamsTrait.class); + } + + /** + * Gets the mapping of {@link MemberShape} to {@link ContextParamTrait} for the operation. + * + * @param operation The operation shape. + * @return The mapping of operation's {@link MemberShape} to {@link ContextParamTrait}. + */ + public Map getContextParams(Shape operation) { + OperationShape operationShape = operation.asOperationShape() + .orElseThrow(() -> new IllegalArgumentException(operation.toShapeId() + + " is not an operation shape")); + + return getModel().expectShape(operationShape.getInputShape()) + .members().stream() + .map(memberShape -> Pair.of(memberShape, memberShape.getTrait(ContextParamTrait.class))) + .filter(pair -> pair.right.isPresent()) + .collect(MapUtils.toUnmodifiableMap(pair -> pair.left, pair -> pair.right.get())); + } + + private Model getModel() { + return Objects.requireNonNull(model.get(), "The dereferenced WeakReference is null"); + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ContextParamTrait.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ContextParamTrait.java new file mode 100644 index 00000000000..78d36002375 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ContextParamTrait.java @@ -0,0 +1,97 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.NodeMapper; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.AbstractTrait; +import software.amazon.smithy.model.traits.AbstractTraitBuilder; +import software.amazon.smithy.model.traits.Trait; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyUnstableApi; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * Binds a Structure member shape to a rule-set parameter. + */ +@SmithyUnstableApi +public final class ContextParamTrait extends AbstractTrait implements ToSmithyBuilder { + public static final ShapeId ID = ShapeId.from("smithy.rules#contextParam"); + + private final String name; + + private ContextParamTrait(Builder builder) { + super(ID, builder.getSourceLocation()); + this.name = SmithyBuilder.requiredState("name", builder.name); + } + + public static Builder builder() { + return new Builder(); + } + + public String getName() { + return this.name; + } + + @Override + protected Node createNode() { + NodeMapper mapper = new NodeMapper(); + mapper.disableToNodeForClass(ContextParamTrait.class); + mapper.setOmitEmptyValues(true); + return mapper.serialize(this).expectObjectNode(); + } + + @Override + public SmithyBuilder toBuilder() { + return new Builder() + .sourceLocation(getSourceLocation()) + .name(name); + } + + public static final class Provider extends AbstractTrait.Provider { + public Provider() { + super(ID); + } + + @Override + public Trait createTrait(ShapeId target, Node value) { + NodeMapper mapper = new NodeMapper(); + mapper.disableFromNodeForClass(ContextParamTrait.class); + mapper.setOmitEmptyValues(true); + ContextParamTrait trait = mapper.deserialize(value, ContextParamTrait.class); + trait.setNodeCache(value); + return trait; + } + } + + public static final class Builder extends AbstractTraitBuilder { + private String name; + + private Builder() { + } + + public Builder name(String name) { + this.name = name; + return this; + } + + @Override + public ContextParamTrait build() { + return new ContextParamTrait(this); + } + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointRuleSetTrait.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointRuleSetTrait.java new file mode 100644 index 00000000000..b976fc8e465 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointRuleSetTrait.java @@ -0,0 +1,92 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.AbstractTrait; +import software.amazon.smithy.model.traits.AbstractTraitBuilder; +import software.amazon.smithy.model.traits.Trait; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyUnstableApi; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/*** + * Defines an endpoint rule-set used to resolve the client's transport endpoint. + */ +@SmithyUnstableApi +public final class EndpointRuleSetTrait extends AbstractTrait implements ToSmithyBuilder { + public static final ShapeId ID = ShapeId.from("smithy.rules#endpointRuleSet"); + + private final Node ruleSet; + + private EndpointRuleSetTrait(Builder builder) { + super(ID, builder.getSourceLocation()); + this.ruleSet = SmithyBuilder.requiredState("ruleSet", builder.ruleSet); + } + + public static Builder builder() { + return new Builder(); + } + + public Node getRuleSet() { + return ruleSet; + } + + @Override + protected Node createNode() { + return ruleSet; + } + + @Override + public SmithyBuilder toBuilder() { + return builder() + .sourceLocation(getSourceLocation()) + .ruleSet(ruleSet); + } + + public static final class Provider extends AbstractTrait.Provider { + public Provider() { + super(ID); + } + + @Override + public Trait createTrait(ShapeId target, Node value) { + EndpointRuleSetTrait trait = builder() + .ruleSet(value) + .build(); + trait.setNodeCache(value); + return trait; + } + } + + public static final class Builder extends AbstractTraitBuilder { + private Node ruleSet; + + private Builder() { + } + + public Builder ruleSet(Node ruleSet) { + this.ruleSet = ruleSet; + return this; + } + + @Override + public EndpointRuleSetTrait build() { + return new EndpointRuleSetTrait(this); + } + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestCase.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestCase.java new file mode 100644 index 00000000000..5b22f9bdae1 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestCase.java @@ -0,0 +1,138 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.utils.BuilderRef; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyUnstableApi; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * Describes an endpoint rule-set test case. + */ +@SmithyUnstableApi +public final class EndpointTestCase implements ToSmithyBuilder { + private final String documentation; + private final ObjectNode params; + private final List operationInputs; + private final EndpointTestExpectation expect; + + public EndpointTestCase(Builder builder) { + this.documentation = builder.documentation; + this.params = builder.params; + this.operationInputs = builder.operationInputs.copy(); + this.expect = builder.expect; + } + + public static Builder builder() { + return new Builder(); + } + + public Optional getDocumentation() { + return Optional.ofNullable(documentation); + } + + public ObjectNode getParams() { + return params; + } + + public List getOperationInputs() { + return operationInputs; + } + + public EndpointTestExpectation getExpect() { + return expect; + } + + @Override + public Builder toBuilder() { + return builder() + .documentation(documentation) + .params(params) + .operationInputs(operationInputs) + .expect(expect); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EndpointTestCase that = (EndpointTestCase) o; + return Objects.equals(getDocumentation(), that.getDocumentation()) + && Objects.equals(getParams(), that.getParams()) + && Objects.equals(getOperationInputs(), that.getOperationInputs()) + && Objects.equals(getExpect(), that.getExpect()); + } + + @Override + public int hashCode() { + return Objects.hash(getDocumentation(), getParams(), getOperationInputs(), getExpect()); + } + + public static final class Builder implements SmithyBuilder { + private final BuilderRef> operationInputs = BuilderRef.forList(); + private String documentation; + private ObjectNode params; + private EndpointTestExpectation expect; + + private Builder() { + } + + public Builder documentation(String documentation) { + this.documentation = documentation; + return this; + } + + public Builder params(ObjectNode params) { + this.params = params; + return this; + } + + public Builder operationInputs(List operationInputs) { + this.operationInputs.clear(); + this.operationInputs.get().addAll(operationInputs); + return this; + } + + public Builder addOperationInput(EndpointTestOperationInput operationInput) { + this.operationInputs.get().add(operationInput); + return this; + } + + public Builder removeOperationInput(EndpointTestOperationInput operationInput) { + this.operationInputs.get().remove(operationInput); + return this; + } + + public Builder expect(EndpointTestExpectation expect) { + this.expect = expect; + return this; + } + + @Override + public EndpointTestCase build() { + return new EndpointTestCase(this); + } + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestExpectation.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestExpectation.java new file mode 100644 index 00000000000..3da8b261cee --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestExpectation.java @@ -0,0 +1,95 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import java.util.Objects; +import java.util.Optional; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyUnstableApi; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * An endpoint test case expectation. + */ +@SmithyUnstableApi +public final class EndpointTestExpectation implements ToSmithyBuilder { + private final String error; + private final ExpectedEndpoint endpoint; + + EndpointTestExpectation(Builder builder) { + this.error = builder.error; + this.endpoint = builder.endpoint; + } + + public static Builder builder() { + return new Builder(); + } + + public Optional getError() { + return Optional.ofNullable(error); + } + + public Optional getEndpoint() { + return Optional.ofNullable(endpoint); + } + + @Override + public Builder toBuilder() { + return builder() + .endpoint(endpoint) + .error(error); + } + + @Override + public int hashCode() { + return Objects.hash(getError(), getEndpoint()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EndpointTestExpectation that = (EndpointTestExpectation) o; + return Objects.equals(getError(), that.getError()) && Objects.equals(getEndpoint(), that.getEndpoint()); + } + + public static final class Builder implements SmithyBuilder { + private String error; + private ExpectedEndpoint endpoint; + + private Builder() { + } + + public Builder error(String error) { + this.error = error; + return this; + } + + public Builder endpoint(ExpectedEndpoint endpoint) { + this.endpoint = endpoint; + return this; + } + + @Override + public EndpointTestExpectation build() { + return new EndpointTestExpectation(this); + } + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestOperationInput.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestOperationInput.java new file mode 100644 index 00000000000..04e553e098a --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestOperationInput.java @@ -0,0 +1,117 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import java.util.Objects; +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyUnstableApi; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * A description of a service operation and input used to verify an endpoint rule-set test case. + */ +@SmithyUnstableApi +public final class EndpointTestOperationInput implements ToSmithyBuilder { + private final String operationName; + private final ObjectNode operationParams; + private final ObjectNode builtInParams; + private final ObjectNode clientParams; + + private EndpointTestOperationInput(Builder builder) { + this.operationName = SmithyBuilder.requiredState("operationName", builder.operationName); + this.operationParams = builder.operationParams; + this.builtInParams = builder.builtInParams; + this.clientParams = builder.clientParams; + } + + public static Builder builder() { + return new Builder(); + } + + public ObjectNode getOperationParams() { + return operationParams; + } + + public ObjectNode getBuiltInParams() { + return builtInParams; + } + + public ObjectNode getClientParams() { + return clientParams; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EndpointTestOperationInput that = (EndpointTestOperationInput) o; + return operationName.equals(that.operationName) + && Objects.equals(getOperationParams(), that.getOperationParams()) + && Objects.equals(getBuiltInParams(), that.getBuiltInParams()) + && Objects.equals(getClientParams(), that.getClientParams()); + } + + @Override + public int hashCode() { + return Objects.hash(operationName, getOperationParams(), getBuiltInParams(), getClientParams()); + } + + @Override + public SmithyBuilder toBuilder() { + return builder() + .operationName(operationName) + .operationParams(operationParams) + .builtInParams(builtInParams) + .clientParams(clientParams); + } + + public static final class Builder implements SmithyBuilder { + private String operationName; + private ObjectNode operationParams; + private ObjectNode builtInParams; + private ObjectNode clientParams; + + public Builder operationName(String operationName) { + this.operationName = operationName; + return this; + } + + public Builder operationParams(ObjectNode operationParams) { + this.operationParams = operationParams; + return this; + } + + public Builder builtInParams(ObjectNode builtinParams) { + this.builtInParams = builtinParams; + return this; + } + + public Builder clientParams(ObjectNode clientParams) { + this.clientParams = clientParams; + return this; + } + + @Override + public EndpointTestOperationInput build() { + return new EndpointTestOperationInput(this); + } + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestsTrait.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestsTrait.java new file mode 100644 index 00000000000..158ddcf9b65 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/EndpointTestsTrait.java @@ -0,0 +1,113 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import java.util.List; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.NodeMapper; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.AbstractTrait; +import software.amazon.smithy.model.traits.AbstractTraitBuilder; +import software.amazon.smithy.model.traits.Trait; +import software.amazon.smithy.utils.BuilderRef; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyUnstableApi; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * Describes test cases for validating an endpoint rule-set. + */ +@SmithyUnstableApi +public final class EndpointTestsTrait extends AbstractTrait implements ToSmithyBuilder { + public static final ShapeId ID = ShapeId.from("smithy.rules#endpointTests"); + + private final List testCases; + + private EndpointTestsTrait(Builder builder) { + super(ID, builder.getSourceLocation()); + this.testCases = builder.testCases.copy(); + } + + public static Builder builder() { + return new Builder(); + } + + public static EndpointTestsTrait fromNode(Node node) { + NodeMapper mapper = new NodeMapper(); + mapper.disableFromNodeForClass(EndpointTestsTrait.class); + EndpointTestsTrait trait = mapper.deserialize(node, EndpointTestsTrait.class); + trait.setNodeCache(node); + return trait; + } + + public List getTestCases() { + return testCases; + } + + @Override + protected Node createNode() { + NodeMapper mapper = new NodeMapper(); + mapper.setOmitEmptyValues(true); + mapper.disableToNodeForClass(EndpointTestsTrait.class); + return mapper.serialize(this); + } + + @Override + public SmithyBuilder toBuilder() { + return builder() + .sourceLocation(getSourceLocation()) + .testCases(testCases); + } + + public static final class Provider extends AbstractTrait.Provider { + public Provider() { + super(ID); + } + + @Override + public Trait createTrait(ShapeId target, Node value) { + return EndpointTestsTrait.fromNode(value); + } + } + + public static final class Builder extends AbstractTraitBuilder { + private final BuilderRef> testCases = BuilderRef.forList(); + + private Builder() { + } + + public Builder testCases(List testCases) { + this.testCases.clear(); + this.testCases.get().addAll(testCases); + return this; + } + + public Builder addTestCase(EndpointTestCase testCase) { + this.testCases.get().add(testCase); + return this; + } + + public Builder removeTestCase(EndpointTestCase testCase) { + this.testCases.get().remove(testCase); + return this; + } + + @Override + public EndpointTestsTrait build() { + return new EndpointTestsTrait(this); + } + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ExpectedEndpoint.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ExpectedEndpoint.java new file mode 100644 index 00000000000..ee09b39c807 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/ExpectedEndpoint.java @@ -0,0 +1,134 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.utils.BuilderRef; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyUnstableApi; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * An endpoint test-case expectation. + */ +@SmithyUnstableApi +public final class ExpectedEndpoint implements ToSmithyBuilder { + private final String url; + private final Map> headers; + private final Map properties; + + public ExpectedEndpoint(Builder builder) { + this.url = SmithyBuilder.requiredState("url", builder.url); + this.headers = builder.headers.copy(); + this.properties = builder.properties.copy(); + } + + public static Builder builder() { + return new Builder(); + } + + public String getUrl() { + return url; + } + + public Map> getHeaders() { + return headers; + } + + public Map getProperties() { + return properties; + } + + @Override + public SmithyBuilder toBuilder() { + return builder() + .url(url) + .headers(headers) + .properties(properties); + } + + @Override + public int hashCode() { + return Objects.hash(getUrl(), getHeaders(), getProperties()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ExpectedEndpoint that = (ExpectedEndpoint) o; + return getUrl().equals(that.getUrl()) && Objects.equals(getHeaders(), that.getHeaders()) + && Objects.equals(getProperties(), that.getProperties()); + } + + public static final class Builder implements SmithyBuilder { + private final BuilderRef>> headers = BuilderRef.forOrderedMap(); + private final BuilderRef> properties = BuilderRef.forOrderedMap(); + private String url; + + private Builder() { + } + + public Builder url(String url) { + this.url = url; + return this; + } + + public Builder headers(Map> headers) { + this.headers.clear(); + this.headers.get().putAll(headers); + return this; + } + + public Builder putHeader(String header, List values) { + this.headers.get().put(header, values); + return this; + } + + public Builder removeHeader(String header) { + this.headers.get().remove(header); + return this; + } + + public Builder properties(Map properties) { + this.properties.clear(); + this.properties.get().putAll(properties); + return this; + } + + public Builder putProperty(String property, Node value) { + this.properties.get().put(property, value); + return this; + } + + public Builder removeProperty(String property) { + this.properties.get().remove(property); + return this; + } + + @Override + public ExpectedEndpoint build() { + return new ExpectedEndpoint(this); + } + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/StaticContextParamDefinition.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/StaticContextParamDefinition.java new file mode 100644 index 00000000000..cd38a340ebe --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/StaticContextParamDefinition.java @@ -0,0 +1,80 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import java.util.Objects; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyUnstableApi; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * An operation static context parameter definition. + */ +@SmithyUnstableApi +public final class StaticContextParamDefinition implements ToSmithyBuilder { + private final Node value; + + private StaticContextParamDefinition(Builder builder) { + this.value = SmithyBuilder.requiredState("value", builder.value); + } + + public static Builder builder() { + return new Builder(); + } + + public Node getValue() { + return value; + } + + @Override + public Builder toBuilder() { + return builder().value(value); + } + + @Override + public int hashCode() { + return Objects.hash(getValue()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StaticContextParamDefinition that = (StaticContextParamDefinition) o; + return getValue().equals(that.getValue()); + } + + public static final class Builder implements SmithyBuilder { + private Node value; + + private Builder() { + } + + public Builder value(Node value) { + this.value = value; + return this; + } + + public StaticContextParamDefinition build() { + return new StaticContextParamDefinition(this); + } + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/StaticContextParamsTrait.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/StaticContextParamsTrait.java new file mode 100644 index 00000000000..07a6ecb9ca7 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/StaticContextParamsTrait.java @@ -0,0 +1,121 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import java.util.LinkedHashMap; +import java.util.Map; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.NodeMapper; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.AbstractTrait; +import software.amazon.smithy.model.traits.AbstractTraitBuilder; +import software.amazon.smithy.model.traits.Trait; +import software.amazon.smithy.utils.BuilderRef; +import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.utils.SmithyUnstableApi; +import software.amazon.smithy.utils.ToSmithyBuilder; + +/** + * Binds static values to rule-set parameters for the targeted operation. + */ +@SmithyUnstableApi +public final class StaticContextParamsTrait extends AbstractTrait implements ToSmithyBuilder { + public static final ShapeId ID = ShapeId.from("smithy.rules#staticContextParams"); + + private final Map parameters; + + private StaticContextParamsTrait(Builder builder) { + super(ID, builder.getSourceLocation()); + this.parameters = builder.parameters.copy(); + } + + public static Builder builder() { + return new Builder(); + } + + public Map getParameters() { + return parameters; + } + + @Override + protected Node createNode() { + NodeMapper mapper = new NodeMapper(); + mapper.setOmitEmptyValues(true); + return mapper.serialize(getParameters()).expectObjectNode(); + } + + @Override + public SmithyBuilder toBuilder() { + return new Builder() + .sourceLocation(getSourceLocation()) + .parameters(parameters); + } + + public static final class Provider extends AbstractTrait.Provider { + public Provider() { + super(ID); + } + + @Override + public Trait createTrait(ShapeId target, Node value) { + NodeMapper mapper = new NodeMapper(); + Map parameters = new LinkedHashMap<>(); + value.expectObjectNode().getMembers().forEach((stringNode, node) -> { + parameters.put(stringNode.getValue(), mapper.deserialize(node, StaticContextParamDefinition.class)); + }); + StaticContextParamsTrait trait = builder() + .sourceLocation(value) + .parameters(parameters) + .build(); + trait.setNodeCache(value); + return trait; + } + } + + public static final class Builder extends AbstractTraitBuilder { + private final BuilderRef> parameters = BuilderRef.forOrderedMap(); + + private Builder() { + } + + public Builder parameters(Map parameters) { + this.parameters.clear(); + this.parameters.get().putAll(parameters); + return this; + } + + public Builder putParameter(String name, StaticContextParamDefinition definition) { + this.parameters.get().put(name, definition); + return this; + } + + public Builder removeParameter(String name) { + this.parameters.get().remove(name); + return this; + } + + public Builder clearParameters() { + this.parameters.clear(); + return this; + } + + @Override + public StaticContextParamsTrait build() { + return new StaticContextParamsTrait(this); + } + } + +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/StaticContextParamsTraitValidator.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/StaticContextParamsTraitValidator.java new file mode 100644 index 00000000000..f9ff49745f9 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/StaticContextParamsTraitValidator.java @@ -0,0 +1,58 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.shapes.OperationShape; +import software.amazon.smithy.model.validation.AbstractValidator; +import software.amazon.smithy.model.validation.ValidationEvent; +import software.amazon.smithy.utils.SmithyUnstableApi; + +/** + * Validates {@link StaticContextParamsTrait} traits. + */ +@SmithyUnstableApi +public final class StaticContextParamsTraitValidator extends AbstractValidator { + @Override + public List validate(Model model) { + ContextIndex index = ContextIndex.of(model); + List events = new ArrayList<>(); + for (OperationShape operationShape : model.getOperationShapes()) { + Map definitionMap = index.getStaticContextParams(operationShape) + .map(StaticContextParamsTrait::getParameters) + .orElse(Collections.emptyMap()); + for (Map.Entry entry : definitionMap.entrySet()) { + Node node = entry.getValue().getValue(); + if (node.isStringNode() || node.isBooleanNode()) { + continue; + } + events.add(error(operationShape, + String.format("The operation `%s` is marked with `%s` which contains a " + + "key `%s` with an unsupported document type value `%s`.", + operationShape.getId(), + StaticContextParamsTrait.ID.toString(), + entry.getKey(), + entry.getValue().getValue().getType().toString()))); + } + } + return events; + } +} diff --git a/smithy-rules-engine/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService b/smithy-rules-engine/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService new file mode 100644 index 00000000000..ab79eaaa5e5 --- /dev/null +++ b/smithy-rules-engine/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService @@ -0,0 +1,5 @@ +software.amazon.smithy.rulesengine.traits.ClientContextParamsTrait$Provider +software.amazon.smithy.rulesengine.traits.ContextParamTrait$Provider +software.amazon.smithy.rulesengine.traits.StaticContextParamsTrait$Provider +software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait$Provider +software.amazon.smithy.rulesengine.traits.EndpointTestsTrait$Provider diff --git a/smithy-rules-engine/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator b/smithy-rules-engine/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator new file mode 100644 index 00000000000..710e09784f3 --- /dev/null +++ b/smithy-rules-engine/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator @@ -0,0 +1 @@ +software.amazon.smithy.rulesengine.traits.StaticContextParamsTraitValidator diff --git a/smithy-rules-engine/src/main/resources/META-INF/smithy/manifest b/smithy-rules-engine/src/main/resources/META-INF/smithy/manifest new file mode 100644 index 00000000000..053d0dd8ab2 --- /dev/null +++ b/smithy-rules-engine/src/main/resources/META-INF/smithy/manifest @@ -0,0 +1 @@ +smithy.rules.smithy diff --git a/smithy-rules-engine/src/main/resources/META-INF/smithy/smithy.rules.smithy b/smithy-rules-engine/src/main/resources/META-INF/smithy/smithy.rules.smithy new file mode 100644 index 00000000000..8dab0333d4d --- /dev/null +++ b/smithy-rules-engine/src/main/resources/META-INF/smithy/smithy.rules.smithy @@ -0,0 +1,188 @@ +$version: "2.0" + +namespace smithy.rules + +/// Defines an endpoint rule-set used to resolve the client's transport endpoint. +@unstable +@trait(selector: "service") +document endpointRuleSet + +/// Defines endpoint test-cases for validating a client's endpoint rule-set. +@unstable +@trait(selector: "service") +structure endpointTests { + /// List of endpoint test cases. + testCases: EndpointTestList +} + +/// Binds the targeted member of an operation's input structure to the named rule-set parameter. +/// The type of the shape targeted by the trait MUST match the parameter type defined in the rule-set. +@unstable +@trait(selector: "operation -[input]-> structure > member") +structure contextParam { + /// The rule-set parameter name. + @required + name: String +} + +/// Binds one or more named rule-set parameters to the defined static value for the targeted operation. +/// The type of the targeted shape targeted by the trait MUST match the parameter type defined in the rule-set. +@unstable +@trait(selector: "operation") +map staticContextParams { + /// The rule-set parameter name. + key: String, + + /// The static parameter definition. + value: StaticContextParamDefinition +} + +/// Defines one or more named rule-set parameters to be generated as configurable client parameters. +/// The type specified for the client parameter MUST match the parameter type defined in the rule-set. +@unstable +@trait(selector: "service") +map clientContextParams { + /// The rule-set parameter name. + key: String, + + /// The client parameter definition. + value: ClientContextParamDefinition +} + +/// A static context parameter definition. +@unstable +@private +structure StaticContextParamDefinition { + /// The value to set the associated rule-set parameter to. + @required + value: Document +} + +/// A client context parameter definition. +@unstable +@private +structure ClientContextParamDefinition { + /// The Smithy shape type that should be used to generate a client configurable for the rule-set parameter. + @required + type: ShapeType, + + /// Documentation string to be generated with the client parameter. + documentation: String +} + +/// An enum representing supported Smithy shape types. +@unstable +@private +enum ShapeType { + /// Indicates a Smithy string shape type. + STRING = "string" + + /// Indicates a Smithy boolean shape type. + BOOLEAN = "boolean" +} + +/// A list of endpoint rule-set tests. +@unstable +@private +list EndpointTestList { + member: EndpointTest +} + +/// Describes an endpoint test case for validation of an endpoint rule-set. +@unstable +@private +structure EndpointTest { + /// Documentation describing the test case. + documentation: String, + + /// Defines rule-set parameters and values to use for testing rules-engine. + params: Document, + + /// Defines a set of service operation configurations used for testing the rules-engine. + operationInputs: OperationInputs, + + /// The expected outcome of the test case. + expect: EndpointTestExpectation +} + +/// A list of operation input descriptions for an endpoint rule-set test case. +@unstable +@private +list OperationInputs { + /// The service operation configuration to be used for testing the rules-engine. + member: OperationInput +} + +/// A description of a service operation and input used to verify an endpoint rule-set test case. +@unstable +@private +structure OperationInput { + /// The name of the service operation targeted by the test. + @required + operationName: String, + + /// Defines the input parameters used to generate the operation request. + /// These parameters MUST be compatible with the input of the operation. + operationParams: Document, + + /// Defines the set of rule-set built-ins and their corresponding values to be set. + builtInParams: Document, + + /// Defines the set of client configuration parameters to be set. + clientParams: Document +} + +/// An endpoint rule-set test expectation describing an expected endpoint or error. +@unstable +@private +union EndpointTestExpectation { + /// A test case expectation resulting in an error. + error: String, + + /// A test case expectation resulting in an endpoint. + endpoint: EndpointExpectation +} + +/// A description of an expected endpoint to be resolved for an endpoint rule-set test case. +@unstable +@private +structure EndpointExpectation { + /// The expected endpoint URL to be resolved for this test case. + url: String, + + /// The transport headers to be set for this test case. + headers: EndpointHeaders, + + /// The properties for the endpoint for this test case. + properties: Properties +} + +/// A map of header names to list of values. +@unstable +@private +map EndpointHeaders { + /// The transport header name. + key: String, + + /// The transport header values. + value: EndpointHeaderValue +} + +/// A list of transport header values. +@unstable +@private +list EndpointHeaderValue { + /// A transport header value. + member: String +} + +/// A map of strings to document values. +@unstable +@private +map Properties { + /// The property name. + key: String, + + /// The property value. + value: Document +} diff --git a/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/ClientContextParamsTraitTest.java b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/ClientContextParamsTraitTest.java new file mode 100644 index 00000000000..460eaf3f94e --- /dev/null +++ b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/ClientContextParamsTraitTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.shapes.ShapeType; +import software.amazon.smithy.utils.MapUtils; + +public final class ClientContextParamsTraitTest { + + @Test + public void loadsFromModel() { + Model result = Model.assembler() + .discoverModels(getClass().getClassLoader()) + .addImport(getClass().getResource("traits-test-model.smithy")) + .assemble() + .unwrap(); + ServiceShape service = result + .expectShape(ShapeId.from("smithy.example#ExampleService")) + .asServiceShape().get(); + + ClientContextParamsTrait trait = service.getTrait(ClientContextParamsTrait.class).get(); + + assertEquals(trait.getParameters(), MapUtils.of( + "stringFoo", ClientContextParamDefinition.builder() + .type(ShapeType.STRING) + .documentation("a client string parameter") + .build(), + "boolFoo", ClientContextParamDefinition.builder() + .type(ShapeType.BOOLEAN) + .documentation("a client boolean parameter") + .build() + )); + } +} diff --git a/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/ContextIndexTest.java b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/ContextIndexTest.java new file mode 100644 index 00000000000..9f0afa7b365 --- /dev/null +++ b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/ContextIndexTest.java @@ -0,0 +1,94 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Map; +import java.util.Optional; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.node.BooleanNode; +import software.amazon.smithy.model.node.StringNode; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.shapes.ShapeType; + +public class ContextIndexTest { + private static Model model; + + @BeforeAll + public static void before() { + model = Model.assembler() + .discoverModels(ContextIndexTest.class.getClassLoader()) + .addImport(ContextIndexTest.class.getResource("traits-test-model.smithy")) + .assemble() + .unwrap(); + } + @Test + public void indexesClientContextParams() { + ContextIndex index = ContextIndex.of(model); + + Map clientContexts = index.getClientContextParams( + model.expectShape(ShapeId.from("smithy.example#ExampleService"))).get().getParameters(); + + assertEquals(ClientContextParamDefinition.builder() + .type(ShapeType.STRING) + .documentation("a client string parameter") + .build(), clientContexts.get("stringFoo")); + assertEquals(ClientContextParamDefinition.builder() + .type(ShapeType.BOOLEAN) + .documentation("a client boolean parameter") + .build(), clientContexts.get("boolFoo")); + } + + @Test + public void indexesStaticContextParams() { + ContextIndex index = ContextIndex.of(model); + + Map staticContexts = index.getStaticContextParams( + model.expectShape(ShapeId.from("smithy.example#GetThing"))).get().getParameters(); + + assertEquals(StaticContextParamDefinition.builder() + .value(StringNode.from("some value")) + .build(), + staticContexts.get("stringBar")); + + assertEquals(StaticContextParamDefinition.builder() + .value(BooleanNode.from(true)) + .build(), + staticContexts.get("boolBar")); + } + + @Test + public void indexesContextParam() { + ContextIndex index = ContextIndex.of(model); + + Map contexts = index.getContextParams( + model.expectShape(ShapeId.from("smithy.example#GetThing"))); + + assertEquals(contexts.get(model.expectShape(ShapeId.from("smithy.example#GetThingInput$buzz"), MemberShape.class)), + ContextParamTrait.builder() + .name("stringBaz") + .build()); + + assertEquals(contexts.get(model.expectShape(ShapeId.from("smithy.example#GetThingInput$fuzz"), MemberShape.class)), + ContextParamTrait.builder() + .name("boolBaz") + .build()); + } +} diff --git a/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/ContextParamTraitTest.java b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/ContextParamTraitTest.java new file mode 100644 index 00000000000..55c95cb4fbb --- /dev/null +++ b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/ContextParamTraitTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.shapes.StructureShape; + +public final class ContextParamTraitTest { + + @Test + public void loadsFromModel() { + Model result = Model.assembler() + .discoverModels(getClass().getClassLoader()) + .addImport(getClass().getResource("traits-test-model.smithy")) + .assemble() + .unwrap(); + + StructureShape structureShape = result.expectShape(ShapeId.from("smithy.example#GetThingInput"), + StructureShape.class); + + MemberShape buzz = structureShape.getMember("buzz").get(); + ContextParamTrait trait = buzz.getTrait(ContextParamTrait.class).get(); + assertEquals(trait.getName(), "stringBaz"); + + MemberShape fuzz = structureShape.getMember("fuzz").get(); + trait = fuzz.getTrait(ContextParamTrait.class).get(); + assertEquals(trait.getName(), "boolBaz"); + } +} diff --git a/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/EndpointRuleSetTraitTest.java b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/EndpointRuleSetTraitTest.java new file mode 100644 index 00000000000..2408ae1ab73 --- /dev/null +++ b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/EndpointRuleSetTraitTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.TraitFactory; + +public final class EndpointRuleSetTraitTest { + @Test + public void indexesTheModel() { + Model result = Model.assembler() + .discoverModels(getClass().getClassLoader()) + .addImport(getClass().getResource("traits-test-model.smithy")) + .assemble() + .unwrap(); + + ServiceShape serviceShape = result.expectShape(ShapeId.from("smithy.example#ExampleService"), ServiceShape.class); + + EndpointRuleSetTrait ruleSetTrait = serviceShape.getTrait(EndpointRuleSetTrait.class).get(); + + ObjectNode document = ruleSetTrait.getRuleSet().expectObjectNode(); + + assertEquals(document.expectStringMember("version").getValue(), "1.0"); + } + + @Test + public void roundTrips() { + Node expectedNode = Node.parse( + "{\"version\":\"1.0\",\"parameters\":{\"stringParam\":{\"type\":\"string\"}" + + ",\"booleanParam\":{\"type\":\"boolean\"}},\"rules\":[]}"); + + TraitFactory traitFactory = TraitFactory.createServiceFactory(); + EndpointRuleSetTrait expectedTrait = (EndpointRuleSetTrait) traitFactory.createTrait(EndpointRuleSetTrait.ID, + ShapeId.from("ns.example#Foo"), expectedNode).get(); + + EndpointRuleSetTrait actualTrait = expectedTrait.toBuilder().build(); + assertThat(expectedTrait, equalTo(actualTrait)); + + assertThat(expectedNode, equalTo(expectedTrait.toNode())); + } +} diff --git a/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/EndpointTestsTest.java b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/EndpointTestsTest.java new file mode 100644 index 00000000000..a6fc90f918b --- /dev/null +++ b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/EndpointTestsTest.java @@ -0,0 +1,116 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.List; +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.node.ArrayNode; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.TraitFactory; +import software.amazon.smithy.utils.ListUtils; +import software.amazon.smithy.utils.MapUtils; + +public final class EndpointTestsTest { + @Test + public void loadsFromTheModel() { + Model model = Model.assembler() + .discoverModels(EndpointTestsTest.class.getClassLoader()) + .addImport(EndpointTestsTest.class.getResource("traits-test-model.smithy")) + .assemble() + .unwrap(); + + ServiceShape serviceShape = model.expectShape(ShapeId.from("smithy.example#ExampleService"), + ServiceShape.class); + + EndpointTestsTrait ruleSetTrait = serviceShape.getTrait(EndpointTestsTrait.class).get(); + + List testCases = ruleSetTrait.getTestCases(); + + assertThat(2, equalTo(testCases.size())); + assertThat(EndpointTestCase.builder() + .documentation("a documentation string") + .params(ObjectNode.builder() + .withMember("stringFoo", "a b") + .withMember("boolFoo", false) + .build()) + .expect(EndpointTestExpectation.builder() + .error("endpoint error") + .build()) + .build(), equalTo(testCases.get(0))); + assertThat(EndpointTestCase.builder() + .params(ObjectNode.builder() + .withMember("stringFoo", "c d") + .withMember("boolFoo", true) + .build()) + .operationInputs(ListUtils.of( + EndpointTestOperationInput.builder() + .operationName("GetThing") + .clientParams(ObjectNode.builder() + .withMember("stringFoo", "client value") + .build()) + .operationParams(ObjectNode.builder() + .withMember("buzz", "a buzz value") + .build()) + .builtInParams(ObjectNode.builder() + .withMember("SDK::Endpoint", "https://custom.example.com") + .build()) + .build() + )) + .expect(EndpointTestExpectation.builder() + .endpoint(ExpectedEndpoint.builder() + .url("https://example.com") + .properties(MapUtils.of( + "authSchemes", ArrayNode.arrayNode(ObjectNode.builder() + .withMember("name", "v4") + .withMember("signingName", "example") + .withMember("signingScope", "us-west-2") + .build()) + )) + .headers(MapUtils.of( + "single", ListUtils.of("foo"), + "multi", ListUtils.of("foo", "bar", "baz") + )) + .build()) + .build()) + .build(), equalTo(testCases.get(1))); + } + + @Test + public void roundTrips() { + Node expectedNode = Node.parse( + "{\"testCases\":[{\"documentation\":\"foo bar\",\"params\":" + + "{\"foo\":\"bar\",\"bar\":\"foo\"},\"expect\":{\"endpoint\":{\"url\":\"example.com\",\"headers\"" + + ":{\"single\":[\"one\"],\"multi\":[\"one\",\"two\"]},\"properties\":{\"foo\":{\"bar\":\"thing\"," + + "\"baz\":false}}}}},{\"documentation\":\"bar foo\",\"params\":{\"foo\":\"foo\"},\"expect\":" + + "{\"error\":\"error string\"}}]}"); + + TraitFactory traitFactory = TraitFactory.createServiceFactory(); + EndpointTestsTrait expectedTrait = (EndpointTestsTrait) traitFactory.createTrait(EndpointTestsTrait.ID, + ShapeId.from("ns.example#Foo"), expectedNode).get(); + + EndpointTestsTrait actualTrait = expectedTrait.toBuilder().build(); + assertThat(expectedTrait, equalTo(actualTrait)); + + assertThat(expectedNode, equalTo(expectedTrait.toNode())); + } +} diff --git a/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/StaticContextParamsTraitTest.java b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/StaticContextParamsTraitTest.java new file mode 100644 index 00000000000..eda2552577d --- /dev/null +++ b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/StaticContextParamsTraitTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2022 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.rulesengine.traits; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.StringNode; +import software.amazon.smithy.model.shapes.OperationShape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.utils.MapUtils; + +public class StaticContextParamsTraitTest { + + @Test + public void loadsFromModel() { + Model result = Model.assembler() + .discoverModels(getClass().getClassLoader()) + .addImport(getClass().getResource("traits-test-model.smithy")) + .assemble() + .unwrap(); + + OperationShape operationShape = result.expectShape(ShapeId.from("smithy.example#GetThing"), + OperationShape.class); + + StaticContextParamsTrait trait = operationShape.getTrait(StaticContextParamsTrait.class).get(); + assertEquals(trait.getParameters(), MapUtils.of( + "stringBar", StaticContextParamDefinition.builder() + .value(StringNode.from("some value")) + .build(), + "boolBar", StaticContextParamDefinition.builder() + .value(Node.from(true)) + .build() + )); + } +} diff --git a/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/TestRunnerTest.java b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/TestRunnerTest.java new file mode 100644 index 00000000000..325e7a49553 --- /dev/null +++ b/smithy-rules-engine/src/test/java/software/amazon/smithy/rulesengine/traits/TestRunnerTest.java @@ -0,0 +1,20 @@ +package software.amazon.smithy.rulesengine.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 callable) throws Exception { + callable.call(); + } + + public static Stream source() { + return SmithyTestSuite.defaultParameterizedTestSource(TestRunnerTest.class); + } +} diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-client-param-type.errors b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-client-param-type.errors new file mode 100644 index 00000000000..2dfb775d61f --- /dev/null +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-client-param-type.errors @@ -0,0 +1,2 @@ +[WARNING] smithy.example#ExampleService: This shape applies a trait that is unstable: smithy.rules#clientContextParams | UnstableTrait +[ERROR] smithy.example#ExampleService: Error validating trait `smithy.rules#clientContextParams`.invalidParam.type: String value provided for `smithy.rules#ShapeType` must be one of the following values: `boolean`, `string` | TraitValue diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-client-param-type.smithy b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-client-param-type.smithy new file mode 100644 index 00000000000..51434d34e78 --- /dev/null +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-client-param-type.smithy @@ -0,0 +1,16 @@ +$version: "1.0" + +namespace smithy.example + +use smithy.rules#clientContextParams + +@clientContextParams( + invalidParam: {type: "integer"}, +) +service ExampleService { + version: "2022-01-01", + operations: [GetThing] +} + +@readonly +operation GetThing {} diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-array-param-value.errors b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-array-param-value.errors new file mode 100644 index 00000000000..f95dbd47e19 --- /dev/null +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-array-param-value.errors @@ -0,0 +1,2 @@ +[WARNING] smithy.example#OperationArray: This shape applies a trait that is unstable: smithy.rules#staticContextParams | UnstableTrait +[ERROR] smithy.example#OperationArray: The operation `smithy.example#OperationArray` is marked with `smithy.rules#staticContextParams` which contains a key `arrayParam` with an unsupported document type value `array`. | StaticContextParamsTrait diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-array-param-value.smithy b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-array-param-value.smithy new file mode 100644 index 00000000000..88a5d894427 --- /dev/null +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-array-param-value.smithy @@ -0,0 +1,8 @@ +$version: "1.0" + +namespace smithy.example + +use smithy.rules#staticContextParams + +@staticContextParams(arrayParam: {value: ["foo", "bar"]}) +operation OperationArray {} diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-null-param-value.errors b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-null-param-value.errors new file mode 100644 index 00000000000..02a83acbc71 --- /dev/null +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-null-param-value.errors @@ -0,0 +1,2 @@ +[WARNING] smithy.example#OperationNull: This shape applies a trait that is unstable: smithy.rules#staticContextParams | UnstableTrait +[ERROR] smithy.example#OperationNull: The operation `smithy.example#OperationNull` is marked with `smithy.rules#staticContextParams` which contains a key `nullParam` with an unsupported document type value `null`. | StaticContextParamsTrait diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-null-param-value.smithy b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-null-param-value.smithy new file mode 100644 index 00000000000..4d0c8584ae6 --- /dev/null +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-null-param-value.smithy @@ -0,0 +1,8 @@ +$version: "1.0" + +namespace smithy.example + +use smithy.rules#staticContextParams + +@staticContextParams(nullParam: {value: null}) +operation OperationNull {} diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-number-param-value.errors b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-number-param-value.errors new file mode 100644 index 00000000000..2d0b952c8e6 --- /dev/null +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-number-param-value.errors @@ -0,0 +1,2 @@ +[WARNING] smithy.example#OperationNumber: This shape applies a trait that is unstable: smithy.rules#staticContextParams | UnstableTrait +[ERROR] smithy.example#OperationNumber: The operation `smithy.example#OperationNumber` is marked with `smithy.rules#staticContextParams` which contains a key `numberParam` with an unsupported document type value `number`. | StaticContextParamsTrait diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-number-param-value.smithy b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-number-param-value.smithy new file mode 100644 index 00000000000..bc06373133b --- /dev/null +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-number-param-value.smithy @@ -0,0 +1,8 @@ +$version: "1.0" + +namespace smithy.example + +use smithy.rules#staticContextParams + +@staticContextParams(numberParam: {value: 42}) +operation OperationNumber {} diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-object-param-value.errors b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-object-param-value.errors new file mode 100644 index 00000000000..53ad39df6b6 --- /dev/null +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-object-param-value.errors @@ -0,0 +1,2 @@ +[WARNING] smithy.example#OperationObject: This shape applies a trait that is unstable: smithy.rules#staticContextParams | UnstableTrait +[ERROR] smithy.example#OperationObject: The operation `smithy.example#OperationObject` is marked with `smithy.rules#staticContextParams` which contains a key `objectParam` with an unsupported document type value `object`. | StaticContextParamsTrait diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-object-param-value.smithy b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-object-param-value.smithy new file mode 100644 index 00000000000..5a60ef87be6 --- /dev/null +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/errorfiles/invalid-static-object-param-value.smithy @@ -0,0 +1,8 @@ +$version: "1.0" + +namespace smithy.example + +use smithy.rules#staticContextParams + +@staticContextParams(objectParam: {value: {key: "value"}}) +operation OperationObject {} diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/traits-test-model.smithy b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/traits-test-model.smithy new file mode 100644 index 00000000000..aabddea4505 --- /dev/null +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/traits/traits-test-model.smithy @@ -0,0 +1,103 @@ +$version: "1.0" + +namespace smithy.example + +use smithy.rules#clientContextParams +use smithy.rules#staticContextParams +use smithy.rules#contextParam +use smithy.rules#endpointRuleSet +use smithy.rules#endpointTests + +@clientContextParams( + stringFoo: {type: "string", documentation: "a client string parameter"}, + boolFoo: {type: "boolean", documentation: "a client boolean parameter"} +) +service ExampleService { + version: "2022-01-01", + operations: [GetThing] +} + +apply ExampleService @endpointRuleSet({ + version: "1.0", + parameters: { + stringFoo: {type: "string"}, + stringBar: {type: "string"}, + stringBaz: {type: "string"}, + endpoint: {type: "string", builtIn: "SDK::Endpoint"}, + boolFoo: {type: "boolean"}, + boolBar: {type: "boolean"}, + boolBaz: {type: "string"} + }, + rules: [] +}) + +apply ExampleService @endpointTests({ + "testCases": [ + { + "documentation": "a documentation string", + "params": { + "stringFoo": "a b", + "boolFoo": false + }, + "expect": { + "error": "endpoint error" + } + }, + { + "params": { + "stringFoo": "c d", + "boolFoo": true + }, + "operationInputs": [{ + "operationName": "GetThing", + "clientParams": { + "stringFoo": "client value" + }, + "operationParams": { + "buzz": "a buzz value" + }, + "builtInParams": { + "SDK::Endpoint": "https://custom.example.com" + } + }], + "expect": { + "endpoint": { + "url": "https://example.com", + "properties": { + "authSchemes": [ + { + "name": "v4", + "signingName": "example", + "signingScope": "us-west-2" + } + ] + }, + "headers": { + "single": ["foo"], + "multi": ["foo", "bar", "baz"] + } + } + } + } + ] +}) + +@readonly +@staticContextParams( + stringBar: {value: "some value"}, + boolBar: {value: true} +) +operation GetThing { + input: GetThingInput +} + +@input +structure GetThingInput { + fizz: String, + + @contextParam(name: "stringBaz") + buzz: String, + + @contextParam(name: "boolBaz") + fuzz: String, +}