Skip to content

Commit

Permalink
Normalize map keys for shapes targeted by HeaderPrefix during deseria…
Browse files Browse the repository at this point in the history
…lization.
  • Loading branch information
skmcgrail committed Dec 16, 2020
1 parent 30597f9 commit 96ed422
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.function.BiFunction;
import java.util.logging.Logger;
Expand All @@ -28,10 +29,13 @@
import software.amazon.smithy.codegen.core.SymbolDependency;
import software.amazon.smithy.codegen.core.SymbolDependencyContainer;
import software.amazon.smithy.codegen.core.SymbolReference;
import software.amazon.smithy.go.codegen.knowledge.GoUsageIndex;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.MemberShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.traits.DeprecatedTrait;
import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.model.traits.HttpPrefixHeadersTrait;
import software.amazon.smithy.model.traits.MediaTypeTrait;
import software.amazon.smithy.model.traits.RequiredTrait;
import software.amazon.smithy.model.traits.StringTrait;
Expand Down Expand Up @@ -290,24 +294,60 @@ boolean writePackageShapeDocs(Shape shape) {
* @return Returns true if docs were written.
*/
boolean writeMemberDocs(Model model, MemberShape member) {
return member.getMemberTrait(model, DocumentationTrait.class)
boolean hasDocs;

hasDocs = member.getMemberTrait(model, DocumentationTrait.class)
.map(DocumentationTrait::getValue)
.map(docs -> {
writeDocs(docs);
member.getMemberTrait(model, MediaTypeTrait.class)
.map(StringTrait::getValue)
.ifPresent(mediaType -> writeDocs(
"\n\nThis value conforms to the media type: " + mediaType));

member.getMemberTrait(model, RequiredTrait.class)
.ifPresent((value) -> {
if (docs.length() != 0) {
writeDocs("");
}
writeDocs("This member is required.");
});
return true;
}).orElse(false);

Optional<String> stringOptional = member.getMemberTrait(model, MediaTypeTrait.class)
.map(StringTrait::getValue);
if (stringOptional.isPresent()) {
if (hasDocs) {
writeDocs("");
}
writeDocs("This value conforms to the media type: " + stringOptional.get());
hasDocs = true;
}

GoUsageIndex usageIndex = GoUsageIndex.of(model);
if (usageIndex.isUsedForOutput(member)) {
if (member.getMemberTrait(model,
HttpPrefixHeadersTrait.class).isPresent()) {
if (hasDocs) {
writeDocs("");
}
writeDocs("Map keys will be normalized to lower-case.");
hasDocs = true;
}
}

if (member.getMemberTrait(model, RequiredTrait.class).isPresent()) {
if (hasDocs) {
writeDocs("");
}
writeDocs("This member is required.");
hasDocs = true;
}

Optional<DeprecatedTrait> deprecatedTrait = member.getMemberTrait(model, DeprecatedTrait.class);
if (deprecatedTrait.isPresent()) {
if (hasDocs) {
writeDocs("");
}
final String defaultMessage = "This member has been deprecated.";
writeDocs("Deprecated: " + deprecatedTrait.get().getMessage().map(s -> {
if (s.length() == 0) {
return defaultMessage;
}
return s;
}).orElse(defaultMessage));
}

return hasDocs;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import software.amazon.smithy.model.shapes.OperationShape;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.traits.DeprecatedTrait;

/**
* Generates a client operation and associated custom shapes.
Expand Down Expand Up @@ -91,7 +92,20 @@ public void run() {
Symbol outputSymbol = symbolProvider.toSymbol(outputShape);

// Generate operation method
writer.writeShapeDocs(operation);
final boolean hasDocs = writer.writeShapeDocs(operation);
operation.getTrait(DeprecatedTrait.class)
.ifPresent(trait -> {
if (hasDocs) {
writer.writeDocs("");
}
final String defaultMessage = "This operation has been deprecated.";
writer.writeDocs("Deprecated: " + trait.getMessage().map(s -> {
if (s.length() == 0) {
return defaultMessage;
}
return s;
}).orElse(defaultMessage));
});
Symbol contextSymbol = SymbolUtils.createValueSymbolBuilder("Context", SmithyGoDependency.CONTEXT).build();
writer.openBlock("func (c $P) $T(ctx $T, params $P, optFns ...func(*Options)) ($P, error) {", "}",
serviceSymbol, operationSymbol, contextSymbol, inputSymbol, outputSymbol, () -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public void generateUnionExamples(GoWriter writer) {
symbol.getNamespace()).build();

writer.openBlock("case *$T:", "", memberSymbol, () -> {
writer.write("_ = v.Value // Value is $L", targetSymbol.getName());
writer.write("_ = v.Value // Value is $T", targetSymbol);
});
}
writer.addUseImports(SmithyGoDependency.FMT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1086,7 +1086,7 @@ private void writePrefixHeaderDeserializerFunction(

String value = generateHttpHeaderValue(context, writer, valueMemberShape,
binding, operand);
writer.write("v.$L[headerKey[lenPrefix:]] = $L", memberName,
writer.write("v.$L[strings.ToLower(headerKey[lenPrefix:])] = $L", memberName,
CodegenUtils.getAsPointerIfPointable(context.getModel(), writer,
GoPointableIndex.of(context.getModel()), valueMemberShape, value));
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2020 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.go.codegen.knowledge;

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.knowledge.KnowledgeIndex;
import software.amazon.smithy.model.knowledge.OperationIndex;
import software.amazon.smithy.model.knowledge.TopDownIndex;
import software.amazon.smithy.model.neighbor.RelationshipDirection;
import software.amazon.smithy.model.neighbor.Walker;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.shapes.ToShapeId;

/**
* Provides {@link KnowledgeIndex} of how shapes are used in the model.
*/
public class GoUsageIndex implements KnowledgeIndex {
private final Model model;
private final Walker walker;

private final Set<ShapeId> inputShapes = new HashSet<>();
private final Set<ShapeId> outputShapes = new HashSet<>();

public GoUsageIndex(Model model) {
this.model = model;
this.walker = new Walker(model);

TopDownIndex topDownIndex = TopDownIndex.of(model);
OperationIndex operationIndex = OperationIndex.of(model);

model.shapes(ServiceShape.class).forEach(serviceShape -> {
topDownIndex.getContainedOperations(serviceShape).forEach(operationShape -> {
StructureShape inputShape = operationIndex.getInput(operationShape).get();
StructureShape outputShape = operationIndex.getOutput(operationShape).get();

inputShapes.addAll(walker.walkShapes(inputShape, relationship ->
relationship.getDirection() == RelationshipDirection.DIRECTED).stream()
.map(Shape::toShapeId).collect(Collectors.toList()));

outputShapes.addAll(walker.walkShapes(outputShape, relationship ->
relationship.getDirection() == RelationshipDirection.DIRECTED).stream()
.map(Shape::toShapeId).collect(Collectors.toList()));

});
});
}

/**
* Returns whether shape is used as part of an input to an operation.
*
* @param shape the shape
* @return whether the shape is used as input.
*/
public boolean isUsedForInput(ToShapeId shape) {
return inputShapes.contains(shape.toShapeId());
}

/**
* Returns whether shape is used as output of an operation.
*
* @param shape the shape
* @return whether the shape is used as input.
*/
public boolean isUsedForOutput(ToShapeId shape) {
return outputShapes.contains(shape.toShapeId());
}

public static GoUsageIndex of(Model model) {
return model.getKnowledge(GoUsageIndex.class, GoUsageIndex::new);
}
}

0 comments on commit 96ed422

Please sign in to comment.