Skip to content

Commit 2d86175

Browse files
authored
Generate Standard Regional Endpoints for AWS services
1 parent c6fb91e commit 2d86175

File tree

17 files changed

+467
-50
lines changed

17 files changed

+467
-50
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package software.amazon.smithy.python.aws.codegen;
6+
7+
import java.util.List;
8+
import software.amazon.smithy.aws.traits.ServiceTrait;
9+
import software.amazon.smithy.codegen.core.Symbol;
10+
import software.amazon.smithy.python.codegen.CodegenUtils;
11+
import software.amazon.smithy.python.codegen.ConfigProperty;
12+
import software.amazon.smithy.python.codegen.GenerationContext;
13+
import software.amazon.smithy.python.codegen.integrations.PythonIntegration;
14+
import software.amazon.smithy.python.codegen.integrations.RuntimeClientPlugin;
15+
import software.amazon.smithy.python.codegen.sections.EndpointParametersSection;
16+
import software.amazon.smithy.python.codegen.sections.EndpointResolverSection;
17+
import software.amazon.smithy.python.codegen.sections.InitDefaultEndpointResolverSection;
18+
import software.amazon.smithy.python.codegen.writer.PythonWriter;
19+
import software.amazon.smithy.utils.CodeInterceptor;
20+
import software.amazon.smithy.utils.CodeSection;
21+
22+
public class AwsStandardRegionalEndpointsIntegration implements PythonIntegration {
23+
@Override
24+
public List<RuntimeClientPlugin> getClientPlugins(GenerationContext context) {
25+
if (context.applicationProtocol().isHttpProtocol()) {
26+
var region = ConfigProperty.builder()
27+
.name("region")
28+
.type(Symbol.builder().name("str").build())
29+
.documentation(" The AWS region to connect to. The configured region is used to "
30+
+ "determine the service endpoint.")
31+
.build();
32+
return List.of(
33+
RuntimeClientPlugin.builder()
34+
.addConfigProperty(region)
35+
.build());
36+
} else {
37+
return List.of();
38+
}
39+
}
40+
41+
@Override
42+
public List<? extends CodeInterceptor<? extends CodeSection, PythonWriter>> interceptors(
43+
GenerationContext context
44+
) {
45+
return List.of(
46+
new RegionalEndpointParametersInterceptor(context),
47+
new RegionalEndpointResolverInterceptor(context),
48+
new RegionalInitEndpointResolverInterceptor(context));
49+
}
50+
51+
private static final class RegionalEndpointParametersInterceptor
52+
implements CodeInterceptor<EndpointParametersSection, PythonWriter> {
53+
54+
private final GenerationContext context;
55+
56+
public RegionalEndpointParametersInterceptor(GenerationContext context) {
57+
this.context = context;
58+
}
59+
60+
@Override
61+
public Class<EndpointParametersSection> sectionType() {
62+
return EndpointParametersSection.class;
63+
}
64+
65+
@Override
66+
public void write(PythonWriter writer, String previousText, EndpointParametersSection section) {
67+
var params = CodegenUtils.getEndpointParametersSymbol(context.settings());
68+
69+
writer.write("from smithy_aws_core.endpoints.standard_regional import RegionalEndpointParameters");
70+
writer.write("$L = RegionalEndpointParameters", params.getName());
71+
}
72+
}
73+
74+
private static final class RegionalEndpointResolverInterceptor
75+
implements CodeInterceptor<EndpointResolverSection, PythonWriter> {
76+
77+
private final GenerationContext context;
78+
79+
public RegionalEndpointResolverInterceptor(GenerationContext context) {
80+
this.context = context;
81+
}
82+
83+
@Override
84+
public Class<EndpointResolverSection> sectionType() {
85+
return EndpointResolverSection.class;
86+
}
87+
88+
@Override
89+
public void write(PythonWriter writer, String previousText, EndpointResolverSection section) {
90+
var resolver = CodegenUtils.getEndpointResolverSymbol(context.settings());
91+
92+
writer.write("from smithy_aws_core.endpoints.standard_regional import StandardRegionalEndpointsResolver");
93+
writer.write("$L = StandardRegionalEndpointsResolver", resolver.getName());
94+
}
95+
}
96+
97+
private static final class RegionalInitEndpointResolverInterceptor
98+
implements CodeInterceptor<InitDefaultEndpointResolverSection, PythonWriter> {
99+
100+
private final GenerationContext context;
101+
102+
public RegionalInitEndpointResolverInterceptor(GenerationContext context) {
103+
this.context = context;
104+
}
105+
106+
@Override
107+
public Class<InitDefaultEndpointResolverSection> sectionType() {
108+
return InitDefaultEndpointResolverSection.class;
109+
}
110+
111+
@Override
112+
public void write(PythonWriter writer, String previousText, InitDefaultEndpointResolverSection section) {
113+
var resolver = CodegenUtils.getEndpointResolverSymbol(context.settings());
114+
115+
String endpointPrefix = context.settings()
116+
.service(context.model())
117+
.getTrait(ServiceTrait.class)
118+
.map(ServiceTrait::getEndpointPrefix)
119+
.orElse(context.settings().service().getName());
120+
121+
writer.write("self.endpoint_resolver = endpoint_resolver or $T(endpoint_prefix=$S)",
122+
resolver,
123+
endpointPrefix);
124+
125+
}
126+
}
127+
}

