Skip to content

Commit 2c4b943

Browse files
kuhesrchase
authored andcommitted
feat(endpoint): endpoints v2 codegen (smithy-lang#586)
1 parent 60afda1 commit 2c4b943

12 files changed

+699
-3
lines changed

smithy-typescript-codegen/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ buildscript {
2929

3030
dependencies {
3131
api("software.amazon.smithy:smithy-codegen-core:${rootProject.extra["smithyVersion"]}")
32+
api("software.amazon.smithy:smithy-rules-engine:${rootProject.extra["smithyVersion"]}")
3233
api("software.amazon.smithy:smithy-waiters:${rootProject.extra["smithyVersion"]}")
3334
implementation("software.amazon.smithy:smithy-protocol-test-traits:${rootProject.extra["smithyVersion"]}")
3435
}

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CodegenVisitor.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@
5151
import software.amazon.smithy.model.traits.EnumTrait;
5252
import software.amazon.smithy.model.traits.PaginatedTrait;
5353
import software.amazon.smithy.model.validation.ValidationEvent;
54+
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
5455
import software.amazon.smithy.typescript.codegen.TypeScriptSettings.ArtifactType;
56+
import software.amazon.smithy.typescript.codegen.endpointsV2.EndpointsV2Generator;
5557
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator;
5658
import software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin;
5759
import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration;
@@ -323,6 +325,7 @@ public Void serviceShape(ServiceShape shape) {
323325
}
324326
if (settings.generateClient() || settings.generateServerSdk()) {
325327
generateCommands(shape);
328+
generateEndpointV2(shape);
326329
}
327330

328331
if (settings.generateServerSdk()) {
@@ -444,4 +447,14 @@ private void generateCommands(ServiceShape shape) {
444447
}
445448
}
446449
}
450+
451+
private void generateEndpointV2(ServiceShape shape) {
452+
if (!shape.hasTrait(EndpointRuleSetTrait.class)) {
453+
return;
454+
}
455+
456+
new EndpointsV2Generator(
457+
settings, model, fileManifest
458+
).run();
459+
}
447460
}

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CommandGenerator.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
import software.amazon.smithy.model.shapes.OperationShape;
3636
import software.amazon.smithy.model.shapes.ServiceShape;
3737
import software.amazon.smithy.model.shapes.StructureShape;
38+
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
39+
import software.amazon.smithy.typescript.codegen.endpointsV2.EndpointsParamNameMap;
40+
import software.amazon.smithy.typescript.codegen.endpointsV2.RuleSetParameterFinder;
3841
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator;
3942
import software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin;
4043
import software.amazon.smithy.utils.OptionalUtils;
@@ -185,6 +188,46 @@ private void generateCommandMiddlewareResolver(String configType) {
185188
// Add customizations.
186189
addCommandSpecificPlugins();
187190

191+
// EndpointsV2
192+
if (service.hasTrait(EndpointRuleSetTrait.class)) {
193+
writer.addImport(
194+
"getEndpointPlugin",
195+
"getEndpointPlugin",
196+
"@aws-sdk/middleware-endpoint"
197+
);
198+
writer.openBlock(
199+
"this.middlewareStack.use(getEndpointPlugin(configuration, {",
200+
"}));",
201+
() -> {
202+
RuleSetParameterFinder parameterFinder = new RuleSetParameterFinder(service);
203+
parameterFinder.getBuiltInParams().forEach((name, type) -> {
204+
writer.write(
205+
"$L: { type: \"builtInParams\", name: \"$L\" },",
206+
name, EndpointsParamNameMap.getLocalName(name)
207+
);
208+
});
209+
parameterFinder.getClientContextParams().forEach((name, type) -> {
210+
writer.write(
211+
"$L: { type: \"clientContextParams\", name: \"$L\" },",
212+
name, EndpointsParamNameMap.getLocalName(name)
213+
);
214+
});
215+
parameterFinder.getStaticContextParamValues(operation).forEach((name, value) -> {
216+
writer.write(
217+
"$L: { type: \"staticContextParams\", value: $L },",
218+
name, value
219+
);
220+
});
221+
parameterFinder.getContextParams(operation).forEach((name, type) -> {
222+
writer.write(
223+
"$L: { type: \"contextParams\", name: \"$L\" },",
224+
name, EndpointsParamNameMap.getLocalName(name)
225+
);
226+
});
227+
}
228+
);
229+
}
230+
188231
// Resolve the middleware stack.
189232
writer.write("\nconst stack = clientStack.concat(this.middlewareStack);\n");
190233
writer.write("const { logger } = configuration;");

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/HttpProtocolTestGenerator.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,6 @@ private void generateMalformedRequestTest(OperationShape operation, HttpMalforme
389389
});
390390
writer.write("const r = await handler.handle(request, {});").write("");
391391

392-
// Assert that the function has been called exactly once.
393392
writer.write("expect(testFunction.mock.calls.length).toBe(0);");
394393

