Skip to content

Commit 8aa8b04

Browse files
committed
feat(endpoint): endpoints v2 codegen
1 parent e81b7d0 commit 8aa8b04

File tree

9 files changed

+109
-49
lines changed

9 files changed

+109
-49
lines changed

codegen/sdk-codegen/build.gradle.kts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ import software.amazon.smithy.model.shapes.ServiceShape
1818
import software.amazon.smithy.model.node.Node
1919
import software.amazon.smithy.gradle.tasks.SmithyBuild
2020
import software.amazon.smithy.aws.traits.ServiceTrait
21+
import java.util.stream.Stream
2122
import kotlin.streams.toList
2223

2324
buildscript {
2425
repositories {
26+
mavenLocal()
2527
mavenCentral()
2628
}
2729
dependencies {
@@ -62,7 +64,10 @@ tasks.register("generate-smithy-build") {
6264
val model = Model.assembler()
6365
.addImport(file.absolutePath)
6466
.assemble().result.get();
65-
val services = model.shapes(ServiceShape::class.javaObjectType).sorted().toList();
67+
val servicesStream: Stream<ServiceShape> = model.shapes(ServiceShape::class.javaObjectType)
68+
val servicesStreamSorted: Stream<ServiceShape> = servicesStream.sorted()
69+
val services: List<ServiceShape> = servicesStreamSorted.toList()
70+
6671
if (services.size != 1) {
6772
throw Exception("There must be exactly one service in each aws model file, but found " +
6873
"${services.size} in ${file.name}: ${services.map { it.id }}");

codegen/smithy-aws-typescript-codegen/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ dependencies {
3636
api("software.amazon.smithy:smithy-aws-iam-traits:${rootProject.extra["smithyVersion"]}")
3737
api("software.amazon.smithy:smithy-protocol-test-traits:${rootProject.extra["smithyVersion"]}")
3838
api("software.amazon.smithy:smithy-model:${rootProject.extra["smithyVersion"]}")
39+
api("software.amazon.smithy:smithy-rules-engine:${rootProject.extra["smithyVersion"]}")
3940
api("software.amazon.smithy.typescript:smithy-typescript-codegen:0.11.0")
4041
}
4142

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddBuiltinPlugins.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package software.amazon.smithy.aws.typescript.codegen;
1717

1818
import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService;
19+
import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isEndpointsV2Service;
1920
import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isSigV4Service;
2021
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_CONFIG;
2122
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_MIDDLEWARE;
@@ -53,11 +54,15 @@ public List<RuntimeClientPlugin> getClientPlugins() {
5354
// Only one of Endpoints or CustomEndpoints should be used
5455
RuntimeClientPlugin.builder()
5556
.withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "Endpoints", HAS_CONFIG)
56-
.servicePredicate((m, s) -> isAwsService(s))
57+
.servicePredicate((m, s) -> isAwsService(s) && !isEndpointsV2Service(s))
5758
.build(),
5859
RuntimeClientPlugin.builder()
5960
.withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "CustomEndpoints", HAS_CONFIG)
60-
.servicePredicate((m, s) -> !isAwsService(s))
61+
.servicePredicate((m, s) -> !isAwsService(s) && !isEndpointsV2Service(s))
62+
.build(),
63+
RuntimeClientPlugin.builder()
64+
.withConventions(TypeScriptDependency.MIDDLEWARE_ENDPOINTS_V2.dependency, "Endpoint", HAS_CONFIG)
65+
.servicePredicate((m, s) -> isAwsService(s) && isEndpointsV2Service(s))
6166
.build(),
6267
RuntimeClientPlugin.builder()
6368
.withConventions(TypeScriptDependency.MIDDLEWARE_RETRY.dependency, "Retry")

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddS3Config.java

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package software.amazon.smithy.aws.typescript.codegen;
1717

18+
import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isEndpointsV2Service;
1819
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_CONFIG;
1920
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_MIDDLEWARE;
2021

@@ -77,7 +78,7 @@ public final class AddS3Config implements TypeScriptIntegration {
7778
@Override
7879
public Model preprocessModel(Model model, TypeScriptSettings settings) {
7980
ServiceShape serviceShape = settings.getService(model);
80-
if (!testServiceId(serviceShape)) {
81+
if (!isS3(serviceShape)) {
8182
return model;
8283
}
8384
Model.Builder modelBuilder = model.toBuilder();
@@ -106,9 +107,14 @@ public Model preprocessModel(Model model, TypeScriptSettings settings) {
106107
}
107108

108109
@Override
109-
public void addConfigInterfaceFields(TypeScriptSettings settings, Model model, SymbolProvider symbolProvider,
110-
TypeScriptWriter writer) {
111-
if (!testServiceId(settings.getService(model))) {
110+
public void addConfigInterfaceFields(
111+
TypeScriptSettings settings,
112+
Model model,
113+
SymbolProvider symbolProvider,
114+
TypeScriptWriter writer
115+
) {
116+
ServiceShape service = settings.getService(model);
117+
if (!isS3(service)) {
112118
return;
113119
}
114120
writer.writeDocs("Whether to escape request path when signing the request.")
@@ -123,7 +129,7 @@ public void addConfigInterfaceFields(TypeScriptSettings settings, Model model, S
123129
@Override
124130
public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(TypeScriptSettings settings, Model model,
125131
SymbolProvider symbolProvider, LanguageTarget target) {
126-
if (!testServiceId(settings.getService(model))) {
132+
if (!isS3(settings.getService(model))) {
127133
return Collections.emptyMap();
128134
}
129135
switch (target) {
@@ -158,62 +164,68 @@ public List<RuntimeClientPlugin> getClientPlugins() {
158164
RuntimeClientPlugin.builder()
159165
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "ValidateBucketName",
160166
HAS_MIDDLEWARE)
161-
.servicePredicate((m, s) -> testServiceId(s))
167+
.servicePredicate((m, s) -> isS3(s))
162168
.build(),
163169
RuntimeClientPlugin.builder()
164170
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "CheckContentLengthHeader",
165171
HAS_MIDDLEWARE)
166-
.operationPredicate((m, s, o) -> testServiceId(s) && o.getId().getName(s).equals("PutObject"))
172+
.operationPredicate((m, s, o) -> isS3(s) && o.getId().getName(s).equals("PutObject"))
167173
.build(),
168174
RuntimeClientPlugin.builder()
169-
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "throw200Exceptions",
170-
HAS_MIDDLEWARE)
171-
.operationPredicate(
172-
(m, s, o) -> EXCEPTIONS_OF_200_OPERATIONS.contains(o.getId().getName(s))
173-
&& testServiceId(s))
174-
.build(),
175+
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "throw200Exceptions",
176+
HAS_MIDDLEWARE)
177+
.operationPredicate(
178+
(m, s, o) -> EXCEPTIONS_OF_200_OPERATIONS.contains(o.getId().getName(s))
179+
&& isS3(s))
180+
.build(),
175181
RuntimeClientPlugin.builder()
176-
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency,
177-
"WriteGetObjectResponseEndpoint", HAS_MIDDLEWARE)
178-
.operationPredicate((m, s, o) -> testServiceId(s)
179-
&& o.getId().getName(s).equals("WriteGetObjectResponse"))
180-
.build(),
182+
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency,
183+
"WriteGetObjectResponseEndpoint", HAS_MIDDLEWARE)
184+
.operationPredicate((m, s, o) -> isS3(s)
185+
&& o.getId().getName(s).equals("WriteGetObjectResponse"))
186+
.build(),
181187
RuntimeClientPlugin.builder()
182188
.withConventions(AwsDependency.ADD_EXPECT_CONTINUE.dependency, "AddExpectContinue",
183189
HAS_MIDDLEWARE)
184-
.servicePredicate((m, s) -> testServiceId(s))
190+
.servicePredicate((m, s) -> isS3(s))
185191
.build(),
186192
RuntimeClientPlugin.builder()
187193
.withConventions(AwsDependency.SSEC_MIDDLEWARE.dependency, "Ssec", HAS_MIDDLEWARE)
188-
.operationPredicate((m, s, o) -> testInputContainsMember(m, o, SSEC_INPUT_KEYS)
189-
&& testServiceId(s))
194+
.operationPredicate((m, s, o) -> containsInputMembers(m, o, SSEC_INPUT_KEYS)
195+
&& isS3(s))
190196
.build(),
191197
RuntimeClientPlugin.builder()
192198
.withConventions(AwsDependency.LOCATION_CONSTRAINT.dependency, "LocationConstraint",
193199
HAS_MIDDLEWARE)
194200
.operationPredicate((m, s, o) -> o.getId().getName(s).equals("CreateBucket")
195-
&& testServiceId(s))
201+
&& isS3(s))
196202
.build(),
197-
/**
203+
RuntimeClientPlugin.builder()
204+
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "S3",
205+
HAS_CONFIG)
206+
.servicePredicate((m, s) -> isS3(s) && isEndpointsV2Service(s))
207+
.build(),
208+
/*
198209
* BUCKET_ENDPOINT_MIDDLEWARE needs two separate plugins. The first resolves the config in the client.
199210
* The second applies the middleware to bucket endpoint operations.
200211
*/
201212
RuntimeClientPlugin.builder()
202213
.withConventions(AwsDependency.BUCKET_ENDPOINT_MIDDLEWARE.dependency, "BucketEndpoint",
203214
HAS_CONFIG)
204-
.servicePredicate((m, s) -> testServiceId(s))
215+
.servicePredicate((m, s) -> isS3(s) && !isEndpointsV2Service(s))
205216
.build(),
206217
RuntimeClientPlugin.builder()
207218
.withConventions(AwsDependency.BUCKET_ENDPOINT_MIDDLEWARE.dependency, "BucketEndpoint",
208219
HAS_MIDDLEWARE)
209220
.operationPredicate((m, s, o) -> !NON_BUCKET_ENDPOINT_OPERATIONS.contains(o.getId().getName(s))
210-
&& testServiceId(s)
211-
&& testInputContainsMember(m, o, BUCKET_ENDPOINT_INPUT_KEYS))
221+
&& isS3(s)
222+
&& !isEndpointsV2Service(s)
223+
&& containsInputMembers(m, o, BUCKET_ENDPOINT_INPUT_KEYS))
212224
.build()
213225
);
214226
}
215227

216-
private static boolean testInputContainsMember(
228+
private static boolean containsInputMembers(
217229
Model model,
218230
OperationShape operationShape,
219231
Set<String> expectedMemberNames
@@ -224,7 +236,7 @@ private static boolean testInputContainsMember(
224236
.isPresent();
225237
}
226238

227-
private static boolean testServiceId(Shape serviceShape) {
239+
private static boolean isS3(Shape serviceShape) {
228240
return serviceShape.getTrait(ServiceTrait.class).map(ServiceTrait::getSdkId).orElse("").equals("S3");
229241
}
230242
}

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsEndpointGeneratorIntegration.java

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,20 @@
1616
package software.amazon.smithy.aws.typescript.codegen;
1717

1818
import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService;
19+
import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isEndpointsV2Service;
1920

2021
import java.nio.file.Paths;
22+
import java.util.ArrayList;
2123
import java.util.Collections;
2224
import java.util.Map;
2325
import java.util.function.BiConsumer;
2426
import java.util.function.Consumer;
27+
28+
import com.sun.tools.javac.util.List;
2529
import software.amazon.smithy.codegen.core.SymbolProvider;
2630
import software.amazon.smithy.model.Model;
31+
import software.amazon.smithy.model.shapes.ServiceShape;
32+
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
2733
import software.amazon.smithy.typescript.codegen.CodegenUtils;
2834
import software.amazon.smithy.typescript.codegen.LanguageTarget;
2935
import software.amazon.smithy.typescript.codegen.TypeScriptDependency;
@@ -45,7 +51,9 @@ public void writeAdditionalFiles(
4551
SymbolProvider symbolProvider,
4652
BiConsumer<String, Consumer<TypeScriptWriter>> writerFactory
4753
) {
48-
if (!settings.generateClient() || !isAwsService(settings, model)) {
54+
if (!settings.generateClient()
55+
|| !isAwsService(settings, model)
56+
|| settings.getService(model).hasTrait(EndpointRuleSetTrait.class)) {
4957
return;
5058
}
5159

@@ -65,10 +73,28 @@ public void addConfigInterfaceFields(
6573
return;
6674
}
6775

68-
writer.addImport("RegionInfoProvider", "RegionInfoProvider", TypeScriptDependency.AWS_SDK_TYPES.packageName);
69-
writer.writeDocs("Fetch related hostname, signing name or signing region with given region.\n"
70-
+ "@internal");
71-
writer.write("regionInfoProvider?: RegionInfoProvider;\n");
76+
ServiceShape service = settings.getService(model);
77+
if (isEndpointsV2Service(service)) {
78+
new ArrayList<String>() {{
79+
add("ClientInputEndpointParameters");
80+
add("ClientResolvedEndpointParameters");
81+
add("resolveClientEndpointParameters");
82+
add("EndpointParameters");
83+
}}.forEach(name -> {
84+
writer.addImport(
85+
name,
86+
null,
87+
Paths.get(".", CodegenUtils.SOURCE_FOLDER, "endpoint/EndpointParameters").toString()
88+
);
89+
});
90+
91+
writer.addImport("EndpointV2", "__EndpointV2", TypeScriptDependency.AWS_SDK_TYPES.packageName);
92+
} else {
93+
writer.addImport("RegionInfoProvider", "RegionInfoProvider", TypeScriptDependency.AWS_SDK_TYPES.packageName);
94+
writer.writeDocs("Fetch related hostname, signing name or signing region with given region.\n"
95+
+ "@internal");
96+
writer.write("regionInfoProvider?: RegionInfoProvider;\n");
97+
}
7298
}
7399

74100
@Override
@@ -82,15 +108,23 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
82108
return Collections.emptyMap();
83109
}
84110

85-
switch (target) {
86-
case SHARED:
111+
if (settings.getService(model).hasTrait(EndpointRuleSetTrait.class)) {
112+
if (target == LanguageTarget.SHARED) {
113+
return MapUtils.of("endpointProvider", writer -> {
114+
writer.addImport("defaultEndpointResolver", null,
115+
Paths.get(".", CodegenUtils.SOURCE_FOLDER, "endpoint/endpointResolver").toString());
116+
writer.write("defaultEndpointResolver");
117+
});
118+
}
119+
} else {
120+
if (target == LanguageTarget.SHARED) {
87121
return MapUtils.of("regionInfoProvider", writer -> {
88122
writer.addImport("defaultRegionInfoProvider", "defaultRegionInfoProvider",
89123
Paths.get(".", CodegenUtils.SOURCE_FOLDER, "endpoints").toString());
90124
writer.write("defaultRegionInfoProvider");
91125
});
92-
default:
93-
return Collections.emptyMap();
126+
}
94127
}
128+
return Collections.emptyMap();
95129
}
96130
}

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsTraitsUtils.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import software.amazon.smithy.aws.traits.auth.SigV4Trait;
2020
import software.amazon.smithy.model.Model;
2121
import software.amazon.smithy.model.shapes.ServiceShape;
22+
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
2223
import software.amazon.smithy.typescript.codegen.TypeScriptSettings;
2324
import software.amazon.smithy.utils.SmithyInternalApi;
2425

@@ -45,4 +46,8 @@ static boolean isSigV4Service(TypeScriptSettings settings, Model model) {
4546
static boolean isSigV4Service(ServiceShape serviceShape) {
4647
return serviceShape.hasTrait(SigV4Trait.class);
4748
}
49+
50+
static boolean isEndpointsV2Service(ServiceShape serviceShape) {
51+
return serviceShape.hasTrait(EndpointRuleSetTrait.class);
52+
}
4853
}

lib/lib-storage/src/Upload.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,12 @@ export class Upload extends EventEmitter {
122122

123123
const [putResult, endpoint] = await Promise.all([
124124
this.client.send(new PutObjectCommand(params)),
125-
this.client.config.endpoint(),
125+
this.client.config?.endpoint?.(),
126126
]);
127+
if (!endpoint) {
128+
// TODO(endpointsv2): handle endpoint v2
129+
throw new Error();
130+
}
127131

128132
if (eventEmitter !== null) {
129133
eventEmitter.off("xhr.upload.progress", uploadEventListener);

scripts/downlevel-dts/downlevelWorkspace.mjs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import { exec } from "child_process";
33
import { access, readFile, writeFile } from "fs/promises";
44
import { join } from "path";
5-
import prettier from "prettier";
65
import stripComments from "strip-comments";
76
import { promisify } from "util";
87

@@ -40,13 +39,7 @@ export const downlevelWorkspace = async (workspacesDir, workspaceName) => {
4039
for (const downlevelTypesFilepath of files) {
4140
try {
4241
const content = await readFile(downlevelTypesFilepath, "utf8");
43-
const strippedContent = stripComments(content);
44-
try {
45-
const formatted = prettier.format(strippedContent, { parser: "typescript" });
46-
await writeFile(downlevelTypesFilepath, formatted);
47-
} catch (error) {
48-
console.warn(`Failed to format "${downlevelTypesFilepath}". Skipping...`);
49-
}
42+
await writeFile(downlevelTypesFilepath, stripComments(content));
5043
} catch (error) {
5144
console.error(`Error while stripping comments from "${downlevelTypesFilepath.replace(process.cwd(), "")}"`);
5245
console.error(error);

scripts/generate-clients/code-gen.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const generateClients = async (models, batchSize) => {
1717
const options = [
1818
":sdk-codegen:clean",
1919
":sdk-codegen:build",
20+
"--stacktrace",
2021
`-PmodelsDirProp=${relative(CODE_GEN_SDK_ROOT, TEMP_CODE_GEN_INPUT_DIR)}`,
2122
];
2223

0 commit comments

Comments
 (0)