codegen/aws/core/src/main/resources/META-INF/services/software.amazon.smithy.python.codegen.integrations.PythonIntegration

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
software.amazon.smithy.python.aws.codegen.AwsAuthIntegration
77
software.amazon.smithy.python.aws.codegen.AwsProtocolsIntegration
88
software.amazon.smithy.python.aws.codegen.AwsUserAgentIntegration
9+
software.amazon.smithy.python.aws.codegen.AwsStandardRegionalEndpointsIntegration

codegen/core/src/main/java/software/amazon/smithy/python/codegen/ClientGenerator.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -444,16 +444,10 @@ async def _handle_attempt(
444444
if (context.applicationProtocol().isHttpProtocol()) {
445445
writer.addDependency(SmithyPythonDependency.SMITHY_CORE);
446446
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
447-
// TODO: implement the endpoints 2.0 spec and remove the hard-coded handling of static params.
448-
writer.addImport("smithy_http.endpoints", "StaticEndpointParams");
449447
writer.addImport("smithy_core", "URI");
450448
writer.write("""
451449
# Step 7f: Invoke endpoint_resolver.resolve_endpoint
452-
if config.endpoint_uri is None:
453-
raise $1T(
454-
"No endpoint_uri found on the operation config."
455-
)
456-
endpoint_resolver_parameters = StaticEndpointParams(uri=config.endpoint_uri)
450+
endpoint_resolver_parameters = $1T.build(config=config)
457451
logger.debug("Calling endpoint resolver with parameters: %s", endpoint_resolver_parameters)
458452
endpoint = await config.endpoint_resolver.resolve_endpoint(
459453
endpoint_resolver_parameters
@@ -476,7 +470,8 @@ async def _handle_attempt(
476470
)
477471
context._transport_request.fields.extend(endpoint.headers)
478472
479-
""", errorSymbol);
473+
""",
474+
CodegenUtils.getEndpointParametersSymbol(context.settings()));
480475
}
481476
writer.popState();
482477

codegen/core/src/main/java/software/amazon/smithy/python/codegen/CodegenUtils.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,38 @@ public static Symbol getUnknownApiError(PythonSettings settings) {
140140
}
141141

142142
/**
143-
* Gets the symbol for the http auth parameters object.
143+
* Gets the symbol for the endpoint parameters object.
144144
*
145145
* @param settings The client settings, used to account for module configuration.
146-
* @return Returns the symbol for http auth params.
146+
* @return Returns the symbol for endpoint parameters.
147+
*/
148+
public static Symbol getEndpointParametersSymbol(PythonSettings settings) {
149+
return Symbol.builder()
150+
.name("EndpointParameters")
151+
.namespace(String.format("%s.endpoints", settings.moduleName()), ".")
152+
.definitionFile(String.format("./%s/endpoints.py", settings.moduleName()))
153+
.build();
154+
}
155+
156+
/**
157+
* Gets the symbol for the endpoint resolver object.
158+
*
159+
* @param settings The client settings, used to account for module configuration.
160+
* @return Returns the symbol for endpoint resolver.
161+
*/
162+
public static Symbol getEndpointResolverSymbol(PythonSettings settings) {
163+
return Symbol.builder()
164+
.name("EndpointResolver")
165+
.namespace(String.format("%s.endpoints", settings.moduleName()), ".")
166+
.definitionFile(String.format("./%s/endpoints.py", settings.moduleName()))
167+
.build();
168+
}
169+
170+
/**
171+
* Gets the symbol for the http auth params.
172+
*
173+
* @param settings The client settings, used to account for module configuration.
174+
* @return Returns the http auth params symbol.
147175
*/
148176
public static Symbol getHttpAuthParamsSymbol(PythonSettings settings) {
149177
return Symbol.builder()

codegen/core/src/main/java/software/amazon/smithy/python/codegen/DirectedPythonClientCodegen.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import software.amazon.smithy.model.shapes.ServiceShape;
3030
import software.amazon.smithy.model.shapes.ShapeId;
3131
import software.amazon.smithy.python.codegen.generators.ConfigGenerator;
32+
import software.amazon.smithy.python.codegen.generators.EndpointsGenerator;
3233
import software.amazon.smithy.python.codegen.generators.EnumGenerator;
3334
import software.amazon.smithy.python.codegen.generators.InitGenerator;
3435
import software.amazon.smithy.python.codegen.generators.IntEnumGenerator;
@@ -110,6 +111,7 @@ public void customizeBeforeShapeGeneration(CustomizeDirective<GenerationContext,
110111
if (directive.context().applicationProtocol().isHttpProtocol()
111112
&& !serviceIndex.getAuthSchemes(directive.service()).isEmpty()) {
112113
new HttpAuthGenerator(directive.context(), directive.settings()).run();
114+
new EndpointsGenerator(directive.context(), directive.settings()).run();
113115
}
114116
}
115117

codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/ConfigGenerator.java

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import software.amazon.smithy.python.codegen.integrations.PythonIntegration;
2727
import software.amazon.smithy.python.codegen.integrations.RuntimeClientPlugin;
2828
import software.amazon.smithy.python.codegen.sections.ConfigSection;
29+
import software.amazon.smithy.python.codegen.sections.InitDefaultEndpointResolverSection;
2930
import software.amazon.smithy.python.codegen.writer.PythonWriter;
3031
import software.amazon.smithy.utils.CodeInterceptor;
3132
import software.amazon.smithy.utils.SmithyInternalApi;
@@ -78,30 +79,6 @@ public final class ConfigGenerator implements Runnable {
7879
.build())
7980
.documentation("Configuration for individual HTTP requests.")
8081
.build(),
81-
ConfigProperty.builder()
82-
.name("endpoint_resolver")
83-
.type(Symbol.builder()
84-
.name("EndpointResolver[Any]")
85-
.addReference(Symbol.builder()
86-
.name("Any")
87-
.namespace("typing", ".")
88-
.putProperty(SymbolProperties.STDLIB, true)
89-
.build())
90-
.addReference(Symbol.builder()
91-
.name("EndpointResolver")
92-
.namespace("smithy_http.aio.interfaces", ".")
93-
.addDependency(SmithyPythonDependency.SMITHY_HTTP)
94-
.build())
95-
.build())
96-
.documentation("""
97-
The endpoint resolver used to resolve the final endpoint per-operation based on the \
98-
configuration.""")
99-
.nullable(false)
100-
.initialize(writer -> {
101-
writer.addImport("smithy_http.aio.endpoints", "StaticEndpointResolver");
102-
writer.write("self.endpoint_resolver = endpoint_resolver or StaticEndpointResolver()");
103-
})
104-
.build(),
10582
ConfigProperty.builder()
10683
.name("endpoint_uri")
10784
.type(Symbol.builder()
@@ -124,7 +101,7 @@ public ConfigGenerator(PythonSettings settings, GenerationContext context) {
124101
}
125102

126103
private static List<ConfigProperty> getHttpProperties(GenerationContext context) {
127-
var properties = new ArrayList<ConfigProperty>(HTTP_PROPERTIES.size() + 1);
104+
var properties = new ArrayList<ConfigProperty>(HTTP_PROPERTIES.size() + 2);
128105
var clientBuilder = ConfigProperty.builder()
129106
.name("http_client")
130107
.type(Symbol.builder()
@@ -153,6 +130,25 @@ private static List<ConfigProperty> getHttpProperties(GenerationContext context)
153130
}
154131
properties.add(clientBuilder.build());
155132

133+
var endpointResolver = CodegenUtils.getEndpointResolverSymbol(context.settings());
134+
properties.add(ConfigProperty.builder()
135+
.name("endpoint_resolver")
136+
.type(Symbol.builder()
137+
.name("_EndpointResolver[EndpointParameters]")
138+
.addReference(CodegenUtils.getEndpointParametersSymbol(context.settings()))
139+
.build())
140+
.documentation("""
141+
The endpoint resolver used to resolve the final endpoint per-operation based on the \
142+
configuration.""")
143+
.nullable(false)
144+
.initialize(writer -> {
145+
writer.addImport("smithy_http.aio.interfaces", "EndpointResolver", "_EndpointResolver");
146+
writer.pushState(new InitDefaultEndpointResolverSection());
147+
writer.write("self.endpoint_resolver = endpoint_resolver or $T()", endpointResolver);
148+
writer.popState();
149+
})
150+
.build());
151+
156152
properties.addAll(HTTP_PROPERTIES);
157153
return List.copyOf(properties);
158154
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package software.amazon.smithy.python.codegen.generators;
6+
7+
import software.amazon.smithy.codegen.core.Symbol;
8+
import software.amazon.smithy.python.codegen.CodegenUtils;
9+
import software.amazon.smithy.python.codegen.GenerationContext;
10+
import software.amazon.smithy.python.codegen.PythonSettings;
11+
import software.amazon.smithy.python.codegen.SmithyPythonDependency;
12+
import software.amazon.smithy.python.codegen.sections.EndpointParametersSection;
13+
import software.amazon.smithy.python.codegen.sections.EndpointResolverSection;
14+
import software.amazon.smithy.python.codegen.writer.PythonWriter;
15+
import software.amazon.smithy.utils.SmithyInternalApi;
16+
17+
/**
18+
* This class is responsible for generating the endpoint resolver and its parameters.
19+
*/
20+
@SmithyInternalApi
21+
public final class EndpointsGenerator implements Runnable {
22+
23+
private final PythonSettings settings;
24+
private final GenerationContext context;
25+
26+
public EndpointsGenerator(GenerationContext context, PythonSettings settings) {
27+
this.context = context;
28+
this.settings = settings;
29+
}
30+
31+
@Override
32+
public void run() {
33+
var params = CodegenUtils.getEndpointParametersSymbol(settings);
34+
context.writerDelegator().useFileWriter(params.getDefinitionFile(), params.getNamespace(), writer -> {
35+
generateEndpointParameters(writer, params);
36+
});
37+
38+
var resolver = CodegenUtils.getEndpointResolverSymbol(settings);
39+
context.writerDelegator().useFileWriter(resolver.getDefinitionFile(), resolver.getNamespace(), writer -> {
40+
generateEndpointResolver(writer, resolver);
41+
});
42+
}
43+
44+
private void generateEndpointParameters(PythonWriter writer, Symbol params) {
45+
writer.pushState(new EndpointParametersSection());
46+
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
47+
writer.write("from smithy_http.endpoints import StaticEndpointParams");
48+
writer.write("$L = StaticEndpointParams", params.getName());
49+
writer.popState();
50+
}
51+
52+
private void generateEndpointResolver(PythonWriter writer, Symbol resolver) {
53+
writer.pushState(new EndpointResolverSection());
54+
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
55+
writer.write("from smithy_http.aio.endpoints import StaticEndpointResolver");
56+
writer.write("$L = StaticEndpointResolver", resolver.getName());
57+
writer.popState();
58+
}
59+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package software.amazon.smithy.python.codegen.sections;
6+
7+
import software.amazon.smithy.utils.CodeSection;
8+
import software.amazon.smithy.utils.SmithyInternalApi;
9+
10+
/**
11+
* A section that controls generating the EndpointParameters class.
12+
*/
13+
@SmithyInternalApi
14+
public record EndpointParametersSection() implements CodeSection {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package software.amazon.smithy.python.codegen.sections;
6+
7+
import software.amazon.smithy.utils.CodeSection;
8+
import software.amazon.smithy.utils.SmithyInternalApi;
9+
10+
/**
11+
* A section that controls generating the EndpointResolver class.
12+
*/
13+
@SmithyInternalApi
14+
public record EndpointResolverSection() implements CodeSection {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package software.amazon.smithy.python.codegen.sections;
6+
7+
import software.amazon.smithy.utils.CodeSection;
8+
import software.amazon.smithy.utils.SmithyInternalApi;
9+
10+
/**
11+
* A section that controls generating the EndpointParameters class.
12+
*/
13+
@SmithyInternalApi
14+
public record InitDefaultEndpointResolverSection() implements CodeSection {}

0 commit comments

Comments
 (0)