Skip to content

Commit 7432c64

Browse files
committed
Codegen S3CrtAsyncClientBuilder
This ensures that new additions to S3's set of ClientContextParams are always accounted for. Note that the implementation is still handwritten so when S3 adds new ones, we would still need to make a small code change, but at least there will be a clear signal since the build will fail.
1 parent d27b8e3 commit 7432c64

File tree

13 files changed

+508
-225
lines changed

13 files changed

+508
-225
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.codegen.emitters.tasks;
17+
18+
import java.util.Collections;
19+
import java.util.List;
20+
import java.util.Map;
21+
import java.util.stream.Collectors;
22+
import software.amazon.awssdk.codegen.emitters.GeneratorTask;
23+
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
24+
import software.amazon.awssdk.codegen.model.config.customization.AdditionalClientBuilder;
25+
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
26+
import software.amazon.awssdk.codegen.poet.ClassSpec;
27+
import software.amazon.awssdk.codegen.poet.builder.AdditionalClientBuilderInterfaceSpec;
28+
29+
public class AdditionalClientBuildersTasks extends BaseGeneratorTasks {
30+
31+
private final IntermediateModel intermediateModel;
32+
33+
public AdditionalClientBuildersTasks(GeneratorTaskParams dependencies) {
34+
super(dependencies);
35+
intermediateModel = dependencies.getModel();
36+
}
37+
38+
@Override
39+
protected List<GeneratorTask> createTasks() throws Exception {
40+
Map<String, AdditionalClientBuilder> additionalAsyncClientBuilders =
41+
intermediateModel.getCustomizationConfig().getAdditionalClientBuilders();
42+
43+
if (additionalAsyncClientBuilders == null) {
44+
return Collections.emptyList();
45+
}
46+
47+
return additionalAsyncClientBuilders.entrySet()
48+
.stream()
49+
.map(e -> {
50+
String name = e.getKey();
51+
AdditionalClientBuilder model = e.getValue();
52+
ClassSpec spec = new AdditionalClientBuilderInterfaceSpec(intermediateModel,
53+
name, model);
54+
return createPoetGeneratorTask(spec);
55+
})
56+
.collect(Collectors.toList());
57+
}
58+
}

codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/AwsGeneratorTasks.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public AwsGeneratorTasks(GeneratorTaskParams params) {
2727
new PaginatorsGeneratorTasks(params),
2828
new EventStreamGeneratorTasks(params),
2929
new WaitersGeneratorTasks(params),
30-
new EndpointProviderTasks(params));
30+
new EndpointProviderTasks(params),
31+
new AdditionalClientBuildersTasks(params));
3132
}
3233
}

codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/BaseGeneratorTasks.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ protected boolean hasTasks() {
5151
return true;
5252
}
5353

