diff --git a/aws/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/clientendpointdiscovery/ClientEndpointDiscoveryValidator.java b/aws/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/clientendpointdiscovery/ClientEndpointDiscoveryValidator.java index 3c2951856c2..979feff7dc5 100644 --- a/aws/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/clientendpointdiscovery/ClientEndpointDiscoveryValidator.java +++ b/aws/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/clientendpointdiscovery/ClientEndpointDiscoveryValidator.java @@ -38,6 +38,8 @@ import software.amazon.smithy.utils.SetUtils; public class ClientEndpointDiscoveryValidator extends AbstractValidator { + private static final Set VALID_INPUT_MEMBERS = SetUtils.of("Operation", "Identifiers"); + @Override public List validate(Model model) { ClientEndpointDiscoveryIndex discoveryIndex = model.getKnowledge(ClientEndpointDiscoveryIndex.class); @@ -133,37 +135,9 @@ private List validateEndpointOperation( Model model, OperationIndex opIndex, OperationShape operation ) { List events = new ArrayList<>(); - opIndex.getInput(operation).ifPresent(input -> { - Set memberNames = SetUtils.copyOf(input.getMemberNames()); - if (!memberNames.equals(SetUtils.of("Operation", "Identifiers"))) { - events.add(error(input, String.format( - "Input for endpoint discovery operation `%s` may only have the members Operation and " - + "Identifiers but found: %s", - operation.getId().toString(), - String.join(", ", memberNames) - ))); - } - - input.getMember("Operation") - .flatMap(member -> model.getShape(member.getTarget())) - .filter(shape -> !shape.isStringShape()) - .ifPresent(shape -> events.add(error( - shape, "The Operation member of an endpoint discovery operation must be a string"))); - - input.getMember("Identifiers") - .map(member -> Pair.of(member, model.getShape(member.getTarget()))) - .ifPresent(pair -> { - Optional map = pair.getRight().flatMap(Shape::asMapShape); - if (map.isPresent()) { - Optional value = model.getShape(map.get().getValue().getTarget()); - if (value.isPresent() && value.get().isStringShape()) { - return; - } - } - events.add(error(pair.getLeft(), "The Identifiers member of an endpoint discovery " - + "operation must be a map whose keys and values are strings.")); - }); - }); + opIndex.getInput(operation) + .map(input -> validateEndpointOperationInput(model, input, operation)) + .ifPresent(events::addAll); Optional output = opIndex.getOutput(operation); if (!output.isPresent()) { @@ -224,4 +198,40 @@ private List validateEndpointOperation( return events; } + + private List validateEndpointOperationInput( + Model model, StructureShape input, OperationShape operation + ) { + List events = new ArrayList<>(); + Set memberNames = SetUtils.copyOf(input.getMemberNames()); + if (!VALID_INPUT_MEMBERS.containsAll(memberNames)) { + events.add(error(input, String.format( + "Input for endpoint discovery operation `%s` may only have the members Operation and " + + "Identifiers but found: %s", + operation.getId().toString(), + String.join(", ", memberNames) + ))); + } + + input.getMember("Operation") + .flatMap(member -> model.getShape(member.getTarget())) + .filter(shape -> !shape.isStringShape()) + .ifPresent(shape -> events.add(error( + shape, "The Operation member of an endpoint discovery operation must be a string"))); + + input.getMember("Identifiers") + .map(member -> Pair.of(member, model.getShape(member.getTarget()))) + .ifPresent(pair -> { + Optional map = pair.getRight().flatMap(Shape::asMapShape); + if (map.isPresent()) { + Optional value = model.getShape(map.get().getValue().getTarget()); + if (value.isPresent() && value.get().isStringShape()) { + return; + } + } + events.add(error(pair.getLeft(), "The Identifiers member of an endpoint discovery " + + "operation must be a map whose keys and values are strings.")); + }); + return events; + } } diff --git a/aws/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/clientendpointdiscovery/errorfiles/no-input.errors b/aws/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/clientendpointdiscovery/errorfiles/no-input.errors new file mode 100644 index 00000000000..e69de29bb2d diff --git a/aws/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/clientendpointdiscovery/errorfiles/no-input.json b/aws/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/clientendpointdiscovery/errorfiles/no-input.json new file mode 100644 index 00000000000..f025f2f3905 --- /dev/null +++ b/aws/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/clientendpointdiscovery/errorfiles/no-input.json @@ -0,0 +1,111 @@ +{ + "smithy": "0.4.0", + "ns.foo": { + "shapes": { + "FooService": { + "type": "service", + "version": "2019-09-10", + "aws.api#clientEndpointDiscovery": { + "operation": "ns.foo#DescribeEndpoints", + "error": "ns.foo#InvalidEndpointError" + }, + "operations": [ + "DescribeEndpoints", + "GetObject", + "PutObject" + ] + }, + "BarService": { + "type": "service", + "version": "2019-09-10", + "operations": [ + "DescribeEndpoints" + ] + }, + "DescribeEndpoints": { + "type": "operation", + "input": "DescribeEndpointsInput", + "output": "DescribeEndpointsOutput" + }, + "DescribeEndpointsInput": { + "type": "structure" + }, + "DescribeEndpointsOutput": { + "type": "structure", + "members": { + "Endpoints": { + "target": "Endpoints" + } + } + }, + "Endpoints": { + "type": "list", + "member": { + "target": "Endpoint" + } + }, + "Endpoint": { + "type": "structure", + "members": { + "Address": { + "target": "smithy.api#String" + }, + "CachePeriodInMinutes": { + "target": "smithy.api#Long" + } + } + }, + "GetObject": { + "type": "operation", + "input": "GetObjectInput", + "output": "GetObjectOutput", + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "errors": ["InvalidEndpointError"] + }, + "GetObjectInput": { + "type": "structure", + "members": { + "Id": { + "target": "smithy.api#String", + "required": true + } + } + }, + "GetObjectOutput": { + "type": "structure", + "members": { + "Object": { + "target": "smithy.api#Blob" + } + } + }, + "PutObject": { + "type": "operation", + "input": "PutObjectInput", + "aws.api#clientDiscoveredEndpoint": { + "required": false + }, + "errors": ["InvalidEndpointError"] + }, + "PutObjectInput": { + "type": "structure", + "members": { + "Id": { + "target": "smithy.api#String", + "required": true + }, + "Object": { + "target": "smithy.api#Blob" + } + } + }, + "InvalidEndpointError": { + "type": "structure", + "error": "client", + "httpError": 421 + } + } + } +} \ No newline at end of file