Skip to content
Merged
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,127 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
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.integrations.PythonIntegration;
import software.amazon.smithy.python.codegen.integrations.RuntimeClientPlugin;
import software.amazon.smithy.python.codegen.sections.EndpointParametersSection;
import software.amazon.smithy.python.codegen.sections.EndpointResolverSection;
import software.amazon.smithy.python.codegen.sections.InitDefaultEndpointResolverSection;
import software.amazon.smithy.python.codegen.writer.PythonWriter;
import software.amazon.smithy.utils.CodeInterceptor;
import software.amazon.smithy.utils.CodeSection;

public class AwsStandardRegionalEndpointsIntegration implements PythonIntegration {
@Override
public List<RuntimeClientPlugin> getClientPlugins(GenerationContext context) {
if (context.applicationProtocol().isHttpProtocol()) {
var region = 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();
return List.of(
RuntimeClientPlugin.builder()
.addConfigProperty(region)
.build());
} else {
return List.of();
}
}

@Override
public List<? extends CodeInterceptor<? extends CodeSection, PythonWriter>> interceptors(
GenerationContext context
) {
return List.of(
new RegionalEndpointParametersInterceptor(context),
new RegionalEndpointResolverInterceptor(context),
new RegionalInitEndpointResolverInterceptor(context));
}

private static final class RegionalEndpointParametersInterceptor
implements CodeInterceptor<EndpointParametersSection, PythonWriter> {

private final GenerationContext context;

public RegionalEndpointParametersInterceptor(GenerationContext context) {
this.context = context;
}

@Override
public Class<EndpointParametersSection> sectionType() {
return EndpointParametersSection.class;
}

@Override
public void write(PythonWriter writer, String previousText, EndpointParametersSection section) {
var params = CodegenUtils.getEndpointParametersSymbol(context.settings());

writer.write("from smithy_aws_core.endpoints.standard_regional import RegionalEndpointParameters");
writer.write("$L = RegionalEndpointParameters", params.getName());
}
}

private static final class RegionalEndpointResolverInterceptor
implements CodeInterceptor<EndpointResolverSection, PythonWriter> {

private final GenerationContext context;

public RegionalEndpointResolverInterceptor(GenerationContext context) {
this.context = context;
}

@Override
public Class<EndpointResolverSection> sectionType() {
return EndpointResolverSection.class;
}

@Override
public void write(PythonWriter writer, String previousText, EndpointResolverSection section) {
var resolver = CodegenUtils.getEndpointResolverSymbol(context.settings());

writer.write("from smithy_aws_core.endpoints.standard_regional import StandardRegionalEndpointsResolver");
writer.write("$L = StandardRegionalEndpointsResolver", resolver.getName());
}
}

private static final class RegionalInitEndpointResolverInterceptor
implements CodeInterceptor<InitDefaultEndpointResolverSection, PythonWriter> {

private final GenerationContext context;

public RegionalInitEndpointResolverInterceptor(GenerationContext context) {
this.context = context;
}

@Override
public Class<InitDefaultEndpointResolverSection> sectionType() {
return InitDefaultEndpointResolverSection.class;
}

@Override
public void write(PythonWriter writer, String previousText, InitDefaultEndpointResolverSection section) {
var resolver = CodegenUtils.getEndpointResolverSymbol(context.settings());

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 $T(endpoint_prefix=$S)",
resolver,
endpointPrefix);

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
software.amazon.smithy.python.aws.codegen.AwsAuthIntegration
software.amazon.smithy.python.aws.codegen.AwsProtocolsIntegration
software.amazon.smithy.python.aws.codegen.AwsUserAgentIntegration
software.amazon.smithy.python.aws.codegen.AwsStandardRegionalEndpointsIntegration
Original file line number Diff line number Diff line change
Expand Up @@ -443,16 +443,10 @@ async def _handle_attempt(
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."
)
endpoint_resolver_parameters = StaticEndpointParams(uri=config.endpoint_uri)
endpoint_resolver_parameters = $1T.build(config=config)
logger.debug("Calling endpoint resolver with parameters: %s", endpoint_resolver_parameters)
endpoint = await config.endpoint_resolver.resolve_endpoint(
endpoint_resolver_parameters
Expand All @@ -475,7 +469,8 @@ async def _handle_attempt(
)
context._transport_request.fields.extend(endpoint.headers)

""", errorSymbol);
""",
CodegenUtils.getEndpointParametersSymbol(context.settings()));
}
writer.popState();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,38 @@ public static Symbol getUnknownApiError(PythonSettings settings) {
}

/**
* Gets the symbol for the http auth parameters object.
* Gets the symbol for the endpoint parameters object.
*
* @param settings The client settings, used to account for module configuration.
* @return Returns the symbol for http auth params.
* @return Returns the symbol for endpoint parameters.
*/
public static Symbol getEndpointParametersSymbol(PythonSettings settings) {
return Symbol.builder()
.name("EndpointParameters")
.namespace(String.format("%s.endpoints", settings.moduleName()), ".")
.definitionFile(String.format("./%s/endpoints.py", settings.moduleName()))
.build();
}

/**
* Gets the symbol for the endpoint resolver object.
*
* @param settings The client settings, used to account for module configuration.
* @return Returns the symbol for endpoint resolver.
*/
public static Symbol getEndpointResolverSymbol(PythonSettings settings) {
return Symbol.builder()
.name("EndpointResolver")
.namespace(String.format("%s.endpoints", settings.moduleName()), ".")
.definitionFile(String.format("./%s/endpoints.py", settings.moduleName()))
.build();
}

/**
* Gets the symbol for the http auth params.
*
* @param settings The client settings, used to account for module configuration.
* @return Returns the http auth params symbol.
*/
public static Symbol getHttpAuthParamsSymbol(PythonSettings settings) {
return Symbol.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.python.codegen.generators.ConfigGenerator;
import software.amazon.smithy.python.codegen.generators.EndpointsGenerator;
import software.amazon.smithy.python.codegen.generators.EnumGenerator;
import software.amazon.smithy.python.codegen.generators.InitGenerator;
import software.amazon.smithy.python.codegen.generators.IntEnumGenerator;
Expand Down Expand Up @@ -110,6 +111,7 @@ public void customizeBeforeShapeGeneration(CustomizeDirective<GenerationContext,
if (directive.context().applicationProtocol().isHttpProtocol()
&& !serviceIndex.getAuthSchemes(directive.service()).isEmpty()) {
new HttpAuthGenerator(directive.context(), directive.settings()).run();
new EndpointsGenerator(directive.context(), directive.settings()).run();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import software.amazon.smithy.python.codegen.integrations.PythonIntegration;
import software.amazon.smithy.python.codegen.integrations.RuntimeClientPlugin;
import software.amazon.smithy.python.codegen.sections.ConfigSection;
import software.amazon.smithy.python.codegen.sections.InitDefaultEndpointResolverSection;
import software.amazon.smithy.python.codegen.writer.PythonWriter;
import software.amazon.smithy.utils.CodeInterceptor;
import software.amazon.smithy.utils.SmithyInternalApi;
Expand Down Expand Up @@ -78,30 +79,6 @@ public final class ConfigGenerator implements Runnable {
.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()
Expand All @@ -124,7 +101,7 @@ public ConfigGenerator(PythonSettings settings, GenerationContext context) {
}

private static List<ConfigProperty> getHttpProperties(GenerationContext context) {
var properties = new ArrayList<ConfigProperty>(HTTP_PROPERTIES.size() + 1);
var properties = new ArrayList<ConfigProperty>(HTTP_PROPERTIES.size() + 2);
var clientBuilder = ConfigProperty.builder()
.name("http_client")
.type(Symbol.builder()
Expand Down Expand Up @@ -155,6 +132,25 @@ private static List<ConfigProperty> getHttpProperties(GenerationContext context)
}
properties.add(clientBuilder.build());

var endpointResolver = CodegenUtils.getEndpointResolverSymbol(context.settings());
properties.add(ConfigProperty.builder()
.name("endpoint_resolver")
.type(Symbol.builder()
.name("_EndpointResolver[EndpointParameters]")
.addReference(CodegenUtils.getEndpointParametersSymbol(context.settings()))
.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.interfaces", "EndpointResolver", "_EndpointResolver");
writer.pushState(new InitDefaultEndpointResolverSection());
writer.write("self.endpoint_resolver = endpoint_resolver or $T()", endpointResolver);
writer.popState();
})
.build());

properties.addAll(HTTP_PROPERTIES);
return List.copyOf(properties);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package software.amazon.smithy.python.codegen.generators;

import software.amazon.smithy.codegen.core.Symbol;
import software.amazon.smithy.python.codegen.CodegenUtils;
import software.amazon.smithy.python.codegen.GenerationContext;
import software.amazon.smithy.python.codegen.PythonSettings;
import software.amazon.smithy.python.codegen.SmithyPythonDependency;
import software.amazon.smithy.python.codegen.sections.EndpointParametersSection;
import software.amazon.smithy.python.codegen.sections.EndpointResolverSection;
import software.amazon.smithy.python.codegen.writer.PythonWriter;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
* This class is responsible for generating the endpoint resolver and its parameters.
*/
@SmithyInternalApi
public final class EndpointsGenerator implements Runnable {

private final PythonSettings settings;
private final GenerationContext context;

public EndpointsGenerator(GenerationContext context, PythonSettings settings) {
this.context = context;
this.settings = settings;
}

@Override
public void run() {
var params = CodegenUtils.getEndpointParametersSymbol(settings);
context.writerDelegator().useFileWriter(params.getDefinitionFile(), params.getNamespace(), writer -> {
generateEndpointParameters(writer, params);
});

var resolver = CodegenUtils.getEndpointResolverSymbol(settings);
context.writerDelegator().useFileWriter(resolver.getDefinitionFile(), resolver.getNamespace(), writer -> {
generateEndpointResolver(writer, resolver);
});
}

private void generateEndpointParameters(PythonWriter writer, Symbol params) {
writer.pushState(new EndpointParametersSection());
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
writer.write("from smithy_http.endpoints import StaticEndpointParams");
writer.write("$L = StaticEndpointParams", params.getName());
writer.popState();
}

private void generateEndpointResolver(PythonWriter writer, Symbol resolver) {
writer.pushState(new EndpointResolverSection());
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
writer.write("from smithy_http.aio.endpoints import StaticEndpointResolver");
writer.write("$L = StaticEndpointResolver", resolver.getName());
writer.popState();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package software.amazon.smithy.python.codegen.sections;

import software.amazon.smithy.utils.CodeSection;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
* A section that controls generating the EndpointParameters class.
*/
@SmithyInternalApi
public record EndpointParametersSection() implements CodeSection {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package software.amazon.smithy.python.codegen.sections;

import software.amazon.smithy.utils.CodeSection;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
* A section that controls generating the EndpointResolver class.
*/
@SmithyInternalApi
public record EndpointResolverSection() implements CodeSection {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package software.amazon.smithy.python.codegen.sections;

import software.amazon.smithy.utils.CodeSection;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
* A section that controls generating the EndpointParameters class.
*/
@SmithyInternalApi
public record InitDefaultEndpointResolverSection() implements CodeSection {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
Loading
Loading