Skip to content

Commit 1a962c6

Browse files
Merge pull request #220 from JordonPhillips/fix-endpoint-discovery
Properly allow omitting endpoint operation inputs
2 parents 2a16c8f + 13c06b1 commit 1a962c6

File tree

3 files changed

+152
-31
lines changed

3 files changed

+152
-31
lines changed

aws/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/clientendpointdiscovery/ClientEndpointDiscoveryValidator.java

+41-31
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
import software.amazon.smithy.utils.SetUtils;
3939

4040
public class ClientEndpointDiscoveryValidator extends AbstractValidator {
41+
private static final Set<String> VALID_INPUT_MEMBERS = SetUtils.of("Operation", "Identifiers");
42+
4143
@Override
4244
public List<ValidationEvent> validate(Model model) {
4345
ClientEndpointDiscoveryIndex discoveryIndex = model.getKnowledge(ClientEndpointDiscoveryIndex.class);
@@ -133,37 +135,9 @@ private List<ValidationEvent> validateEndpointOperation(
133135
Model model, OperationIndex opIndex, OperationShape operation
134136
) {
135137
List<ValidationEvent> events = new ArrayList<>();
136-
opIndex.getInput(operation).ifPresent(input -> {
137-
Set<String> memberNames = SetUtils.copyOf(input.getMemberNames());
138-
if (!memberNames.equals(SetUtils.of("Operation", "Identifiers"))) {
139-
events.add(error(input, String.format(
140-
"Input for endpoint discovery operation `%s` may only have the members Operation and "
141-
+ "Identifiers but found: %s",
142-
operation.getId().toString(),
143-
String.join(", ", memberNames)
144-
)));
145-
}
146-
147-
input.getMember("Operation")
148-
.flatMap(member -> model.getShape(member.getTarget()))
149-
.filter(shape -> !shape.isStringShape())
150-
.ifPresent(shape -> events.add(error(
151-
shape, "The Operation member of an endpoint discovery operation must be a string")));
152-
153-
input.getMember("Identifiers")
154-
.map(member -> Pair.of(member, model.getShape(member.getTarget())))
155-
.ifPresent(pair -> {
156-
Optional<MapShape> map = pair.getRight().flatMap(Shape::asMapShape);
157-
if (map.isPresent()) {
158-
Optional<Shape> value = model.getShape(map.get().getValue().getTarget());
159-
if (value.isPresent() && value.get().isStringShape()) {
160-
return;
161-
}
162-
}
163-
events.add(error(pair.getLeft(), "The Identifiers member of an endpoint discovery "
164-
+ "operation must be a map whose keys and values are strings."));
165-
});
166-
});
138+
opIndex.getInput(operation)
139+
.map(input -> validateEndpointOperationInput(model, input, operation))
140+
.ifPresent(events::addAll);
167141

168142
Optional<StructureShape> output = opIndex.getOutput(operation);
169143
if (!output.isPresent()) {
@@ -224,4 +198,40 @@ private List<ValidationEvent> validateEndpointOperation(
224198

225199
return events;
226200
}
201+
202+
private List<ValidationEvent> validateEndpointOperationInput(
203+
Model model, StructureShape input, OperationShape operation
204+
) {
205+
List<ValidationEvent> events = new ArrayList<>();
206+
Set<String> memberNames = SetUtils.copyOf(input.getMemberNames());
207+
if (!VALID_INPUT_MEMBERS.containsAll(memberNames)) {
208+
events.add(error(input, String.format(
209+
"Input for endpoint discovery operation `%s` may only have the members Operation and "
210+
+ "Identifiers but found: %s",
211+
operation.getId().toString(),
212+
String.join(", ", memberNames)
213+
)));
214+
}
215+
216+
input.getMember("Operation")
217+
.flatMap(member -> model.getShape(member.getTarget()))
218+
.filter(shape -> !shape.isStringShape())
219+
.ifPresent(shape -> events.add(error(
220+
shape, "The Operation member of an endpoint discovery operation must be a string")));
221+
222+
input.getMember("Identifiers")
223+
.map(member -> Pair.of(member, model.getShape(member.getTarget())))
224+
.ifPresent(pair -> {
225+
Optional<MapShape> map = pair.getRight().flatMap(Shape::asMapShape);
226+
if (map.isPresent()) {
227+
Optional<Shape> value = model.getShape(map.get().getValue().getTarget());
228+
if (value.isPresent() && value.get().isStringShape()) {
229+
return;
230+
}
231+
}
232+
events.add(error(pair.getLeft(), "The Identifiers member of an endpoint discovery "
233+
+ "operation must be a map whose keys and values are strings."));
234+
});
235+
return events;
236+
}
227237
}

aws/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/clientendpointdiscovery/errorfiles/no-input.errors

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
{
2+
"smithy": "0.4.0",
3+
"ns.foo": {
4+
"shapes": {
5+
"FooService": {
6+
"type": "service",
7+
"version": "2019-09-10",
8+
"aws.api#clientEndpointDiscovery": {
9+
"operation": "ns.foo#DescribeEndpoints",
10+
"error": "ns.foo#InvalidEndpointError"
11+
},
12+
"operations": [
13+
"DescribeEndpoints",
14+
"GetObject",
15+
"PutObject"
16+
]
17+
},
18+
"BarService": {
19+
"type": "service",
20+
"version": "2019-09-10",
21+
"operations": [
22+
"DescribeEndpoints"
23+
]
24+
},
25+
"DescribeEndpoints": {
26+
"type": "operation",
27+
"input": "DescribeEndpointsInput",
28+
"output": "DescribeEndpointsOutput"
29+
},
30+
"DescribeEndpointsInput": {
31+
"type": "structure"
32+
},
33+
"DescribeEndpointsOutput": {
34+
"type": "structure",
35+
"members": {
36+
"Endpoints": {
37+
"target": "Endpoints"
38+
}
39+
}
40+
},
41+
"Endpoints": {
42+
"type": "list",
43+
"member": {
44+
"target": "Endpoint"
45+
}
46+
},
47+
"Endpoint": {
48+
"type": "structure",
49+
"members": {
50+
"Address": {
51+
"target": "smithy.api#String"
52+
},
53+
"CachePeriodInMinutes": {
54+
"target": "smithy.api#Long"
55+
}
56+
}
57+
},
58+
"GetObject": {
59+
"type": "operation",
60+
"input": "GetObjectInput",
61+
"output": "GetObjectOutput",
62+
"aws.api#clientDiscoveredEndpoint": {
63+
"required": true
64+
},
65+
"errors": ["InvalidEndpointError"]
66+
},
67+
"GetObjectInput": {
68+
"type": "structure",
69+
"members": {
70+
"Id": {
71+
"target": "smithy.api#String",
72+
"required": true
73+
}
74+
}
75+
},
76+
"GetObjectOutput": {
77+
"type": "structure",
78+
"members": {
79+
"Object": {
80+
"target": "smithy.api#Blob"
81+
}
82+
}
83+
},
84+
"PutObject": {
85+
"type": "operation",
86+
"input": "PutObjectInput",
87+
"aws.api#clientDiscoveredEndpoint": {
88+
"required": false
89+
},
90+
"errors": ["InvalidEndpointError"]
91+
},
92+
"PutObjectInput": {
93+
"type": "structure",
94+
"members": {
95+
"Id": {
96+
"target": "smithy.api#String",
97+
"required": true
98+
},
99+
"Object": {
100+
"target": "smithy.api#Blob"
101+
}
102+
}
103+
},
104+
"InvalidEndpointError": {
105+
"type": "structure",
106+
"error": "client",
107+
"httpError": 421
108+
}
109+
}
110+
}
111+
}

0 commit comments

Comments
 (0)