395394
writeHttpResponseAssertions(testCase.getResponse());

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import software.amazon.smithy.model.knowledge.TopDownIndex;
3232
import software.amazon.smithy.model.shapes.OperationShape;
3333
import software.amazon.smithy.model.shapes.ServiceShape;
34+
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
3435
import software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin;
3536
import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration;
3637
import software.amazon.smithy.utils.OptionalUtils;
@@ -169,7 +170,15 @@ private void generateConfig() {
169170
if (!inputTypes.isEmpty()) {
170171
writer.indent();
171172
for (SymbolReference symbolReference : inputTypes) {
172-
writer.write("& $T", symbolReference);
173+
if (service.hasTrait(EndpointRuleSetTrait.class)
174+
&& symbolReference.getAlias().equals("EndpointInputConfig")) {
175+
writer.write("& $T<$L>", symbolReference, "EndpointParameters");
176+
} else {
177+
writer.write("& $T", symbolReference);
178+
}
179+
}
180+
if (service.hasTrait(EndpointRuleSetTrait.class)) {
181+
writer.write("& ClientInputEndpointParameters");
173182
}
174183
writer.dedent();
175184
}
@@ -189,7 +198,17 @@ private void generateConfig() {
189198
writer.indent();
190199
runtimePlugins.stream()
191200
.flatMap(p -> OptionalUtils.stream(p.getResolvedConfig()))
192-
.forEach(symbol -> writer.write("& $T", symbol));
201+
.forEach(symbol -> {
202+
if (service.hasTrait(EndpointRuleSetTrait.class)
203+
&& symbol.getAlias().equals("EndpointResolvedConfig")) {
204+
writer.write("& $T<$L>", symbol, "EndpointParameters");
205+
} else {
206+
writer.write("& $T", symbol);
207+
}
208+
});
209+
if (service.hasTrait(EndpointRuleSetTrait.class)) {
210+
writer.write("& ClientResolvedEndpointParameters");
211+
}
193212
writer.dedent();
194213
}
195214

@@ -331,6 +350,14 @@ private void generateConstructor() {
331350
}
332351
}
333352

