Skip to content

Commit e43da3a

Browse files
committed
Normalize map keys for shapes targeted by HeaderPrefix during deserialization.
1 parent 30597f9 commit e43da3a

File tree

5 files changed

+161
-16
lines changed

5 files changed

+161
-16
lines changed

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Arrays;
2020
import java.util.Collection;
2121
import java.util.List;
22+
import java.util.Optional;
2223
import java.util.StringJoiner;
2324
import java.util.function.BiFunction;
2425
import java.util.logging.Logger;
@@ -28,10 +29,13 @@
2829
import software.amazon.smithy.codegen.core.SymbolDependency;
2930
import software.amazon.smithy.codegen.core.SymbolDependencyContainer;
3031
import software.amazon.smithy.codegen.core.SymbolReference;
32+
import software.amazon.smithy.go.codegen.knowledge.GoUsageIndex;
3133
import software.amazon.smithy.model.Model;
3234
import software.amazon.smithy.model.shapes.MemberShape;
3335
import software.amazon.smithy.model.shapes.Shape;
36+
import software.amazon.smithy.model.traits.DeprecatedTrait;
3437
import software.amazon.smithy.model.traits.DocumentationTrait;
38+
import software.amazon.smithy.model.traits.HttpPrefixHeadersTrait;
3539
import software.amazon.smithy.model.traits.MediaTypeTrait;
3640
import software.amazon.smithy.model.traits.RequiredTrait;
3741
import software.amazon.smithy.model.traits.StringTrait;
@@ -290,24 +294,60 @@ boolean writePackageShapeDocs(Shape shape) {
290294
* @return Returns true if docs were written.
291295
*/
292296
boolean writeMemberDocs(Model model, MemberShape member) {
293-
return member.getMemberTrait(model, DocumentationTrait.class)
297+
boolean hasDocs;
298+
299+
hasDocs = member.getMemberTrait(model, DocumentationTrait.class)
294300
.map(DocumentationTrait::getValue)
295301
.map(docs -> {
296302
writeDocs(docs);
297-
member.getMemberTrait(model, MediaTypeTrait.class)
298-
.map(StringTrait::getValue)
299-
.ifPresent(mediaType -> writeDocs(
300-
"\n\nThis value conforms to the media type: " + mediaType));
301-
302-
member.getMemberTrait(model, RequiredTrait.class)
303-
.ifPresent((value) -> {
304-
if (docs.length() != 0) {
305-
writeDocs("");
306-
}
307-
writeDocs("This member is required.");
308-
});
309303
return true;
310304
}).orElse(false);
305+
306+
Optional<String> stringOptional = member.getMemberTrait(model, MediaTypeTrait.class)
307+
.map(StringTrait::getValue);
308+
if (stringOptional.isPresent()) {
309+
if (hasDocs) {
310+
writeDocs("");
311+
}
312+
writeDocs("This value conforms to the media type: " + stringOptional.get());
313+
hasDocs = true;
314+
}
315+
316+
GoUsageIndex usageIndex = GoUsageIndex.of(model);
317+
if (usageIndex.isUsedForOutput(member)) {
318+
if (member.getMemberTrait(model,
319+
HttpPrefixHeadersTrait.class).isPresent()) {
320+
if (hasDocs) {
321+
writeDocs("");
322+
}
323+
writeDocs("Map keys will be normalized to lower-case.");
324+
hasDocs = true;
325+
}
326+
}
327+
328+
if (member.getMemberTrait(model, RequiredTrait.class).isPresent()) {
329+
if (hasDocs) {
330+
writeDocs("");
331+
}
332+
writeDocs("This member is required.");
333+
hasDocs = true;
334+
}
335+
336+
Optional<DeprecatedTrait> deprecatedTrait = member.getMemberTrait(model, DeprecatedTrait.class);
337+
if (deprecatedTrait.isPresent()) {
338+
if (hasDocs) {
339+
writeDocs("");
340+
}
341+
final String defaultMessage = "This member has been deprecated.";
342+
writeDocs("Deprecated: " + deprecatedTrait.get().getMessage().map(s -> {
343+
if (s.length() == 0) {
344+
return defaultMessage;
345+
}
346+
return s;
347+
}).orElse(defaultMessage));
348+
}
349+
350+
return hasDocs;
311351
}
312352

313353
@Override

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.ArrayList;
1919
import java.util.Collection;
2020
import java.util.List;
21+
import java.util.Optional;
2122
import software.amazon.smithy.codegen.core.CodegenException;
2223
import software.amazon.smithy.codegen.core.Symbol;
2324
import software.amazon.smithy.codegen.core.SymbolProvider;
@@ -29,6 +30,7 @@
2930
import software.amazon.smithy.model.shapes.OperationShape;
3031
import software.amazon.smithy.model.shapes.ServiceShape;
3132
import software.amazon.smithy.model.shapes.StructureShape;
33+
import software.amazon.smithy.model.traits.DeprecatedTrait;
3234

3335
/**
3436
* Generates a client operation and associated custom shapes.
@@ -91,7 +93,20 @@ public void run() {
9193
Symbol outputSymbol = symbolProvider.toSymbol(outputShape);
9294

9395
// Generate operation method
94-
writer.writeShapeDocs(operation);
96+
final boolean hasDocs = writer.writeShapeDocs(operation);
97+
operation.getTrait(DeprecatedTrait.class)
98+
.ifPresent(trait -> {
99+
if (hasDocs) {
100+
writer.writeDocs("");
101+
}
102+
final String defaultMessage = "This operation has been deprecated.";
103+
writer.writeDocs("Deprecated: " + trait.getMessage().map(s -> {
104+
if (s.length() == 0) {
105+
return defaultMessage;
106+
}
107+
return s;
108+
}).orElse(defaultMessage));
109+
});
95110
Symbol contextSymbol = SymbolUtils.createValueSymbolBuilder("Context", SmithyGoDependency.CONTEXT).build();
96111
writer.openBlock("func (c $P) $T(ctx $T, params $P, optFns ...func(*Options)) ($P, error) {", "}",
97112
serviceSymbol, operationSymbol, contextSymbol, inputSymbol, outputSymbol, () -> {

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public void generateUnionExamples(GoWriter writer) {
109109
symbol.getNamespace()).build();
110110

111111
writer.openBlock("case *$T:", "", memberSymbol, () -> {
112-
writer.write("_ = v.Value // Value is $L", targetSymbol.getName());
112+
writer.write("_ = v.Value // Value is $T", targetSymbol);
113113
});
114114
}
115115
writer.addUseImports(SmithyGoDependency.FMT);

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/HttpBindingProtocolGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,7 @@ private void writePrefixHeaderDeserializerFunction(
10861086

10871087
String value = generateHttpHeaderValue(context, writer, valueMemberShape,
10881088
binding, operand);
1089-
writer.write("v.$L[headerKey[lenPrefix:]] = $L", memberName,
1089+
writer.write("v.$L[strings.ToLower(headerKey[lenPrefix:])] = $L", memberName,
10901090
CodegenUtils.getAsPointerIfPointable(context.getModel(), writer,
10911091
GoPointableIndex.of(context.getModel()), valueMemberShape, value));
10921092
});
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.smithy.go.codegen.knowledge;
17+
18+
import java.util.HashSet;
19+
import java.util.Set;
20+
import java.util.stream.Collectors;
21+
import software.amazon.smithy.model.Model;
22+
import software.amazon.smithy.model.knowledge.KnowledgeIndex;
23+
import software.amazon.smithy.model.knowledge.OperationIndex;
24+
import software.amazon.smithy.model.knowledge.TopDownIndex;
25+
import software.amazon.smithy.model.neighbor.RelationshipDirection;
26+
import software.amazon.smithy.model.neighbor.Walker;
27+
import software.amazon.smithy.model.shapes.ServiceShape;
28+
import software.amazon.smithy.model.shapes.Shape;
29+
import software.amazon.smithy.model.shapes.ShapeId;
30+
import software.amazon.smithy.model.shapes.StructureShape;
31+
import software.amazon.smithy.model.shapes.ToShapeId;
32+
33+
/**
34+
* Provides {@link KnowledgeIndex} of how shapes are used in the model.
35+
*/
36+
public class GoUsageIndex implements KnowledgeIndex {
37+
private final Model model;
38+
private final Walker walker;
39+
40+
private final Set<ShapeId> inputShapes = new HashSet<>();
41+
private final Set<ShapeId> outputShapes = new HashSet<>();
42+
43+
public GoUsageIndex(Model model) {
44+
this.model = model;
45+
this.walker = new Walker(model);
46+
47+
TopDownIndex topDownIndex = TopDownIndex.of(model);
48+
OperationIndex operationIndex = OperationIndex.of(model);
49+
50+
model.shapes(ServiceShape.class).forEach(serviceShape -> {
51+
topDownIndex.getContainedOperations(serviceShape).forEach(operationShape -> {
52+
StructureShape inputShape = operationIndex.getInput(operationShape).get();
53+
StructureShape outputShape = operationIndex.getOutput(operationShape).get();
54+
55+
inputShapes.addAll(walker.walkShapes(inputShape, relationship ->
56+
relationship.getDirection() == RelationshipDirection.DIRECTED).stream()
57+
.map(Shape::toShapeId).collect(Collectors.toList()));
58+
59+
outputShapes.addAll(walker.walkShapes(outputShape, relationship ->
60+
relationship.getDirection() == RelationshipDirection.DIRECTED).stream()
61+
.map(Shape::toShapeId).collect(Collectors.toList()));
62+
63+
});
64+
});
65+
}
66+
67+
/**
68+
* Returns whether shape is used as part of an input to an operation.
69+
*
70+
* @param shape the shape
71+
* @return whether the shape is used as input.
72+
*/
73+
public boolean isUsedForInput(ToShapeId shape) {
74+
return inputShapes.contains(shape.toShapeId());
75+
}
76+
77+
/**
78+
* Returns whether shape is used as output of an operation.
79+
*
80+
* @param shape the shape
81+
* @return whether the shape is used as input.
82+
*/
83+
public boolean isUsedForOutput(ToShapeId shape) {
84+
return outputShapes.contains(shape.toShapeId());
85+
}
86+
87+
public static GoUsageIndex of(Model model) {
88+
return model.getKnowledge(GoUsageIndex.class, GoUsageIndex::new);
89+
}
90+
}

0 commit comments

Comments
 (0)