Skip to content

Commit

Permalink
feat(endpoint): endpoints v2 codegen (#3942)
Browse files Browse the repository at this point in the history
* feat(endpoint): endpoints v2 codegen

* feat(endpoint): codegen formatting

* feat(endpoint): formatting
  • Loading branch information
kuhe authored Sep 14, 2022
1 parent a044789 commit 4b32da4
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 108 deletions.
7 changes: 6 additions & 1 deletion codegen/sdk-codegen/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.model.node.Node
import software.amazon.smithy.gradle.tasks.SmithyBuild
import software.amazon.smithy.aws.traits.ServiceTrait
import java.util.stream.Stream
import kotlin.streams.toList

buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
Expand Down Expand Up @@ -62,7 +64,10 @@ tasks.register("generate-smithy-build") {
val model = Model.assembler()
.addImport(file.absolutePath)
.assemble().result.get();
val services = model.shapes(ServiceShape::class.javaObjectType).sorted().toList();
val servicesStream: Stream<ServiceShape> = model.shapes(ServiceShape::class.javaObjectType)
val servicesStreamSorted: Stream<ServiceShape> = servicesStream.sorted()
val services: List<ServiceShape> = servicesStreamSorted.toList()

if (services.size != 1) {
throw Exception("There must be exactly one service in each aws model file, but found " +
"${services.size} in ${file.name}: ${services.map { it.id }}");
Expand Down
1 change: 1 addition & 0 deletions codegen/smithy-aws-typescript-codegen/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies {
api("software.amazon.smithy:smithy-aws-iam-traits:${rootProject.extra["smithyVersion"]}")
api("software.amazon.smithy:smithy-protocol-test-traits:${rootProject.extra["smithyVersion"]}")
api("software.amazon.smithy:smithy-model:${rootProject.extra["smithyVersion"]}")
api("software.amazon.smithy:smithy-rules-engine:${rootProject.extra["smithyVersion"]}")
api("software.amazon.smithy.typescript:smithy-typescript-codegen:0.11.0")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package software.amazon.smithy.aws.typescript.codegen;

import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService;
import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isEndpointsV2Service;
import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isSigV4Service;
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_CONFIG;
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_MIDDLEWARE;
Expand Down Expand Up @@ -46,82 +47,89 @@ public List<RuntimeClientPlugin> getClientPlugins() {
// Note that order is significant because configurations might
// rely on previously resolved values.
return ListUtils.of(
RuntimeClientPlugin.builder()
.withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "Region", HAS_CONFIG)
.servicePredicate((m, s) -> isAwsService(s) || isSigV4Service(s))
.build(),
// Only one of Endpoints or CustomEndpoints should be used
RuntimeClientPlugin.builder()
.withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "Endpoints", HAS_CONFIG)
.servicePredicate((m, s) -> isAwsService(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "CustomEndpoints", HAS_CONFIG)
.servicePredicate((m, s) -> !isAwsService(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(TypeScriptDependency.MIDDLEWARE_RETRY.dependency, "Retry")
.build(),
RuntimeClientPlugin.builder()
.withConventions(TypeScriptDependency.MIDDLEWARE_CONTENT_LENGTH.dependency, "ContentLength",
HAS_MIDDLEWARE)
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.ACCEPT_HEADER.dependency, "AcceptHeader",
HAS_MIDDLEWARE)
.servicePredicate((m, s) -> testServiceId(s, "API Gateway"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.GLACIER_MIDDLEWARE.dependency,
"Glacier", HAS_MIDDLEWARE)
.servicePredicate((m, s) -> testServiceId(s, "Glacier"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.EC2_MIDDLEWARE.dependency,
"CopySnapshotPresignedUrl", HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> o.getId().getName(s).equals("CopySnapshot")
&& testServiceId(s, "EC2"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MACHINELEARNING_MIDDLEWARE.dependency, "PredictEndpoint",
HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> o.getId().getName(s).equals("Predict")
&& testServiceId(s, "Machine Learning"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.ROUTE53_MIDDLEWARE.dependency,
"ChangeResourceRecordSets", HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> o.getId().getName(s).equals("ChangeResourceRecordSets")
&& testServiceId(s, "Route 53"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.ROUTE53_MIDDLEWARE.dependency, "IdNormalizer",
HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> testInputContainsMember(m, o, ROUTE_53_ID_MEMBERS)
&& testServiceId(s, "Route 53"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_HOST_HEADER.dependency, "HostHeader")
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_LOGGER.dependency, "Logger", HAS_MIDDLEWARE)
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.RECURSION_DETECTION_MIDDLEWARE.dependency,
"RecursionDetection", HAS_MIDDLEWARE)
.build()
RuntimeClientPlugin.builder()
.withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "Region", HAS_CONFIG)
.servicePredicate((m, s) -> isAwsService(s) || isSigV4Service(s))
.build(),
// Only one of Endpoints or CustomEndpoints should be used
RuntimeClientPlugin.builder()
.withConventions(
TypeScriptDependency.CONFIG_RESOLVER.dependency, "Endpoints", HAS_CONFIG)
.servicePredicate((m, s) -> isAwsService(s) && !isEndpointsV2Service(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(
TypeScriptDependency.CONFIG_RESOLVER.dependency, "CustomEndpoints", HAS_CONFIG)
.servicePredicate((m, s) -> !isAwsService(s) && !isEndpointsV2Service(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(
TypeScriptDependency.MIDDLEWARE_ENDPOINTS_V2.dependency, "Endpoint", HAS_CONFIG)
.servicePredicate((m, s) -> isAwsService(s) && isEndpointsV2Service(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(TypeScriptDependency.MIDDLEWARE_RETRY.dependency, "Retry")
.build(),
RuntimeClientPlugin.builder()
.withConventions(TypeScriptDependency.MIDDLEWARE_CONTENT_LENGTH.dependency, "ContentLength",
HAS_MIDDLEWARE)
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.ACCEPT_HEADER.dependency, "AcceptHeader",
HAS_MIDDLEWARE)
.servicePredicate((m, s) -> testServiceId(s, "API Gateway"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.GLACIER_MIDDLEWARE.dependency,
"Glacier", HAS_MIDDLEWARE)
.servicePredicate((m, s) -> testServiceId(s, "Glacier"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.EC2_MIDDLEWARE.dependency,
"CopySnapshotPresignedUrl", HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> o.getId().getName(s).equals("CopySnapshot")
&& testServiceId(s, "EC2"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MACHINELEARNING_MIDDLEWARE.dependency, "PredictEndpoint",
HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> o.getId().getName(s).equals("Predict")
&& testServiceId(s, "Machine Learning"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.ROUTE53_MIDDLEWARE.dependency,
"ChangeResourceRecordSets", HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> o.getId().getName(s).equals("ChangeResourceRecordSets")
&& testServiceId(s, "Route 53"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.ROUTE53_MIDDLEWARE.dependency, "IdNormalizer",
HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> testInputContainsMember(m, o, ROUTE_53_ID_MEMBERS)
&& testServiceId(s, "Route 53"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_HOST_HEADER.dependency, "HostHeader")
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_LOGGER.dependency, "Logger", HAS_MIDDLEWARE)
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.RECURSION_DETECTION_MIDDLEWARE.dependency,
"RecursionDetection", HAS_MIDDLEWARE)
.build()
);
}

private static boolean testInputContainsMember(
Model model,
OperationShape operationShape,
Set<String> expectedMemberNames
Model model,
OperationShape operationShape,
Set<String> expectedMemberNames
) {
OperationIndex operationIndex = OperationIndex.of(model);
return operationIndex.getInput(operationShape)
.filter(input -> input.getMemberNames().stream().anyMatch(expectedMemberNames::contains))
.isPresent();
.filter(input -> input.getMemberNames().stream().anyMatch(expectedMemberNames::contains))
.isPresent();
}

private static boolean testServiceId(Shape serviceShape, String expectedId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

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

import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isEndpointsV2Service;
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_CONFIG;
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_MIDDLEWARE;

Expand Down Expand Up @@ -77,7 +78,7 @@ public final class AddS3Config implements TypeScriptIntegration {
@Override
public Model preprocessModel(Model model, TypeScriptSettings settings) {
ServiceShape serviceShape = settings.getService(model);
if (!testServiceId(serviceShape)) {
if (!isS3(serviceShape)) {
return model;
}
Model.Builder modelBuilder = model.toBuilder();
Expand Down Expand Up @@ -106,9 +107,14 @@ public Model preprocessModel(Model model, TypeScriptSettings settings) {
}

@Override
public void addConfigInterfaceFields(TypeScriptSettings settings, Model model, SymbolProvider symbolProvider,
TypeScriptWriter writer) {
if (!testServiceId(settings.getService(model))) {
public void addConfigInterfaceFields(
TypeScriptSettings settings,
Model model,
SymbolProvider symbolProvider,
TypeScriptWriter writer
) {
ServiceShape service = settings.getService(model);
if (!isS3(service)) {
return;
}
writer.writeDocs("Whether to escape request path when signing the request.")
Expand All @@ -123,7 +129,7 @@ public void addConfigInterfaceFields(TypeScriptSettings settings, Model model, S
@Override
public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(TypeScriptSettings settings, Model model,
SymbolProvider symbolProvider, LanguageTarget target) {
if (!testServiceId(settings.getService(model))) {
if (!isS3(settings.getService(model))) {
return Collections.emptyMap();
}
switch (target) {
Expand Down Expand Up @@ -158,62 +164,68 @@ public List<RuntimeClientPlugin> getClientPlugins() {
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "ValidateBucketName",
HAS_MIDDLEWARE)
.servicePredicate((m, s) -> testServiceId(s))
.servicePredicate((m, s) -> isS3(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "CheckContentLengthHeader",
HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> testServiceId(s) && o.getId().getName(s).equals("PutObject"))
.operationPredicate((m, s, o) -> isS3(s) && o.getId().getName(s).equals("PutObject"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "throw200Exceptions",
HAS_MIDDLEWARE)
.operationPredicate(
(m, s, o) -> EXCEPTIONS_OF_200_OPERATIONS.contains(o.getId().getName(s))
&& testServiceId(s))
.build(),
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "throw200Exceptions",
HAS_MIDDLEWARE)
.operationPredicate(
(m, s, o) -> EXCEPTIONS_OF_200_OPERATIONS.contains(o.getId().getName(s))
&& isS3(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency,
"WriteGetObjectResponseEndpoint", HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> testServiceId(s)
&& o.getId().getName(s).equals("WriteGetObjectResponse"))
.build(),
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency,
"WriteGetObjectResponseEndpoint", HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> isS3(s)
&& o.getId().getName(s).equals("WriteGetObjectResponse"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.ADD_EXPECT_CONTINUE.dependency, "AddExpectContinue",
HAS_MIDDLEWARE)
.servicePredicate((m, s) -> testServiceId(s))
.servicePredicate((m, s) -> isS3(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.SSEC_MIDDLEWARE.dependency, "Ssec", HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> testInputContainsMember(m, o, SSEC_INPUT_KEYS)
&& testServiceId(s))
.operationPredicate((m, s, o) -> containsInputMembers(m, o, SSEC_INPUT_KEYS)
&& isS3(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.LOCATION_CONSTRAINT.dependency, "LocationConstraint",
HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> o.getId().getName(s).equals("CreateBucket")
&& testServiceId(s))
&& isS3(s))
.build(),
/**
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "S3",
HAS_CONFIG)
.servicePredicate((m, s) -> isS3(s) && isEndpointsV2Service(s))
.build(),
/*
* BUCKET_ENDPOINT_MIDDLEWARE needs two separate plugins. The first resolves the config in the client.
* The second applies the middleware to bucket endpoint operations.
*/
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.BUCKET_ENDPOINT_MIDDLEWARE.dependency, "BucketEndpoint",
HAS_CONFIG)
.servicePredicate((m, s) -> testServiceId(s))
.servicePredicate((m, s) -> isS3(s) && !isEndpointsV2Service(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.BUCKET_ENDPOINT_MIDDLEWARE.dependency, "BucketEndpoint",
HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> !NON_BUCKET_ENDPOINT_OPERATIONS.contains(o.getId().getName(s))
&& testServiceId(s)
&& testInputContainsMember(m, o, BUCKET_ENDPOINT_INPUT_KEYS))
&& isS3(s)
&& !isEndpointsV2Service(s)
&& containsInputMembers(m, o, BUCKET_ENDPOINT_INPUT_KEYS))
.build()
);
}

private static boolean testInputContainsMember(
private static boolean containsInputMembers(
Model model,
OperationShape operationShape,
Set<String> expectedMemberNames
Expand All @@ -224,7 +236,7 @@ private static boolean testInputContainsMember(
.isPresent();
}

private static boolean testServiceId(Shape serviceShape) {
private static boolean isS3(Shape serviceShape) {
return serviceShape.getTrait(ServiceTrait.class).map(ServiceTrait::getSdkId).orElse("").equals("S3");
}
}
Loading

0 comments on commit 4b32da4

Please sign in to comment.