Skip to content

Commit ad28652

Browse files
joewyzmtdowling
authored andcommitted
Add codegen for bdd in client codegen
1 parent bf5b4f4 commit ad28652

File tree

8 files changed

+293
-1
lines changed

8 files changed

+293
-1
lines changed

codegen/plugins/client-codegen/build.gradle.kts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ extra["moduleName"] = "software.amazon.smithy.java.codegen.client"
99

1010
dependencies {
1111
api(project(":client:client-core"))
12+
api(project(":client:client-rulesengine"))
1213
testImplementation(project(":aws:client:aws-client-restjson"))
1314
testImplementation(libs.smithy.aws.traits)
14-
15+
testImplementation(libs.smithy.rules)
1516
itImplementation(project(":aws:client:aws-client-restjson"))
1617
}
1718

@@ -25,5 +26,10 @@ sourceSets {
2526
it {
2627
// Add test plugin to classpath
2728
compileClasspath += sourceSets["test"].output
29+
resources.srcDir("${layout.buildDirectory.get()}/generated-src/resources")
2830
}
2931
}
32+
33+
tasks.named("processItResources") {
34+
dependsOn("generateSources")
35+
}
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+
6+
package software.amazon.smithy.java.codegen.client;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertNotNull;
10+
11+
import java.util.HashMap;
12+
import java.util.List;
13+
import java.util.Map;
14+
import org.junit.jupiter.api.Test;
15+
import smithy.java.codegen.server.bddTest.client.BddServiceClient;
16+
import software.amazon.smithy.java.aws.client.restjson.RestJsonClientProtocol;
17+
import software.amazon.smithy.java.client.core.endpoint.Endpoint;
18+
import software.amazon.smithy.java.client.core.endpoint.EndpointResolver;
19+
import software.amazon.smithy.java.client.core.endpoint.EndpointResolverParams;
20+
import software.amazon.smithy.java.client.rulesengine.EndpointRulesPlugin;
21+
import software.amazon.smithy.java.context.Context;
22+
import software.amazon.smithy.java.core.schema.ApiOperation;
23+
import software.amazon.smithy.java.core.schema.ApiService;
24+
import software.amazon.smithy.java.core.schema.PreludeSchemas;
25+
import software.amazon.smithy.java.core.schema.Schema;
26+
import software.amazon.smithy.java.core.schema.SerializableStruct;
27+
import software.amazon.smithy.java.core.schema.ShapeBuilder;
28+
import software.amazon.smithy.java.core.serde.ShapeSerializer;
29+
import software.amazon.smithy.java.core.serde.TypeRegistry;
30+
import software.amazon.smithy.model.shapes.ShapeId;
31+
32+
public class BddTest {
33+
@Test
34+
public void testBddEndpointResolution() {
35+
BddServiceClient client = BddServiceClient.builder()
36+
.protocol(new RestJsonClientProtocol(PreludeSchemas.DOCUMENT.id()))
37+
.build();
38+
39+
EndpointResolver resolver = client.config().endpointResolver();
40+
41+
EndpointResolverParams params1 = createParams("us-east-1", false);
42+
Endpoint endpoint1 = resolver.resolveEndpoint(params1);
43+
assertNotNull(endpoint1);
44+
assertEquals("https://service.us-east-1.amazonaws.com", endpoint1.uri().toString());
45+
46+
EndpointResolverParams params2 = createParams("us-east-2", true);
47+
Endpoint endpoint2 = resolver.resolveEndpoint(params2);
48+
assertNotNull(endpoint2);
49+
assertEquals("https://service-fips.us-east-2.amazonaws.com", endpoint2.uri().toString());
50+
}
51+
52+
private EndpointResolverParams createParams(String Region, Boolean UseFips) {
53+
Map<String, Object> endpointParams = new HashMap<>();
54+
if (Region != null) {
55+
endpointParams.put("Region", Region);
56+
}
57+
if (UseFips != null) {
58+
endpointParams.put("UseFips", UseFips);
59+
}
60+
Context context = Context.create();
61+
Context fullContext = context.put(EndpointRulesPlugin.ADDITIONAL_ENDPOINT_PARAMS, endpointParams);
62+
63+
TestOperation operation = new TestOperation();
64+
TestInput input = new TestInput();
65+
66+
return EndpointResolverParams.builder().operation(operation).inputValue(input).context(fullContext).build();
67+
}
68+
69+
private static final Schema INPUT_SCHEMA = Schema.structureBuilder(ShapeId.from("smithy.example#I")).build();
70+
71+
private static class TestOperation implements ApiOperation<TestInput, TestInput> {
72+
@Override
73+
public ShapeBuilder<TestInput> inputBuilder() {
74+
throw new UnsupportedOperationException();
75+
}
76+
77+
@Override
78+
public ShapeBuilder<TestInput> outputBuilder() {
79+
throw new UnsupportedOperationException();
80+
}
81+
82+
@Override
83+
public Schema schema() {
84+
return Schema.createOperation(ShapeId.from("smithy.example#Foo"));
85+
}
86+
87+
@Override
88+
public Schema inputSchema() {
89+
return INPUT_SCHEMA;
90+
}
91+
92+
@Override
93+
public Schema outputSchema() {
94+
return INPUT_SCHEMA;
95+
}
96+
97+
@Override
98+
public TypeRegistry errorRegistry() {
99+
throw new UnsupportedOperationException();
100+
}
101+
102+
@Override
103+
public List<ShapeId> effectiveAuthSchemes() {
104+
return List.of();
105+
}
106+
107+
@Override
108+
public ApiService service() {
109+
throw new UnsupportedOperationException();
110+
}
111+
}
112+
113+
private static class TestInput implements SerializableStruct {
114+
@Override
115+
public Schema schema() {
116+
return INPUT_SCHEMA;
117+
}
118+
119+
@Override
120+
public void serializeMembers(ShapeSerializer serializer) {}
121+
122+
@Override
123+
public <T> T getMemberValue(Schema member) {
124+
return null;
125+
}
126+
}
127+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
$version: "2.0"
2+
3+
namespace smithy.java.codegen.server.test
4+
5+
use aws.protocols#restJson1
6+
use smithy.rules#clientContextParams
7+
use smithy.rules#endpointBdd
8+
9+
@clientContextParams(
10+
Region: {type: "string", documentation: "docs"}
11+
UseFips: {type: "boolean", documentation: "docs"}
12+
)
13+
@endpointBdd({
14+
version: "1.1"
15+
"parameters": {
16+
"Region": {
17+
"required": true,
18+
"documentation": "The AWS region",
19+
"type": "string"
20+
},
21+
"UseFips": {
22+
"required": true,
23+
"default": false,
24+
"documentation": "Use FIPS endpoints",
25+
"type": "boolean"
26+
}
27+
},
28+
"conditions": [
29+
{
30+
"fn": "booleanEquals",
31+
"argv": [
32+
{
33+
"ref": "UseFips"
34+
},
35+
true
36+
]
37+
}
38+
],
39+
"results": [
40+
{
41+
"conditions": [],
42+
"endpoint": {
43+
"url": "https://service-fips.{Region}.amazonaws.com",
44+
"properties": {},
45+
"headers": {}
46+
},
47+
"type": "endpoint"
48+
},
49+
{
50+
"conditions": [],
51+
"endpoint": {
52+
"url": "https://service.{Region}.amazonaws.com",
53+
"properties": {},
54+
"headers": {}
55+
},
56+
"type": "endpoint"
57+
}
58+
],
59+
"root": 2,
60+
"nodeCount": 2,
61+
"nodes": "/////wAAAAH/////AAAAAAX14QEF9eEC"
62+
})
63+
@restJson1
64+
service BddService {
65+
version: "2022-01-01"
66+
operations:[
67+
Echo
68+
]
69+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
main.smithy
22
test-auth-scheme.smithy
3+
bdd-test.smithy

codegen/plugins/client-codegen/src/main/java/software/amazon/smithy/java/codegen/client/DirectedJavaClientCodegen.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import software.amazon.smithy.java.codegen.CodeGenerationContext;
1111
import software.amazon.smithy.java.codegen.JavaCodegenIntegration;
1212
import software.amazon.smithy.java.codegen.JavaCodegenSettings;
13+
import software.amazon.smithy.java.codegen.client.generators.BddInfoGenerator;
1314
import software.amazon.smithy.java.codegen.client.generators.ClientImplementationGenerator;
1415
import software.amazon.smithy.java.codegen.client.generators.ClientInterfaceGenerator;
1516
import software.amazon.smithy.java.codegen.generators.ApiServiceGenerator;
@@ -24,6 +25,7 @@
2425
import software.amazon.smithy.java.codegen.generators.SharedSerdeGenerator;
2526
import software.amazon.smithy.java.codegen.generators.StructureGenerator;
2627
import software.amazon.smithy.java.codegen.generators.UnionGenerator;
28+
import software.amazon.smithy.rulesengine.traits.EndpointBddTrait;
2729
import software.amazon.smithy.utils.SmithyUnstableApi;
2830

2931
@SmithyUnstableApi
@@ -115,6 +117,10 @@ public void generateService(GenerateServiceDirective<CodeGenerationContext, Java
115117
new ApiServiceGenerator().accept(directive);
116118
new ServiceExceptionGenerator<>().accept(directive);
117119
}
120+
121+
if (directive.service().hasTrait(EndpointBddTrait.ID)) {
122+
new BddInfoGenerator().accept(directive);
123+
}
118124
}
119125

120126
@Override
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.java.codegen.client.generators;
7+
8+
import java.io.IOException;
9+
import java.nio.file.Files;
10+
import java.util.function.Consumer;
11+
import software.amazon.smithy.codegen.core.directed.GenerateServiceDirective;
12+
import software.amazon.smithy.java.client.rulesengine.RulesEngineBuilder;
13+
import software.amazon.smithy.java.codegen.CodeGenerationContext;
14+
import software.amazon.smithy.java.codegen.JavaCodegenSettings;
15+
import software.amazon.smithy.rulesengine.traits.EndpointBddTrait;
16+
17+
public class BddInfoGenerator
18+
implements Consumer<GenerateServiceDirective<CodeGenerationContext, JavaCodegenSettings>> {
19+
@Override
20+
public void accept(GenerateServiceDirective<CodeGenerationContext, JavaCodegenSettings> directive) {
21+
try {
22+
var baseDir = directive.fileManifest().getBaseDir();
23+
var fileDir = baseDir.resolve("resources/META-INF/endpoints/bdd-info.bin");
24+
var parentDir = fileDir.getParent();
25+
if (parentDir != null) {
26+
Files.createDirectories(parentDir);
27+
}
28+
var engineBuilder = new RulesEngineBuilder();
29+
var bddTrait = directive.service().expectTrait(EndpointBddTrait.class);
30+
var bytecode = engineBuilder.compile(bddTrait);
31+
Files.write(fileDir, bytecode.getBytecode());
32+
} catch (IOException e) {
33+
throw new RuntimeException("Failed to write BDD bytecode binary file", e);
34+
}
35+
}
36+
}

codegen/plugins/client-codegen/src/main/java/software/amazon/smithy/java/codegen/client/generators/ClientInterfaceGenerator.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package software.amazon.smithy.java.codegen.client.generators;
77

8+
import java.io.IOException;
89
import java.util.ArrayList;
910
import java.util.Collection;
1011
import java.util.HashMap;
@@ -28,6 +29,8 @@
2829
import software.amazon.smithy.java.client.core.RequestOverrideConfig;
2930
import software.amazon.smithy.java.client.core.auth.scheme.AuthSchemeFactory;
3031
import software.amazon.smithy.java.client.core.pagination.Paginator;
32+
import software.amazon.smithy.java.client.rulesengine.EndpointRulesPlugin;
33+
import software.amazon.smithy.java.client.rulesengine.RulesEngineBuilder;
3134
import software.amazon.smithy.java.codegen.CodeGenerationContext;
3235
import software.amazon.smithy.java.codegen.CodegenUtils;
3336
import software.amazon.smithy.java.codegen.JavaCodegenSettings;
@@ -56,6 +59,7 @@
5659
import software.amazon.smithy.model.shapes.ToShapeId;
5760
import software.amazon.smithy.model.traits.PaginatedTrait;
5861
import software.amazon.smithy.model.traits.Trait;
62+
import software.amazon.smithy.rulesengine.traits.EndpointBddTrait;
5963
import software.amazon.smithy.utils.SmithyInternalApi;
6064
import software.amazon.smithy.utils.StringUtils;
6165

@@ -145,6 +149,7 @@ private Builder() {
145149
${/hasDefaultProtocol}${?hasDefaultTransport}if (configBuilder().transport() == null) {
146150
configBuilder().transport(${transportFactory:C}.createTransport(${?hasTransportSettings}transportSettings${/hasTransportSettings}));
147151
}${/hasDefaultTransport}
152+
${?hasBdd}${loadBddInfo:C|}${/hasBdd}
148153
return new ${impl:T}(this);
149154
}
150155
}
@@ -175,6 +180,8 @@ final class RequestOverrideBuilder extends ${requestOverride:T}.OverrideBuilder<
175180
writer.putContext("interface", symbol);
176181
writer.putContext("impl", symbol.expectProperty(ClientSymbolProperties.CLIENT_IMPL));
177182
writer.putContext("hasDefaultTransport", settings.transport() != null);
183+
writer.putContext("hasBdd", directive.service().hasTrait(EndpointBddTrait.ID));
184+
writer.putContext("loadBddInfo", new LoadBddInfoGenerator(writer));
178185
var hasTransportSettings = settings.transportSettings() != null && !settings.transportSettings()
179186
.isEmpty();
180187
writer.putContext("hasTransportSettings", hasTransportSettings);
@@ -558,4 +565,24 @@ private static List<Class<?>> getBuilderSettings(JavaCodegenSettings settings) {
558565
}
559566
return result;
560567
}
568+
569+
private record LoadBddInfoGenerator(JavaWriter writer) implements Runnable {
570+
@Override
571+
public void run() {
572+
writer.write("""
573+
try (var stream = getClass().getResourceAsStream("/META-INF/endpoints/bdd-info.bin")) {
574+
if (stream != null) {
575+
var bytecode = new $T().load(stream.readAllBytes());
576+
configBuilder().applyPlugin($T.from(bytecode));
577+
}
578+
} catch ($T e) {
579+
throw new $T("Failed to load BDD bytecode binary file", e);
580+
}
581+
""",
582+
RulesEngineBuilder.class,
583+
EndpointRulesPlugin.class,
584+
IOException.class,
585+
RuntimeException.class);
586+
}
587+
}
561588
}

codegen/plugins/client-codegen/src/test/java/software/amazon/smithy/java/codegen/client/TestServerJavaClientCodegenRunner.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,25 @@ public static void main(String[] args) {
4646
.model(model)
4747
.build();
4848
plugin.execute(context);
49+
50+
PluginContext bddContext = PluginContext.builder()
51+
.fileManifest(FileManifest.create(Paths.get(System.getenv("output"))))
52+
.settings(
53+
ObjectNode.builder()
54+
.withMember("service", "smithy.java.codegen.server.test#BddService")
55+
.withMember("namespace", "smithy.java.codegen.server.bddTest")
56+
.withMember(
57+
"transport",
58+
ObjectNode.builder()
59+
.withMember("http-java", ObjectNode.builder().build())
60+
.build())
61+
.withMember("defaultPlugins",
62+
ArrayNode.fromStrings(TestClientPlugin.class.getCanonicalName()))
63+
.withMember("defaultSettings",
64+
ArrayNode.fromStrings(TestSettings.class.getCanonicalName()))
65+
.build())
66+
.model(model)
67+
.build();
68+
plugin.execute(bddContext);
4969
}
5070
}

0 commit comments

Comments
 (0)