Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Draft] Generate Regional endpoints #394

Draft
wants to merge 6 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package software.amazon.smithy.python.aws.codegen;

import software.amazon.smithy.python.codegen.PythonDependency;
import software.amazon.smithy.utils.SmithyUnstableApi;

/**
* AWS Dependencies used in the smithy python generator.
*/
@SmithyUnstableApi
public class AwsPythonDependency {
/**
* The core aws smithy runtime python package.
*
* <p>While in development this will use the develop branch.
*/
public static final PythonDependency SMITHY_AWS_CORE = new PythonDependency(
"smithy_aws_core",
// You'll need to locally install this before we publish
"==0.0.1",
PythonDependency.Type.DEPENDENCY,
false);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package software.amazon.smithy.python.aws.codegen;

import java.util.List;
import software.amazon.smithy.aws.traits.ServiceTrait;
import software.amazon.smithy.codegen.core.Symbol;
import software.amazon.smithy.python.codegen.CodegenUtils;
import software.amazon.smithy.python.codegen.ConfigProperty;
import software.amazon.smithy.python.codegen.GenerationContext;
import software.amazon.smithy.python.codegen.SmithyPythonDependency;
import software.amazon.smithy.python.codegen.integrations.EndpointsGenerator;
import software.amazon.smithy.python.codegen.writer.PythonWriter;

/**
* Generates endpoint config and resolution logic for standard regional endpoints.
*/
public class AwsRegionalEndpointsGenerator implements EndpointsGenerator {
@Override
public List<ConfigProperty> endpointsConfig(GenerationContext context) {
return List.of(ConfigProperty.builder()
.name("endpoint_resolver")
.type(Symbol.builder()
.name("EndpointResolver[RegionalEndpointParameters]")
.addReference(Symbol.builder()
.name("RegionalEndpointParameters")
.namespace(AwsPythonDependency.SMITHY_AWS_CORE.packageName() + ".endpoints.standard_regional", ".")
.addDependency(AwsPythonDependency.SMITHY_AWS_CORE)
.build())
.addReference(Symbol.builder()
.name("EndpointResolver")
.namespace("smithy_http.aio.interfaces", ".")
.addDependency(SmithyPythonDependency.SMITHY_HTTP)
.build())
.build())
.documentation("""
The endpoint resolver used to resolve the final endpoint per-operation based on the \
configuration.""")
.nullable(false)
.initialize(writer -> {
writer.addImport("smithy_aws_core.endpoints.standard_regional", "StandardRegionalEndpointsResolver");
String endpointPrefix = context.settings().service(context.model())
.getTrait(ServiceTrait.class)
.map(ServiceTrait::getEndpointPrefix)
.orElse(context.settings().service().getName());

writer.write(
"self.endpoint_resolver = endpoint_resolver or StandardRegionalEndpointsResolver(endpoint_prefix='$L')",
endpointPrefix);
})
.build(),
ConfigProperty.builder()
.name("endpoint_uri")
.type(Symbol.builder()
.name("str | URI")
.addReference(Symbol.builder()
.name("URI")
.namespace("smithy_core.interfaces", ".")
.addDependency(SmithyPythonDependency.SMITHY_CORE)
.build())
.build())
.documentation("A static URI to route requests to.")
.build(),
ConfigProperty.builder()
.name("region")
.type(Symbol.builder().name("str").build())
.documentation(" The AWS region to connect to. The configured region is used to "
+ "determine the service endpoint.")
.build());
}

@Override
public void renderEndpointParameterConstruction(GenerationContext context, PythonWriter writer) {

writer.addDependency(AwsPythonDependency.SMITHY_AWS_CORE);
writer.addImport("smithy_aws_core.endpoints.standard_regional", "RegionalEndpointParameters");
writer.write("""
endpoint_parameters = RegionalEndpointParameters(
sdk_endpoint=config.endpoint_uri,
region=config.region
)
""");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package software.amazon.smithy.python.aws.codegen;

import java.util.Optional;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.python.codegen.integrations.EndpointsGenerator;
import software.amazon.smithy.python.codegen.integrations.PythonIntegration;

public class AwsRegionalEndpointsIntegration implements PythonIntegration {
@Override
public Optional<EndpointsGenerator> getEndpointsGenerator(Model model, ServiceShape service) {
return Optional.of(new AwsRegionalEndpointsGenerator());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@

software.amazon.smithy.python.aws.codegen.AwsAuthIntegration
software.amazon.smithy.python.aws.codegen.AwsProtocolsIntegration
software.amazon.smithy.python.aws.codegen.AwsRegionalEndpointsIntegration
Original file line number Diff line number Diff line change
Expand Up @@ -433,20 +433,13 @@ async def _handle_attempt(
writer.pushState(new ResolveEndpointSection());
if (context.applicationProtocol().isHttpProtocol()) {
writer.addDependency(SmithyPythonDependency.SMITHY_CORE);
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
// TODO: implement the endpoints 2.0 spec and remove the hard-coded handling of static params.
writer.addImport("smithy_http.endpoints", "StaticEndpointParams");
writer.addImport("smithy_core", "URI");
writer.write("""
# Step 7f: Invoke endpoint_resolver.resolve_endpoint
if config.endpoint_uri is None:
raise $1T(
"No endpoint_uri found on the operation config."
)
writer.write("# Step 7f: Invoke endpoint_resolver.resolve_endpoint");

endpoint = await config.endpoint_resolver.resolve_endpoint(
StaticEndpointParams(uri=config.endpoint_uri)
)
context.endpointsGenerator().renderEndpointParameterConstruction(context, writer);

writer.write("""
endpoint = await config.endpoint_resolver.resolve_endpoint(endpoint_parameters)
if not endpoint.uri.path:
path = ""
elif endpoint.uri.path.endswith("/"):
Expand All @@ -464,7 +457,7 @@ async def _handle_attempt(
)
context._transport_request.fields.extend(endpoint.headers)

""", errorSymbol);
""");
}
writer.popState();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@
import software.amazon.smithy.python.codegen.generators.ProtocolGenerator;
import software.amazon.smithy.python.codegen.generators.SchemaGenerator;
import software.amazon.smithy.python.codegen.generators.SetupGenerator;
import software.amazon.smithy.python.codegen.generators.StaticEndpointsGenerator;
import software.amazon.smithy.python.codegen.generators.StructureGenerator;
import software.amazon.smithy.python.codegen.generators.UnionGenerator;
import software.amazon.smithy.python.codegen.integrations.EndpointsGenerator;
import software.amazon.smithy.python.codegen.integrations.PythonIntegration;
import software.amazon.smithy.python.codegen.writer.PythonDelegator;
import software.amazon.smithy.python.codegen.writer.PythonWriter;
Expand Down Expand Up @@ -81,9 +83,20 @@ public GenerationContext createContext(CreateContextDirective<PythonSettings, Py
directive.integrations(),
directive.model(),
directive.service()))
.endpointsGenerator(resolveEndpointsGenerator(directive))
.build();
}

private EndpointsGenerator resolveEndpointsGenerator(CreateContextDirective<PythonSettings, PythonIntegration> directive) {
for (PythonIntegration integration : directive.integrations()) {
Optional<EndpointsGenerator> generator = integration.getEndpointsGenerator(directive.model(), directive.service());
if (generator.isPresent()) {
return generator.get();
}
}
return new StaticEndpointsGenerator();
}

private ProtocolGenerator resolveProtocolGenerator(
Collection<PythonIntegration> integrations,
Model model,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import software.amazon.smithy.codegen.core.WriterDelegator;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.python.codegen.generators.ProtocolGenerator;
import software.amazon.smithy.python.codegen.integrations.EndpointsGenerator;
import software.amazon.smithy.python.codegen.integrations.PythonIntegration;
import software.amazon.smithy.python.codegen.writer.PythonDelegator;
import software.amazon.smithy.python.codegen.writer.PythonWriter;
Expand All @@ -32,6 +33,7 @@ public final class GenerationContext
private final PythonDelegator delegator;
private final List<PythonIntegration> integrations;
private final ProtocolGenerator protocolGenerator;
private final EndpointsGenerator endpointsGenerator;

private GenerationContext(Builder builder) {
model = builder.model;
Expand All @@ -41,6 +43,7 @@ private GenerationContext(Builder builder) {
delegator = builder.delegator;
integrations = builder.integrations;
protocolGenerator = builder.protocolGenerator;
endpointsGenerator = builder.endpointsGenerator;
}

@Override
Expand Down Expand Up @@ -80,6 +83,13 @@ public ProtocolGenerator protocolGenerator() {
return protocolGenerator;
}

/**
* @return Returns the endpoints generator to use in code generation.
*/
public EndpointsGenerator endpointsGenerator () {
return endpointsGenerator;
}

/**
* Gets the application protocol for the service protocol.
*
Expand All @@ -105,7 +115,8 @@ public SmithyBuilder<GenerationContext> toBuilder() {
.settings(settings)
.symbolProvider(symbolProvider)
.fileManifest(fileManifest)
.writerDelegator(delegator);
.writerDelegator(delegator)
.endpointsGenerator(endpointsGenerator);
}

/**
Expand All @@ -119,6 +130,7 @@ public static final class Builder implements SmithyBuilder<GenerationContext> {
private PythonDelegator delegator;
private List<PythonIntegration> integrations;
private ProtocolGenerator protocolGenerator;
private EndpointsGenerator endpointsGenerator;

@Override
public GenerationContext build() {
Expand Down Expand Up @@ -187,5 +199,14 @@ public Builder protocolGenerator(ProtocolGenerator protocolGenerator) {
this.protocolGenerator = protocolGenerator;
return this;
}

/**
* @param endpointsGenerator The resolved endpoints generator to use.
* @return Returns the builder.
*/
public Builder endpointsGenerator(EndpointsGenerator endpointsGenerator) {
this.endpointsGenerator = endpointsGenerator;
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public final class ConfigGenerator implements Runnable {

// This list contains any properties that must be added to any http-based
// service client, except for the http client itself.
private static final List<ConfigProperty> HTTP_PROPERTIES = Arrays.asList(
private static final List<ConfigProperty> HTTP_PROPERTIES = List.of(
ConfigProperty.builder()
.name("http_request_config")
.type(Symbol.builder()
Expand All @@ -77,42 +77,6 @@ public final class ConfigGenerator implements Runnable {
.addDependency(SmithyPythonDependency.SMITHY_HTTP)
.build())
.documentation("Configuration for individual HTTP requests.")
.build(),
ConfigProperty.builder()
.name("endpoint_resolver")
.type(Symbol.builder()
.name("EndpointResolver[Any]")
.addReference(Symbol.builder()
.name("Any")
.namespace("typing", ".")
.putProperty(SymbolProperties.STDLIB, true)
.build())
.addReference(Symbol.builder()
.name("EndpointResolver")
.namespace("smithy_http.aio.interfaces", ".")
.addDependency(SmithyPythonDependency.SMITHY_HTTP)
.build())
.build())
.documentation("""
The endpoint resolver used to resolve the final endpoint per-operation based on the \
configuration.""")
.nullable(false)
.initialize(writer -> {
writer.addImport("smithy_http.aio.endpoints", "StaticEndpointResolver");
writer.write("self.endpoint_resolver = endpoint_resolver or StaticEndpointResolver()");
})
.build(),
ConfigProperty.builder()
.name("endpoint_uri")
.type(Symbol.builder()
.name("str | URI")
.addReference(Symbol.builder()
.name("URI")
.namespace("smithy_core.interfaces", ".")
.addDependency(SmithyPythonDependency.SMITHY_CORE)
.build())
.build())
.documentation("A static URI to route requests to.")
.build());

private final PythonSettings settings;
Expand Down Expand Up @@ -154,6 +118,7 @@ private static List<ConfigProperty> getHttpProperties(GenerationContext context)
properties.add(clientBuilder.build());

properties.addAll(HTTP_PROPERTIES);
properties.addAll(context.endpointsGenerator().endpointsConfig(context));
return List.copyOf(properties);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package software.amazon.smithy.python.codegen.generators;

import java.util.List;
import software.amazon.smithy.codegen.core.Symbol;
import software.amazon.smithy.python.codegen.ConfigProperty;
import software.amazon.smithy.python.codegen.GenerationContext;
import software.amazon.smithy.python.codegen.SmithyPythonDependency;
import software.amazon.smithy.python.codegen.SymbolProperties;
import software.amazon.smithy.python.codegen.integrations.EndpointsGenerator;
import software.amazon.smithy.python.codegen.writer.PythonWriter;

public class StaticEndpointsGenerator implements EndpointsGenerator {
@Override
public List<ConfigProperty> endpointsConfig(GenerationContext context) {
return List.of(ConfigProperty.builder()
.name("endpoint_resolver")
.type(Symbol.builder()
.name("EndpointResolver[Any]")
.addReference(Symbol.builder()
.name("Any")
.namespace("typing", ".")
.putProperty(SymbolProperties.STDLIB, true)
.build())
.addReference(Symbol.builder()
.name("EndpointResolver")
.namespace("smithy_http.aio.interfaces", ".")
.addDependency(SmithyPythonDependency.SMITHY_HTTP)
.build())
.build())
.documentation("""
The endpoint resolver used to resolve the final endpoint per-operation based on the \
configuration.""")
.nullable(false)
.initialize(writer -> {
writer.addImport("smithy_http.aio.endpoints", "StaticEndpointResolver");
writer.write("self.endpoint_resolver = endpoint_resolver or StaticEndpointResolver()");
})
.build(),
ConfigProperty.builder()
.name("endpoint_uri")
.type(Symbol.builder()
.name("str | URI")
.addReference(Symbol.builder()
.name("URI")
.namespace("smithy_core.interfaces", ".")
.addDependency(SmithyPythonDependency.SMITHY_CORE)
.build())
.build())
.documentation("A static URI to route requests to.")
.build());
}

@Override
public void renderEndpointParameterConstruction(GenerationContext context, PythonWriter writer) {
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
writer.addImport("smithy_http.endpoints", "StaticEndpointParams");
writer.write("""
endpoint_parameters = StaticEndpointParams(
uri=config.endpoint_uri
)
""");
}
}
Loading
Loading