353+
if (service.hasTrait(EndpointRuleSetTrait.class)) {
354+
configVariable++;
355+
writer.write("let $L = $L($L);",
356+
generateConfigVariable(configVariable),
357+
"resolveClientEndpointParameters",
358+
generateConfigVariable(configVariable - 1));
359+
}
360+
334361
writer.write("super($L);", generateConfigVariable(configVariable));
335362
writer.write("this.config = $L;", generateConfigVariable(configVariable));
336363

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptDependency.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public enum TypeScriptDependency implements SymbolDependencyContainer {
5151
MIDDLEWARE_SERDE("dependencies", "@aws-sdk/middleware-serde", true),
5252
MIDDLEWARE_RETRY("dependencies", "@aws-sdk/middleware-retry", true),
5353
MIDDLEWARE_STACK("dependencies", "@aws-sdk/middleware-stack", true),
54+
MIDDLEWARE_ENDPOINTS_V2("dependencies", "@aws-sdk/middleware-endpoint", false),
5455

5556
AWS_CRYPTO_SHA256_BROWSER("dependencies", "@aws-crypto/sha256-browser", "2.0.0", true),
5657
AWS_CRYPTO_SHA256_JS("dependencies", "@aws-crypto/sha256-js", "2.0.0", true),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2019 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.typescript.codegen.endpointsV2;
17+
18+
import java.util.Map;
19+
import software.amazon.smithy.utils.MapUtils;
20+
21+
22+
public final class EndpointsParamNameMap {
23+
/**
24+
* Map of EndpointsV2 ruleset param name to existing JSv3 config param names.
25+
*/
26+
private static final Map<String, String> MAPPING = MapUtils.of(
27+
"Region", "region",
28+
"UseFIPS", "useFipsEndpoint",
29+
"UseDualStack", "useDualstackEndpoint",
30+
"ForcePathStyle", "forcePathStyle",
31+
"Accelerate", "useAccelerateEndpoint",
32+
"DisableMRAP", "disableMultiregionAccessPoints",
33+
"UseArnRegion", "useArnRegion"
34+
);
35+
36+
private EndpointsParamNameMap() {}
37+
38+
public static String getLocalName(String endpointsV2ParamName) {
39+
return MAPPING.getOrDefault(endpointsV2ParamName, endpointsV2ParamName);
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright 2019 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.typescript.codegen.endpointsV2;
17+
18+
import java.nio.file.Paths;
19+
import java.util.Map;
20+
import software.amazon.smithy.build.FileManifest;
21+
import software.amazon.smithy.model.Model;
22+
import software.amazon.smithy.model.node.ObjectNode;
23+
import software.amazon.smithy.model.shapes.ServiceShape;
24+
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
25+
import software.amazon.smithy.typescript.codegen.CodegenUtils;
26+
import software.amazon.smithy.typescript.codegen.TypeScriptSettings;
27+
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
28+
import software.amazon.smithy.utils.SmithyInternalApi;
29+
30+
/**
31+
* Generates a service endpoint resolver.
32+
*/
33+
@SmithyInternalApi
34+
public final class EndpointsV2Generator implements Runnable {
35+
36+
static final String ENDPOINT_FOLDER = "endpoint";
37+
38+
private final FileManifest fileManifest;
39+
private final EndpointRuleSetTrait endpointRuleSetTrait;
40+
private final ServiceShape service;
41+
42+
public EndpointsV2Generator(
43+
TypeScriptSettings settings,
44+
Model model,
45+
FileManifest fileManifest
46+
) {
47+
this.fileManifest = fileManifest;
48+
service = settings.getService(model);
49+
endpointRuleSetTrait = service.getTrait(EndpointRuleSetTrait.class)
50+
.orElseThrow(() -> new RuntimeException("service missing EndpointRuleSetTrait"));
51+
}
52+
53+
@Override
54+
public void run() {
55+
generateEndpointParameters();
56+
generateEndpointResolver();
57+
generateEndpointRuleset();
58+
}
59+
60+
/**
61+
* Generate the EndpointParameters interface file specific to this service.
62+
*/
63+
private void generateEndpointParameters() {
64+
TypeScriptWriter writer = new TypeScriptWriter("");
65+
66+
writer.addImport("EndpointParameters", "__EndpointParameters", "@aws-sdk/types");
67+
68+
writer.openBlock(
69+
"export interface ClientInputEndpointParameters {",
70+
"}",
71+
() -> {
72+
Map<String, String> clientContextParams =
73+
new RuleSetParameterFinder(service).getClientContextParams();
74+
75+
clientContextParams.forEach((k, v) -> {
76+
writer.write("$L: $L,", k, v);
77+
});
78+
}
79+
);
80+
81+
writer.write("export type ClientResolvedEndpointParameters = ClientInputEndpointParameters;");
82+
83+
writer.openBlock(
84+
"export const resolveClientEndpointParameters = "
85+
+ "<T>(options: T & ClientInputEndpointParameters"
86+
+ "): T & ClientResolvedEndpointParameters => {",
87+
"}",
88+
() -> {
89+
writer.openBlock("return {", "}", () -> {
90+
writer.write("...options,");
91+
});
92+
}
93+
);
94+
95+
writer.openBlock(
96+
"export interface EndpointParameters extends __EndpointParameters {",
97+
"}",
98+
() -> {
99+
ObjectNode ruleSet = endpointRuleSetTrait.getRuleSet().expectObjectNode();
100+
ruleSet.getObjectMember("parameters").ifPresent(parameters -> {
101+
parameters.accept(new RuleSetParametersVisitor(writer));
102+
});
103+
}
104+
);
105+
106+
fileManifest.writeFile(
107+
Paths.get(CodegenUtils.SOURCE_FOLDER, ENDPOINT_FOLDER, "EndpointParameters.ts").toString(),
108+
writer.toString()
109+
);
110+
}
111+
112+
/**
113+
* Generate the resolver function for this service.
114+
*/
115+
private void generateEndpointResolver() {
116+
TypeScriptWriter writer = new TypeScriptWriter("");
117+
118+
writer.addImport("EndpointV2", "EndpointV2", "@aws-sdk/types");
119+
writer.addImport("Logger", "Logger", "@aws-sdk/types");
120+
121+
writer.addImport("endpointProvider", "endpointProvider", "@aws-sdk/util-endpoints");
122+
writer.addImport("EndpointParameters", "EndpointParameters", "../endpoint/EndpointParameters");
123+
writer.addImport("ruleSet", "ruleSet", "../endpoint/ruleset");
124+
125+
writer.openBlock(
126+
"export const defaultEndpointResolver = ",
127+
"",
128+
() -> {
129+
writer.openBlock(
130+
"(param: EndpointParameters, context: { logger?: Logger } = {}): EndpointV2 => {",
131+
"};",
132+
() -> {
133+
// TODO(endpointsV2) cache
134+
writer.write("return endpointProvider(param, ruleSet, context);");
135+
}
136+
);
137+
}
138+
);
139+
140+
fileManifest.writeFile(
141+
Paths.get(CodegenUtils.SOURCE_FOLDER, ENDPOINT_FOLDER, "endpointResolver.ts").toString(),
142+
writer.toString()
143+
);
144+
}
145+
146+
/**
147+
* Generate the ruleset (dynamic resolution only).
148+
*/
149+
private void generateEndpointRuleset() {
150+
TypeScriptWriter writer = new TypeScriptWriter("");
151+
152+
writer.addImport("RuleSet", "RuleSet", "@aws-sdk/util-endpoints");
153+
154+
writer.openBlock(
155+
"export const ruleSet: RuleSet = ",
156+
"",
157+
() -> {
158+
new RuleSetSerializer(
159+
endpointRuleSetTrait.getRuleSet(),
160+
writer
161+
).generate();
162+
}
163+
);
164+
165+
fileManifest.writeFile(
166+
Paths.get(CodegenUtils.SOURCE_FOLDER, ENDPOINT_FOLDER, "ruleset.ts").toString(),
167+
writer.toString()
168+
);
169+
}
170+
}

0 commit comments

Comments
 (0)