diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/analysis/OperationContextParamsChecker.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/analysis/OperationContextParamsChecker.java new file mode 100644 index 00000000000..9581d4c6f53 --- /dev/null +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/analysis/OperationContextParamsChecker.java @@ -0,0 +1,296 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rulesengine.analysis; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; +import software.amazon.smithy.jmespath.JmespathExpression; +import software.amazon.smithy.jmespath.LinterResult; +import software.amazon.smithy.jmespath.RuntimeType; +import software.amazon.smithy.jmespath.ast.LiteralExpression; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.knowledge.OperationIndex; +import software.amazon.smithy.model.shapes.BigDecimalShape; +import software.amazon.smithy.model.shapes.BigIntegerShape; +import software.amazon.smithy.model.shapes.BlobShape; +import software.amazon.smithy.model.shapes.BooleanShape; +import software.amazon.smithy.model.shapes.ByteShape; +import software.amazon.smithy.model.shapes.DocumentShape; +import software.amazon.smithy.model.shapes.DoubleShape; +import software.amazon.smithy.model.shapes.FloatShape; +import software.amazon.smithy.model.shapes.IntegerShape; +import software.amazon.smithy.model.shapes.ListShape; +import software.amazon.smithy.model.shapes.LongShape; +import software.amazon.smithy.model.shapes.MapShape; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.OperationShape; +import software.amazon.smithy.model.shapes.ResourceShape; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeVisitor; +import software.amazon.smithy.model.shapes.ShortShape; +import software.amazon.smithy.model.shapes.StringShape; +import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.shapes.TimestampShape; +import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.model.traits.LengthTrait; +import software.amazon.smithy.model.traits.RangeTrait; +import software.amazon.smithy.rulesengine.language.syntax.parameters.ParameterType; +import software.amazon.smithy.rulesengine.traits.OperationContextParamDefinition; + +public final class OperationContextParamsChecker { + + private OperationContextParamsChecker() { + + } + + public static LinterResult lint( + OperationContextParamDefinition paramDefinition, OperationShape operationShape, Model model) { + JmespathExpression path = JmespathExpression.parse(paramDefinition.getPath()); + StructureShape input = OperationIndex.of(model).expectInputShape(operationShape); + return path.lint(createCurrentNodeFromShape(input, model)); + } + + public static Optional inferParameterType( + OperationContextParamDefinition paramDefinition, OperationShape operationShape, Model model) { + RuntimeType runtimeType = lint(paramDefinition, operationShape, model).getReturnType(); + switch (runtimeType) { + case BOOLEAN: + return Optional.of(ParameterType.BOOLEAN); + case STRING: + return Optional.of(ParameterType.STRING); + case ARRAY: + return Optional.of(ParameterType.STRING_ARRAY); + default: + return Optional.empty(); + } + } + + private static LiteralExpression createCurrentNodeFromShape(Shape shape, Model model) { + return shape == null + ? LiteralExpression.ANY + : new LiteralExpression(shape.accept(new ModelRuntimeTypeGenerator(model))); + } + + + /** + * This class is duplicated from + * smithy-waiters/src/main/java/software/amazon/smithy/waiters/ModelRuntimeTypeGenerator.java + * + * It is duplicated here to avoid taking a dependency on smithy-waiters. + */ + private static final class ModelRuntimeTypeGenerator implements ShapeVisitor { + + private final Model model; + private Set visited = new HashSet<>(); + + ModelRuntimeTypeGenerator(Model model) { + this.model = model; + } + + @Override + public Object blobShape(BlobShape shape) { + return "blob"; + } + + @Override + public Object booleanShape(BooleanShape shape) { + return true; + } + + @Override + public Object byteShape(ByteShape shape) { + return computeRange(shape); + } + + @Override + public Object shortShape(ShortShape shape) { + return computeRange(shape); + } + + @Override + public Object integerShape(IntegerShape shape) { + return computeRange(shape); + } + + @Override + public Object longShape(LongShape shape) { + return computeRange(shape); + } + + @Override + public Object floatShape(FloatShape shape) { + return computeRange(shape); + } + + @Override + public Object doubleShape(DoubleShape shape) { + return computeRange(shape); + } + + @Override + public Object bigIntegerShape(BigIntegerShape shape) { + return computeRange(shape); + } + + @Override + public Object bigDecimalShape(BigDecimalShape shape) { + return computeRange(shape); + } + + @Override + public Object documentShape(DocumentShape shape) { + return LiteralExpression.ANY; + } + + @Override + public Object stringShape(StringShape shape) { + // Create a random string that does not exceed or go under the length trait. + int chars = computeLength(shape); + + // Fill a string with "a"'s up to chars. + return new String(new char[chars]).replace("\0", "a"); + } + + @Override + public Object listShape(ListShape shape) { + return withCopiedVisitors(() -> { + int size = computeLength(shape); + List result = new ArrayList<>(size); + Object memberValue = shape.getMember().accept(this); + if (memberValue != null) { + for (int i = 0; i < size; i++) { + result.add(memberValue); + } + } + return result; + }); + } + + // Visits members and mutates a copy of the current set of visited + // shapes rather than a shared set. This allows a shape to be used + // multiple times in the closure of a single shape without causing the + // reuse of the shape to always be assumed to be a recursive type. + private Object withCopiedVisitors(Supplier supplier) { + // Account for recursive shapes at the current + Set visitedCopy = new HashSet<>(visited); + Object result = supplier.get(); + visited = visitedCopy; + return result; + } + + @Override + public Object mapShape(MapShape shape) { + return withCopiedVisitors(() -> { + int size = computeLength(shape); + Map result = new HashMap<>(); + String key = (String) shape.getKey().accept(this); + Object memberValue = shape.getValue().accept(this); + for (int i = 0; i < size; i++) { + result.put(key + i, memberValue); + } + return result; + }); + } + + @Override + public Object structureShape(StructureShape shape) { + return structureOrUnion(shape); + } + + @Override + public Object unionShape(UnionShape shape) { + return structureOrUnion(shape); + } + + private Object structureOrUnion(Shape shape) { + return withCopiedVisitors(() -> { + Map result = new LinkedHashMap<>(); + for (MemberShape member : shape.members()) { + Object memberValue = member.accept(this); + result.put(member.getMemberName(), memberValue); + } + return result; + }); + } + + @Override + public Object memberShape(MemberShape shape) { + // Account for recursive shapes. + // A false return value means it was in the set. + if (!visited.add(shape)) { + return LiteralExpression.ANY; + } + + return model.getShape(shape.getTarget()) + .map(target -> target.accept(this)) + // Rather than fail on broken models during waiter validation, + // return an ANY to get *some* validation. + .orElse(LiteralExpression.ANY); + } + + @Override + public Object timestampShape(TimestampShape shape) { + return LiteralExpression.NUMBER; + } + + @Override + public Object operationShape(OperationShape shape) { + throw new UnsupportedOperationException(shape.toString()); + } + + @Override + public Object resourceShape(ResourceShape shape) { + throw new UnsupportedOperationException(shape.toString()); + } + + @Override + public Object serviceShape(ServiceShape shape) { + throw new UnsupportedOperationException(shape.toString()); + } + + private int computeLength(Shape shape) { + // Create a random string that does not exceed or go under the length trait. + int chars = 2; + + if (shape.hasTrait(LengthTrait.class)) { + LengthTrait trait = shape.expectTrait(LengthTrait.class); + if (trait.getMin().isPresent()) { + chars = Math.max(chars, trait.getMin().get().intValue()); + } + if (trait.getMax().isPresent()) { + chars = Math.min(chars, trait.getMax().get().intValue()); + } + } + + return chars; + } + + private double computeRange(Shape shape) { + // Create a random string that does not exceed or go under the range trait. + double i = 8; + + if (shape.hasTrait(RangeTrait.class)) { + RangeTrait trait = shape.expectTrait(RangeTrait.class); + if (trait.getMin().isPresent()) { + i = Math.max(i, trait.getMin().get().doubleValue()); + } + if (trait.getMax().isPresent()) { + i = Math.min(i, trait.getMax().get().doubleValue()); + } + } + + return i; + } + } +} diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/OperationContextParamsTraitValidator.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/OperationContextParamsTraitValidator.java index c652fc1c15a..e2666f1c3cc 100644 --- a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/OperationContextParamsTraitValidator.java +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/traits/OperationContextParamsTraitValidator.java @@ -7,13 +7,8 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import software.amazon.smithy.jmespath.ExpressionVisitor; import software.amazon.smithy.jmespath.JmespathException; @@ -38,34 +33,10 @@ import software.amazon.smithy.jmespath.ast.SliceExpression; import software.amazon.smithy.jmespath.ast.Subexpression; import software.amazon.smithy.model.Model; -import software.amazon.smithy.model.knowledge.OperationIndex; -import software.amazon.smithy.model.shapes.BigDecimalShape; -import software.amazon.smithy.model.shapes.BigIntegerShape; -import software.amazon.smithy.model.shapes.BlobShape; -import software.amazon.smithy.model.shapes.BooleanShape; -import software.amazon.smithy.model.shapes.ByteShape; -import software.amazon.smithy.model.shapes.DocumentShape; -import software.amazon.smithy.model.shapes.DoubleShape; -import software.amazon.smithy.model.shapes.FloatShape; -import software.amazon.smithy.model.shapes.IntegerShape; -import software.amazon.smithy.model.shapes.ListShape; -import software.amazon.smithy.model.shapes.LongShape; -import software.amazon.smithy.model.shapes.MapShape; -import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.OperationShape; -import software.amazon.smithy.model.shapes.ResourceShape; -import software.amazon.smithy.model.shapes.ServiceShape; -import software.amazon.smithy.model.shapes.Shape; -import software.amazon.smithy.model.shapes.ShapeVisitor; -import software.amazon.smithy.model.shapes.ShortShape; -import software.amazon.smithy.model.shapes.StringShape; -import software.amazon.smithy.model.shapes.StructureShape; -import software.amazon.smithy.model.shapes.TimestampShape; -import software.amazon.smithy.model.shapes.UnionShape; -import software.amazon.smithy.model.traits.LengthTrait; -import software.amazon.smithy.model.traits.RangeTrait; import software.amazon.smithy.model.validation.AbstractValidator; import software.amazon.smithy.model.validation.ValidationEvent; +import software.amazon.smithy.rulesengine.analysis.OperationContextParamsChecker; import software.amazon.smithy.utils.ListUtils; import software.amazon.smithy.utils.SmithyUnstableApi; @@ -86,8 +57,8 @@ public List validate(Model model) { try { JmespathExpression path = JmespathExpression.parse(entry.getValue().getPath()); - StructureShape input = OperationIndex.of(model).expectInputShape(operationShape); - LinterResult linterResult = path.lint(createCurrentNodeFromShape(input, model)); + LinterResult linterResult = OperationContextParamsChecker.lint( + entry.getValue(), operationShape, model); if (!linterResult.getProblems().isEmpty()) { events.add(error(operationShape, @@ -134,12 +105,6 @@ public List validate(Model model) { return events; } - private LiteralExpression createCurrentNodeFromShape(Shape shape, Model model) { - return shape == null - ? LiteralExpression.ANY - : new LiteralExpression(shape.accept(new ModelRuntimeTypeGenerator(model))); - } - private static final class UnsupportedJmesPathVisitor implements ExpressionVisitor> { @Override @@ -245,215 +210,4 @@ public List visitSubexpression(Subexpression expression) { return Collections.unmodifiableList(unsupported); } } - - /** - * This class is duplicated from - * smithy-waiters/src/main/java/software/amazon/smithy/waiters/ModelRuntimeTypeGenerator.java - * - * It is duplicated here to avoid taking a dependency on smithy-waiters. - */ - private static final class ModelRuntimeTypeGenerator implements ShapeVisitor { - - private final Model model; - private Set visited = new HashSet<>(); - - ModelRuntimeTypeGenerator(Model model) { - this.model = model; - } - - @Override - public Object blobShape(BlobShape shape) { - return "blob"; - } - - @Override - public Object booleanShape(BooleanShape shape) { - return true; - } - - @Override - public Object byteShape(ByteShape shape) { - return computeRange(shape); - } - - @Override - public Object shortShape(ShortShape shape) { - return computeRange(shape); - } - - @Override - public Object integerShape(IntegerShape shape) { - return computeRange(shape); - } - - @Override - public Object longShape(LongShape shape) { - return computeRange(shape); - } - - @Override - public Object floatShape(FloatShape shape) { - return computeRange(shape); - } - - @Override - public Object doubleShape(DoubleShape shape) { - return computeRange(shape); - } - - @Override - public Object bigIntegerShape(BigIntegerShape shape) { - return computeRange(shape); - } - - @Override - public Object bigDecimalShape(BigDecimalShape shape) { - return computeRange(shape); - } - - @Override - public Object documentShape(DocumentShape shape) { - return LiteralExpression.ANY; - } - - @Override - public Object stringShape(StringShape shape) { - // Create a random string that does not exceed or go under the length trait. - int chars = computeLength(shape); - - // Fill a string with "a"'s up to chars. - return new String(new char[chars]).replace("\0", "a"); - } - - @Override - public Object listShape(ListShape shape) { - return withCopiedVisitors(() -> { - int size = computeLength(shape); - List result = new ArrayList<>(size); - Object memberValue = shape.getMember().accept(this); - if (memberValue != null) { - for (int i = 0; i < size; i++) { - result.add(memberValue); - } - } - return result; - }); - } - - // Visits members and mutates a copy of the current set of visited - // shapes rather than a shared set. This allows a shape to be used - // multiple times in the closure of a single shape without causing the - // reuse of the shape to always be assumed to be a recursive type. - private Object withCopiedVisitors(Supplier supplier) { - // Account for recursive shapes at the current - Set visitedCopy = new HashSet<>(visited); - Object result = supplier.get(); - visited = visitedCopy; - return result; - } - - @Override - public Object mapShape(MapShape shape) { - return withCopiedVisitors(() -> { - int size = computeLength(shape); - Map result = new HashMap<>(); - String key = (String) shape.getKey().accept(this); - Object memberValue = shape.getValue().accept(this); - for (int i = 0; i < size; i++) { - result.put(key + i, memberValue); - } - return result; - }); - } - - @Override - public Object structureShape(StructureShape shape) { - return structureOrUnion(shape); - } - - @Override - public Object unionShape(UnionShape shape) { - return structureOrUnion(shape); - } - - private Object structureOrUnion(Shape shape) { - return withCopiedVisitors(() -> { - Map result = new LinkedHashMap<>(); - for (MemberShape member : shape.members()) { - Object memberValue = member.accept(this); - result.put(member.getMemberName(), memberValue); - } - return result; - }); - } - - @Override - public Object memberShape(MemberShape shape) { - // Account for recursive shapes. - // A false return value means it was in the set. - if (!visited.add(shape)) { - return LiteralExpression.ANY; - } - - return model.getShape(shape.getTarget()) - .map(target -> target.accept(this)) - // Rather than fail on broken models during waiter validation, - // return an ANY to get *some* validation. - .orElse(LiteralExpression.ANY); - } - - @Override - public Object timestampShape(TimestampShape shape) { - return LiteralExpression.NUMBER; - } - - @Override - public Object operationShape(OperationShape shape) { - throw new UnsupportedOperationException(shape.toString()); - } - - @Override - public Object resourceShape(ResourceShape shape) { - throw new UnsupportedOperationException(shape.toString()); - } - - @Override - public Object serviceShape(ServiceShape shape) { - throw new UnsupportedOperationException(shape.toString()); - } - - private int computeLength(Shape shape) { - // Create a random string that does not exceed or go under the length trait. - int chars = 2; - - if (shape.hasTrait(LengthTrait.class)) { - LengthTrait trait = shape.expectTrait(LengthTrait.class); - if (trait.getMin().isPresent()) { - chars = Math.max(chars, trait.getMin().get().intValue()); - } - if (trait.getMax().isPresent()) { - chars = Math.min(chars, trait.getMax().get().intValue()); - } - } - - return chars; - } - - private double computeRange(Shape shape) { - // Create a random string that does not exceed or go under the range trait. - double i = 8; - - if (shape.hasTrait(RangeTrait.class)) { - RangeTrait trait = shape.expectTrait(RangeTrait.class); - if (trait.getMin().isPresent()) { - i = Math.max(i, trait.getMin().get().doubleValue()); - } - if (trait.getMax().isPresent()) { - i = Math.min(i, trait.getMax().get().doubleValue()); - } - } - - return i; - } - } } diff --git a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/validators/RuleSetParameterValidator.java b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/validators/RuleSetParameterValidator.java index a08d3bc0b71..27f6d9f4571 100644 --- a/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/validators/RuleSetParameterValidator.java +++ b/smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/validators/RuleSetParameterValidator.java @@ -10,6 +10,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import software.amazon.smithy.model.FromSourceLocation; import software.amazon.smithy.model.Model; @@ -22,6 +23,7 @@ import software.amazon.smithy.model.shapes.StructureShape; import software.amazon.smithy.model.validation.AbstractValidator; import software.amazon.smithy.model.validation.ValidationEvent; +import software.amazon.smithy.rulesengine.analysis.OperationContextParamsChecker; import software.amazon.smithy.rulesengine.language.EndpointRuleSet; import software.amazon.smithy.rulesengine.language.evaluation.value.Value; import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameter; @@ -34,6 +36,7 @@ import software.amazon.smithy.rulesengine.traits.EndpointTestCase; import software.amazon.smithy.rulesengine.traits.EndpointTestOperationInput; import software.amazon.smithy.rulesengine.traits.EndpointTestsTrait; +import software.amazon.smithy.rulesengine.traits.OperationContextParamsTrait; import software.amazon.smithy.rulesengine.traits.StaticContextParamDefinition; import software.amazon.smithy.rulesengine.traits.StaticContextParamsTrait; import software.amazon.smithy.utils.ListUtils; @@ -80,7 +83,7 @@ private Pair, Map> validateAndExtractPa if (serviceShape.hasTrait(ClientContextParamsTrait.class)) { ClientContextParamsTrait trait = serviceShape.expectTrait(ClientContextParamsTrait.class); - for (Map.Entry entry : trait.getParameters().entrySet()) { + for (Map.Entry entry : trait.getParameters().entrySet()) { endpointParams.put(entry.getKey(), Parameter.builder() .name(entry.getKey()) .type(ParameterType.fromShapeType(entry.getValue().getType())) @@ -108,6 +111,26 @@ private Pair, Map> validateAndExtractPa } } + if (operationShape.hasTrait(OperationContextParamsTrait.ID)) { + OperationContextParamsTrait trait = operationShape.expectTrait(OperationContextParamsTrait.class); + trait.getParameters().forEach((name, p) -> { + Optional maybeType = OperationContextParamsChecker + .inferParameterType(p, operationShape, model); + maybeType.ifPresent(parameterType -> { + if (endpointParams.containsKey(name) && endpointParams.get(name).getType() != parameterType) { + errors.add(parameterError(operationShape, trait, + "OperationContextParams.InconsistentType", + String.format("Inconsistent type for `%s` parameter", name))); + } else { + endpointParams.put(name, Parameter.builder() + .name(name) + .type(parameterType) + .build()); + } + }); + }); + } + StructureShape input = model.expectShape(operationShape.getInputShape(), StructureShape.class); for (MemberShape memberShape : input.members()) { if (memberShape.hasTrait(ContextParamTrait.class)) { @@ -200,7 +223,7 @@ private List validateTestsParameters( // All test parameter types from corresponding ruleset parameters must match in all test cases. if (!testSuiteHasParam) { errors.add(danger(serviceShape, parameter, - String.format("Parameter `%s` is never used in an `EndpointTests` test case", name)) + String.format("Parameter `%s` is never used in an `EndpointTests` test case", name)) .toBuilder() .id(getName() + ".TestCase.Unused").build()); } else { @@ -208,7 +231,7 @@ private List validateTestsParameters( if (testParam.getType() != parameter.getType()) { errors.add(parameterError(serviceShape, testParam, "TestCase.TypeMismatch", String.format("Type mismatch for parameter `%s`, `%s` expected", - testParam.getName().toString(), parameter.getType()))); + testParam.getName().toString(), parameter.getType()))); } } } @@ -302,11 +325,11 @@ private Parameter buildParameter(Map.Entry entry, String name) { private Parameter buildParameter(String name, Node node) { Value value = Value.fromNode(node); return Parameter.builder() - .sourceLocation(value) - .name(name) - .value(value) - .type(ParameterType.fromType(value.getType())) - .build(); + .sourceLocation(value) + .name(name) + .value(value) + .type(ParameterType.fromType(value.getType())) + .build(); } private List merge(List previousList, List newList) { diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/language/errorfiles/invalid/inconsistent-params.errors b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/language/errorfiles/invalid/inconsistent-params.errors index 0ac0351134e..9af8d2d2a77 100644 --- a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/language/errorfiles/invalid/inconsistent-params.errors +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/language/errorfiles/invalid/inconsistent-params.errors @@ -4,9 +4,14 @@ [WARNING] example#GetResourceInput$ResourceId: This shape applies a trait that is unstable: smithy.rules#contextParam | UnstableTrait [WARNING] example#GetAnotherResource: This shape applies a trait that is unstable: smithy.rules#staticContextParams | UnstableTrait [WARNING] example#GetResource: This shape applies a trait that is unstable: smithy.rules#staticContextParams | UnstableTrait +[WARNING] example#GetResource: This shape applies a trait that is unstable: smithy.rules#operationContextParams | UnstableTrait.smithy.rules#operationContextParams +[WARNING] example#GetAnotherResource: This shape applies a trait that is unstable: smithy.rules#operationContextParams | UnstableTrait.smithy.rules#operationContextParams [ERROR] example#GetResource: Inconsistent type for `InconsistentParamType` parameter | RuleSetParameter.StaticContextParams.InconsistentType [ERROR] example#FizzBuzz: Type mismatch for parameter `ParameterFoo` | RuleSetParameter.RuleSet.TypeMismatch [ERROR] example#FizzBuzz: Parameter `ExtraParameter` exists in ruleset but not in service model | RuleSetParameter.RuleSet.UnmatchedName [ERROR] example#FizzBuzz: Parameter `AnotherParameterBar` exists in service model but not in ruleset | RuleSetParameter.RuleSet.UnmatchedName [ERROR] example#FizzBuzz: Parameter `InconsistentParamType` exists in service model but not in ruleset | RuleSetParameter.RuleSet.UnmatchedName [ERROR] example#FizzBuzz: Parameter `ParamNotInRuleset` exists in service model but not in ruleset | RuleSetParameter.RuleSet.UnmatchedName +[ERROR] example#FizzBuzz: Parameter `InconsistentOperactionContextParam` exists in service model but not in ruleset | RuleSetParameter.RuleSet.UnmatchedName +[ERROR] example#FizzBuzz: Type mismatch for parameter `StringArrayParam` | RuleSetParameter.RuleSet.TypeMismatch +[ERROR] example#GetResource: Inconsistent type for `InconsistentOperactionContextParam` parameter | RuleSetParameter.OperationContextParams.InconsistentType diff --git a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/language/errorfiles/invalid/inconsistent-params.smithy b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/language/errorfiles/invalid/inconsistent-params.smithy index c3e9e39e4f5..4c64d3adcc4 100644 --- a/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/language/errorfiles/invalid/inconsistent-params.smithy +++ b/smithy-rules-engine/src/test/resources/software/amazon/smithy/rulesengine/language/errorfiles/invalid/inconsistent-params.smithy @@ -5,6 +5,7 @@ namespace example use smithy.rules#clientContextParams use smithy.rules#contextParam use smithy.rules#endpointRuleSet +use smithy.rules#operationContextParams use smithy.rules#staticContextParams @endpointRuleSet({ @@ -30,6 +31,10 @@ use smithy.rules#staticContextParams "type": "string", "documentation": "docs", "builtIn": "SDK::Endpoint" + }, + "StringArrayParam": { + "type": "stringArray", + "documentation": "docs" } }, "rules": [] @@ -47,6 +52,10 @@ service FizzBuzz { "ParamNotInRuleset": {value: "someValue"}, "InconsistentParamType": {value: true} ) +@operationContextParams( + "StringArrayParam": {path: "ResourceId"}, + "InconsistentOperactionContextParam": {path: "ResourceId"} +) operation GetResource { input: GetResourceInput } @@ -61,13 +70,21 @@ structure GetResourceInput { "ParamNotInRuleset": {value: "someOtherValue"}, "InconsistentParamType": {value: "someValue"} ) +@operationContextParams( + "InconsistentOperactionContextParam": {path: "ListOfStrings[*]"} +) operation GetAnotherResource { input: GetAnotherResourceInput } structure GetAnotherResourceInput { @contextParam(name: "AnotherParameterBar") - ResourceId: ResourceId + ResourceId: ResourceId, + ListOfStrings: ListOfStrings +} + +list ListOfStrings { + member: String } string ResourceId