54-
protected final GeneratorTask createPoetGeneratorTask(ClassSpec classSpec) throws IOException {
54+
protected final GeneratorTask createPoetGeneratorTask(ClassSpec classSpec) {
5555
String targetDirectory = baseDirectory + '/' + Utils.packageToDirectory(classSpec.className().packageName());
5656
return new PoetGeneratorTask(targetDirectory, model.getFileHeader(), classSpec);
5757
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.codegen.model.config.customization;
17+
18+
import java.util.Map;
19+
import software.amazon.awssdk.core.ClientType;
20+
21+
public class AdditionalClientBuilder {
22+
private Map<String, BuilderProperty> properties;
23+
private ClientType clientType;
24+
25+
public ClientType getClientType() {
26+
return clientType;
27+
}
28+
29+
public void setClientType(ClientType clientType) {
30+
this.clientType = clientType;
31+
}
32+
33+
public Map<String, BuilderProperty> getProperties() {
34+
return properties;
35+
}
36+
37+
public void setProperties(Map<String, BuilderProperty> properties) {
38+
this.properties = properties;
39+
}
40+
41+
public static class BuilderProperty {
42+
private String classFqcn;
43+
private String javadoc;
44+
private boolean generateConsumerBuilder;
45+
46+
public String getClassFqcn() {
47+
return classFqcn;
48+
}
49+
50+
public void setClassFqcn(String classFqcn) {
51+
this.classFqcn = classFqcn;
52+
}
53+
54+
public String getJavadoc() {
55+
return javadoc;
56+
}
57+
58+
public void setJavadoc(String javadoc) {
59+
this.javadoc = javadoc;
60+
}
61+
62+
public boolean isGenerateConsumerBuilder() {
63+
return generateConsumerBuilder;
64+
}
65+
66+
public void setGenerateConsumerBuilder(boolean generateConsumerBuilder) {
67+
this.generateConsumerBuilder = generateConsumerBuilder;
68+
}
69+
}
70+
}

codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ public class CustomizationConfig {
231231
*/
232232
private boolean requiredTraitValidationEnabled = false;
233233

234+
private Map<String, AdditionalClientBuilder> additionalClientBuilders;
235+
234236
private CustomizationConfig() {
235237
}
236238

@@ -608,4 +610,12 @@ public boolean isRequiredTraitValidationEnabled() {
608610
public void setRequiredTraitValidationEnabled(boolean requiredTraitValidationEnabled) {
609611
this.requiredTraitValidationEnabled = requiredTraitValidationEnabled;
610612
}
613+
614+
public Map<String, AdditionalClientBuilder> getAdditionalClientBuilders() {
615+
return additionalClientBuilders;
616+
}
617+
618+
public void setAdditionalClientBuilders(Map<String, AdditionalClientBuilder> additionalClientBuilders) {
619+
this.additionalClientBuilders = additionalClientBuilders;
620+
}
611621
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.codegen.poet.builder;
17+
18+
import com.squareup.javapoet.ClassName;
19+
import com.squareup.javapoet.MethodSpec;
20+
import com.squareup.javapoet.ParameterizedTypeName;
21+
import com.squareup.javapoet.TypeSpec;
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
import java.util.function.Consumer;
25+
import java.util.stream.Collectors;
26+
import javax.lang.model.element.Modifier;
27+
import software.amazon.awssdk.annotations.SdkPublicApi;
28+
import software.amazon.awssdk.codegen.internal.Utils;
29+
import software.amazon.awssdk.codegen.model.config.customization.AdditionalClientBuilder;
30+
import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig;
31+
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
32+
import software.amazon.awssdk.codegen.poet.ClassSpec;
33+
import software.amazon.awssdk.codegen.poet.PoetExtension;
34+
import software.amazon.awssdk.codegen.poet.PoetUtils;
35+
import software.amazon.awssdk.codegen.poet.rules.EndpointRulesSpecUtils;
36+
import software.amazon.awssdk.utils.Validate;
37+
import software.amazon.awssdk.utils.builder.SdkBuilder;
38+
import software.amazon.awssdk.utils.internal.CodegenNamingUtils;
39+
40+
/**
41+
* Spec for generating additional client builders for a service client. Works in conjunction with
42+
* {@link CustomizationConfig#getAdditionalClientBuilders()}.
43+
*/
44+
public final class AdditionalClientBuilderInterfaceSpec implements ClassSpec {
45+
private final IntermediateModel model;
46+
private final String name;
47+
private final AdditionalClientBuilder clientBuilderModel;
48+
private final PoetExtension poetExt;
49+
private final EndpointRulesSpecUtils endpointRulesSpecUtils;
50+
51+
public AdditionalClientBuilderInterfaceSpec(IntermediateModel model, String name,
52+
AdditionalClientBuilder clientBuilderModel) {
53+
this.model = model;
54+
this.name = name;
55+
this.clientBuilderModel = clientBuilderModel;
56+
this.poetExt = new PoetExtension(model);
57+
this.endpointRulesSpecUtils = new EndpointRulesSpecUtils(model);
58+
}
59+
60+
@Override
61+
public TypeSpec poetSpec() {
62+
TypeSpec.Builder builder = PoetUtils.createInterfaceBuilder(className());
63+
64+
builder.addAnnotation(SdkPublicApi.class);
65+
ParameterizedTypeName superInterface = ParameterizedTypeName.get(
66+
ClassName.get(SdkBuilder.class), className(), classToBuild());
67+
builder.addSuperinterface(superInterface);
68+
69+
propertySetters().forEach(builder::addMethod);
70+
clientContextParamSetterMethods().forEach(builder::addMethod);
71+
builder.addMethod(buildMethod());
72+
73+
return builder.build();
74+
}
75+
76+
@Override
77+
public ClassName className() {
78+
return poetExt.getClientClass(name);
79+
}
80+
81+
private ClassName classToBuild() {
82+
switch (clientBuilderModel.getClientType()) {
83+
case ASYNC:
84+
return poetExt.getClientClass(model.getMetadata().getAsyncInterface());
85+
case SYNC:
86+
return poetExt.getClientClass(model.getMetadata().getSyncInterface());
87+
default:
88+
throw new RuntimeException("Unknown client type: " + clientBuilderModel.getClientType());
89+
}
90+
}
91+
92+
private List<MethodSpec> propertySetters() {
93+
return clientBuilderModel.getProperties()
94+
.entrySet()
95+
.stream()
96+
.flatMap(p -> propertySetterMethods(CodegenNamingUtils.pascalCase(p.getKey()), p.getValue()).stream())
97+
.collect(Collectors.toList());
98+
}
99+
100+
private List<MethodSpec> propertySetterMethods(String name, AdditionalClientBuilder.BuilderProperty property) {
101+
name = Utils.unCapitalize(CodegenNamingUtils.pascalCase(name));
102+
ClassName type = PoetUtils.classNameFromFqcn(property.getClassFqcn());
103+
104+
List<MethodSpec> specs = new ArrayList<>();
105+
106+
specs.add(MethodSpec.methodBuilder(name)
107+
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
108+
.addParameter(type, name)
109+
.addJavadoc(property.getJavadoc())
110+
.returns(className())
111+
.build());
112+
113+
if (property.isGenerateConsumerBuilder()) {
114+
ClassName propertyBuilder = type.nestedClass("Builder");
115+
String builderName = name + "Builder";
116+
ParameterizedTypeName consumer = ParameterizedTypeName.get(ClassName.get(Consumer.class), propertyBuilder);
117+
specs.add(MethodSpec.methodBuilder(name)
118+
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
119+
.addParameter(consumer, builderName)
120+
.addStatement("$1T.paramNotNull($2N, $2S)", Validate.class, builderName)
121+
.addStatement("return $N($T.builder().applyMutation($N).build())", name, type, builderName)
122+
.returns(className())
123+
.build());
124+
}
125+
126+
return specs;
127+
}
128+
129+
private List<MethodSpec> clientContextParamSetterMethods() {
130+
return model.getClientContextParams()
131+
.entrySet()
132+
.stream()
133+
.map(e -> endpointRulesSpecUtils.clientContextParamSetterMethodDeclaration(e.getKey(), e.getValue(),
134+
className()))
135+
.collect(Collectors.toList());
136+
}
137+
138+
private MethodSpec buildMethod() {
139+
return MethodSpec.methodBuilder("build")
140+
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
141+
.addAnnotation(Override.class)
142+
.returns(classToBuild())
143+
.build();
144+
}
145+
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderInterface.java

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,12 @@
2828
import software.amazon.awssdk.auth.token.credentials.aws.DefaultAwsTokenProvider;
2929
import software.amazon.awssdk.auth.token.signer.aws.BearerTokenSigner;
3030
import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;
31-
import software.amazon.awssdk.codegen.internal.Utils;
3231
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
33-
import software.amazon.awssdk.codegen.model.service.ClientContextParam;
3432
import software.amazon.awssdk.codegen.poet.ClassSpec;
3533
import software.amazon.awssdk.codegen.poet.PoetUtils;
3634
import software.amazon.awssdk.codegen.poet.rules.EndpointRulesSpecUtils;
3735
import software.amazon.awssdk.codegen.utils.AuthUtils;
3836
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
39-
import software.amazon.awssdk.utils.internal.CodegenNamingUtils;
4037

4138
public class BaseClientBuilderInterface implements ClassSpec {
4239
private final IntermediateModel model;
@@ -76,7 +73,8 @@ public TypeSpec poetSpec() {
7673

7774
if (hasClientContextParams()) {
7875
model.getClientContextParams().forEach((n, m) -> {
79-
builder.addMethod(clientContextParamSetter(n, m));
76+
builder.addMethod(endpointRulesSpecUtils.clientContextParamSetterMethodDeclaration(
77+
n, m, TypeVariableName.get("B")));
8078
});
8179
}
8280

@@ -148,20 +146,6 @@ private MethodSpec endpointProviderMethod() {
148146
.build();
149147
}
150148

151-
private MethodSpec clientContextParamSetter(String name, ClientContextParam param) {
152-
String setterName = Utils.unCapitalize(CodegenNamingUtils.pascalCase(name));
153-
TypeName type = endpointRulesSpecUtils.toJavaType(param.getType());
154-
155-
MethodSpec.Builder b = MethodSpec.methodBuilder(setterName)
156-
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
157-
.addParameter(type, setterName)
158-
.returns(TypeVariableName.get("B"));
159-
160-
PoetUtils.addJavadoc(b::addJavadoc, param.getDocumentation());
161-
162-
return b.build();
163-
}
164-
165149
private boolean generateTokenProviderMethod() {
166150
return AuthUtils.usesBearerAuth(model);
167151
}

0 commit comments

Comments
 (0)