forked from aws/aws-sdk-go-v2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Route53Customizations.java
164 lines (144 loc) · 7.9 KB
/
Route53Customizations.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package software.amazon.smithy.aws.go.codegen.customization;
import java.util.List;
import java.util.stream.Collectors;
import software.amazon.smithy.aws.traits.ServiceTrait;
import software.amazon.smithy.codegen.core.CodegenException;
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.go.codegen.GoDelegator;
import software.amazon.smithy.go.codegen.GoSettings;
import software.amazon.smithy.go.codegen.GoWriter;
import software.amazon.smithy.go.codegen.SmithyGoDependency;
import software.amazon.smithy.go.codegen.SymbolUtils;
import software.amazon.smithy.go.codegen.integration.GoIntegration;
import software.amazon.smithy.go.codegen.integration.MiddlewareRegistrar;
import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.knowledge.TopDownIndex;
import software.amazon.smithy.model.shapes.MemberShape;
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.utils.ListUtils;
public class Route53Customizations implements GoIntegration {
private static final String ADD_ERROR_HANDLER_INTERNAL = "HandleCustomErrorDeserialization";
private static final String URL_SANITIZE_ADDER = "addSanitizeURLMiddleware";
private static final String URL_SANITIZE_INTERNAL_ADDER= "AddSanitizeURLMiddleware";
private static final String SANITIZE_HOSTED_ZONE_ID_INPUT = "sanitizeHostedZoneIDInput";
@Override
public byte getOrder() {
// The associated customization ordering is relative to operation deserializers
// and thus the integration should be added at the end.
return 127;
}
@Override
public List<RuntimeClientPlugin> getClientPlugins() {
return ListUtils.of(
RuntimeClientPlugin.builder()
.operationPredicate(Route53Customizations::supportsCustomError)
.registerMiddleware(MiddlewareRegistrar.builder()
.resolvedFunction(SymbolUtils.createValueSymbolBuilder(ADD_ERROR_HANDLER_INTERNAL,
AwsCustomGoDependency.ROUTE53_CUSTOMIZATION).build())
.build())
.build(),
RuntimeClientPlugin.builder()
.operationPredicate(Route53Customizations::supportsHostedZoneIDValue)
.registerMiddleware(MiddlewareRegistrar.builder()
.resolvedFunction(SymbolUtils.createValueSymbolBuilder(URL_SANITIZE_ADDER).build())
.build())
.build()
);
}
public void writeAdditionalFiles(
GoSettings settings,
Model model,
SymbolProvider symbolProvider,
GoDelegator goDelegator
) {
if (!isRoute53Service(model, settings.getService(model))) {
return;
}
ServiceShape service = settings.getService(model);
goDelegator.useShapeWriter(service, this::writeMiddlewareHelper);
goDelegator.useShapeWriter(service, writer -> {
writeHostedZoneIDInputSanitizer(writer, model, symbolProvider, service);
});
}
private void writeMiddlewareHelper(GoWriter writer) {
writer.openBlock("func $L(stack *middleware.Stack) error {", "}", URL_SANITIZE_ADDER, () -> {
writer.write("return $T(stack, $T{SanitizeHostedZoneIDInput: $L})",
SymbolUtils.createValueSymbolBuilder(URL_SANITIZE_INTERNAL_ADDER,
AwsCustomGoDependency.ROUTE53_CUSTOMIZATION).build(),
SymbolUtils.createValueSymbolBuilder(URL_SANITIZE_INTERNAL_ADDER + "Options",
AwsCustomGoDependency.ROUTE53_CUSTOMIZATION).build(),
SANITIZE_HOSTED_ZONE_ID_INPUT
);
});
writer.insertTrailingNewline();
}
private void writeHostedZoneIDInputSanitizer(
GoWriter writer,
Model model,
SymbolProvider symbolProvider,
ServiceShape service
) {
writer.writeDocs("Check for and split apart Route53 resource IDs, setting only the last piece. " +
"This allows the output of one operation e.g. foo/1234 to be used as input in another operation " +
"(e.g. it expects just '1234')");
writer.openBlock("func sanitizeHostedZoneIDInput(input interface{}) error {", "}", () -> {
writer.openBlock("switch i:= input.(type) {", "}", () -> {
TopDownIndex.of(model).getContainedOperations(service).forEach((operation)-> {
StructureShape input = model.expectShape(operation.getInput().get(), StructureShape.class);
List<MemberShape> hostedZoneIDMembers = input.getAllMembers().values().stream()
.filter(m -> m.getTarget().getName(service).equalsIgnoreCase("ResourceId")
|| m.getTarget().getName(service).equalsIgnoreCase("DelegationSetId"))
.collect(Collectors.toList());
if (!hostedZoneIDMembers.isEmpty()){
writer.openBlock("case $P :", "", symbolProvider.toSymbol(input), () -> {
writer.addUseImports(SmithyGoDependency.STRINGS);
for (MemberShape member : hostedZoneIDMembers) {
String memberName = member.getMemberName();
writer.openBlock("if i.$L != nil {", "}", memberName, () -> {
writer.write("idx := strings.LastIndex(*i.$L, `/`)", memberName);
writer.write("v := (*i.$L)[idx+1:]", memberName);
writer.write("i.$L = &v", memberName);
});
}
});
}
});
writer.write("default: break");
});
writer.write("return nil");
});
}
// returns true if the operation supports custom route53 error response
private static boolean supportsCustomError(Model model, ServiceShape service, OperationShape operation) {
if (!isRoute53Service(model, service)) {
return false;
}
return operation.getId().getName(service).equalsIgnoreCase("ChangeResourceRecordSets");
}
// return true if the operation takes input that supports Hosted zone ID.
//
// For Route53, HostedZoneID is supported by member shapes targeting `ResourceId` or `DelegationSetId`.
private static boolean supportsHostedZoneIDValue(Model model, ServiceShape service, OperationShape operation) {
if (!isRoute53Service(model, service)) {
return false;
}
StructureShape input = model.expectShape(operation.getInput().get(), StructureShape.class);
List<MemberShape> targetMembers = input.getAllMembers().values().stream().filter(
memberShape -> memberShape.getTarget().getName(service).equalsIgnoreCase("ResourceId") ||
memberShape.getTarget().getName(service).equalsIgnoreCase("DelegationSetId")
).collect(Collectors.toList());
if (targetMembers.size() >1 ){
throw new CodegenException(String.format("Route53 service has ResourceId, DelegationSetId members " +
"modeled on %s shape", input.getId().getName(service)));
}
return targetMembers.size() != 0;
}
// returns true if service is route53
private static boolean isRoute53Service(Model model, ServiceShape service) {
String serviceId= service.expectTrait(ServiceTrait.class).getSdkId();
return serviceId.equalsIgnoreCase("Route 53");
}
}