Skip to content

Commit 2218184

Browse files
committed
Add user agent for service version
1 parent bcbe304 commit 2218184

File tree

11 files changed

+205
-10
lines changed

11 files changed

+205
-10
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import software.amazon.awssdk.codegen.poet.client.EnvironmentTokenSystemSettingsClass;
2424
import software.amazon.awssdk.codegen.poet.client.SdkClientOptions;
2525
import software.amazon.awssdk.codegen.poet.client.specs.ServiceVersionInfoSpec;
26+
import software.amazon.awssdk.codegen.poet.client.specs.ServiceVersionUserAgentSpec;
2627
import software.amazon.awssdk.codegen.poet.common.UserAgentUtilsSpec;
2728

2829
public class CommonInternalGeneratorTasks extends BaseGeneratorTasks {
@@ -42,6 +43,7 @@ protected List<GeneratorTask> createTasks() throws Exception {
4243
tasks.add(createEnvironmentTokenSystemSettingTask());
4344
}
4445
tasks.add(createServiceVersionInfoTask());
46+
tasks.add(createServiceVersionUserAgentTask());
4547
return tasks;
4648
}
4749

@@ -65,6 +67,11 @@ private GeneratorTask createServiceVersionInfoTask() {
6567
new ServiceVersionInfoSpec(params.getModel()));
6668
}
6769

70+
private GeneratorTask createServiceVersionUserAgentTask() {
71+
return new PoetGeneratorTask(clientOptionsDir(), params.getModel().getFileHeader(),
72+
new ServiceVersionUserAgentSpec(params.getModel()));
73+
}
74+
6875
private String clientOptionsDir() {
6976
return params.getPathProvider().getClientInternalDirectory();
7077
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/PoetExtension.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ public ClassName getServiceVersionInfoClass() {
8383
return ClassName.get(model.getMetadata().getFullClientInternalPackageName(), "ServiceVersionInfo");
8484
}
8585

86+
public ClassName getServiceVersionUserAgentClass() {
87+
return ClassName.get(model.getMetadata().getFullClientInternalPackageName(), "ServiceVersionUserAgent");
88+
}
89+
8690
public ClassName getEnvironmentTokenSystemSettingsClass() {
8791
return ClassName.get(model.getMetadata().getFullClientInternalPackageName(), "EnvironmentTokenSystemSettings");
8892
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,13 @@ private MethodSpec constructor(TypeSpec.Builder classBuilder) {
229229
.addStatement("this.clientHandler = new $T(clientConfiguration)", AwsAsyncClientHandler.class)
230230
.addStatement("this.clientConfiguration = clientConfiguration.toBuilder()"
231231
+ ".option($T.SDK_CLIENT, this)"
232-
+ ".build()", SdkClientOption.class);
232+
+ ".option($T.API_METADATA, $T.USER_AGENT)"
233+
+ ".build()",
234+
SdkClientOption.class,
235+
SdkClientOption.class,
236+
ClassName.get(model.getMetadata().getFullClientInternalPackageName(),
237+
"ServiceVersionUserAgent"));
238+
233239
FieldSpec protocolFactoryField = protocolSpec.protocolFactory(model);
234240
if (model.getMetadata().isJsonProtocol()) {
235241
builder.addStatement("this.$N = init($T.builder()).build()", protocolFactoryField.name,

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,12 @@ private MethodSpec constructor() {
201201
.addStatement("this.clientHandler = new $T(clientConfiguration)", protocolSpec.getClientHandlerClass())
202202
.addStatement("this.clientConfiguration = clientConfiguration.toBuilder()"
203203
+ ".option($T.SDK_CLIENT, this)"
204-
+ ".build()", SdkClientOption.class);
204+
+ ".option($T.API_METADATA, $T.USER_AGENT)"
205+
+ ".build()",
206+
SdkClientOption.class,
207+
SdkClientOption.class,
208+
ClassName.get(model.getMetadata().getFullClientInternalPackageName(),
209+
"ServiceVersionUserAgent"));
205210

206211
FieldSpec protocolFactoryField = protocolSpec.protocolFactory(model);
207212
if (model.getMetadata().isJsonProtocol()) {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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.client.specs;
17+
18+
import static software.amazon.awssdk.core.util.VersionInfo.SDK_VERSION;
19+
20+
import com.squareup.javapoet.ClassName;
21+
import com.squareup.javapoet.FieldSpec;
22+
import com.squareup.javapoet.MethodSpec;
23+
import com.squareup.javapoet.TypeSpec;
24+
import javax.lang.model.element.Modifier;
25+
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
26+
import software.amazon.awssdk.codegen.poet.ClassSpec;
27+
import software.amazon.awssdk.codegen.poet.PoetExtension;
28+
import software.amazon.awssdk.codegen.poet.PoetUtils;
29+
30+
31+
public class ServiceVersionUserAgentSpec implements ClassSpec {
32+
private final PoetExtension poetExtension;
33+
private final IntermediateModel model;
34+
35+
public ServiceVersionUserAgentSpec(IntermediateModel model) {
36+
this.poetExtension = new PoetExtension(model);
37+
this.model = model;
38+
}
39+
40+
@Override
41+
public TypeSpec poetSpec() {
42+
TypeSpec.Builder builder = TypeSpec.classBuilder("ServiceVersionUserAgent")
43+
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
44+
.addAnnotation(PoetUtils.generatedAnnotation())
45+
.addField(userAgentField())
46+
.addMethod(privateConstructor());
47+
return builder.build();
48+
}
49+
50+
private String transformServiceId(String serviceId) {
51+
// According to User Agent 2.0 spec, replace spaces with underscores
52+
return serviceId.replace(" ", "_");
53+
}
54+
55+
private FieldSpec userAgentField() {
56+
return FieldSpec.builder(String.class, "USER_AGENT", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
57+
.initializer("$S", transformServiceId(model.getMetadata().getServiceId()) + "#" + SDK_VERSION)
58+
.addJavadoc("Returns a user agent containing the service and "
59+
+ "version info")
60+
.build();
61+
62+
}
63+
64+
protected MethodSpec privateConstructor() {
65+
return MethodSpec.constructorBuilder()
66+
.addModifiers(Modifier.PRIVATE)
67+
.build();
68+
}
69+
70+
@Override
71+
public ClassName className() {
72+
return poetExtension.getServiceVersionUserAgentClass();
73+
}
74+
}
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.poet.client;
17+
18+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
19+
20+
import com.squareup.javapoet.JavaFile;
21+
import com.squareup.javapoet.TypeSpec;
22+
import java.io.InputStream;
23+
import java.util.Scanner;
24+
import org.junit.jupiter.api.Test;
25+
import software.amazon.awssdk.codegen.poet.ClassSpec;
26+
import software.amazon.awssdk.codegen.poet.ClientTestModels;
27+
import software.amazon.awssdk.codegen.poet.client.specs.ServiceVersionUserAgentSpec;
28+
import software.amazon.awssdk.core.util.VersionInfo;
29+
30+
public class ServiceVersionUserAgentSpecTest {
31+
32+
// Fixture test that compares generated ServiceVersionUserAgent class against expected output.
33+
// The fixture file uses {{VERSION}} as a placeholder which gets replaced with the current
34+
// SDK version at test time, since the generated code injects the actual version at build time.
35+
@Test
36+
void testServiceVersionUserAgentClass() {
37+
String currVersion = VersionInfo.SDK_VERSION;
38+
ClassSpec serviceVersionUserAgentSpec = new ServiceVersionUserAgentSpec(ClientTestModels.restJsonServiceModels());
39+
40+
String expectedContent = loadFixtureFile("test-service-version-user-agent-class.java");
41+
expectedContent = expectedContent.replace("{{VERSION}}", currVersion);
42+
43+
String actualContent = generateContent(serviceVersionUserAgentSpec);
44+
45+
assertThat(actualContent).isEqualToIgnoringWhitespace(expectedContent);
46+
}
47+
48+
private String loadFixtureFile(String filename) {
49+
InputStream is = getClass().getResourceAsStream("specs/" + filename);
50+
return new Scanner(is).useDelimiter("\\A").next();
51+
}
52+
53+
private String generateContent(ClassSpec spec) {
54+
TypeSpec typeSpec = spec.poetSpec();
55+
JavaFile javaFile = JavaFile.builder(spec.className().packageName(), typeSpec).build();
56+
return javaFile.toString();
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package software.amazon.awssdk.services.json.internal;
2+
3+
import java.lang.String;
4+
import software.amazon.awssdk.annotations.Generated;
5+
6+
@Generated("software.amazon.awssdk:codegen")
7+
public final class ServiceVersionUserAgent {
8+
/**
9+
* Returns a user agent containing the service and version info
10+
*/
11+
public static final String USER_AGENT = "Json_Service#{{VERSION}}";
12+
13+
private ServiceVersionUserAgent() {
14+
}
15+
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.USER_AGENT_PREFIX;
2222
import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.USER_AGENT_SUFFIX;
2323
import static software.amazon.awssdk.core.client.config.SdkClientOption.ADDITIONAL_HTTP_HEADERS;
24+
import static software.amazon.awssdk.core.client.config.SdkClientOption.API_METADATA;
2425
import static software.amazon.awssdk.core.client.config.SdkClientOption.ASYNC_HTTP_CLIENT;
2526
import static software.amazon.awssdk.core.client.config.SdkClientOption.CLIENT_TYPE;
2627
import static software.amazon.awssdk.core.client.config.SdkClientOption.CLIENT_USER_AGENT;
@@ -93,6 +94,7 @@
9394
import software.amazon.awssdk.core.internal.useragent.AppIdResolver;
9495
import software.amazon.awssdk.core.internal.useragent.SdkClientUserAgentProperties;
9596
import software.amazon.awssdk.core.internal.useragent.SdkUserAgentBuilder;
97+
import software.amazon.awssdk.core.internal.useragent.UserAgentConstant;
9698
import software.amazon.awssdk.core.retry.RetryMode;
9799
import software.amazon.awssdk.core.util.SystemUserAgent;
98100
import software.amazon.awssdk.http.ExecutableHttpRequest;
@@ -290,6 +292,7 @@ private SdkClientConfiguration mergeGlobalDefaults(SdkClientConfiguration config
290292
.option(USER_AGENT_PREFIX, "")
291293
.option(USER_AGENT_SUFFIX, "")
292294
.option(CRC32_FROM_COMPRESSED_DATA_ENABLED, false)
295+
.option(API_METADATA, "")
293296
.option(CONFIGURED_COMPRESSION_CONFIGURATION,
294297
CompressionConfiguration.builder().build()));
295298
return configuration;
@@ -402,6 +405,7 @@ private String resolveClientUserAgent(LazyValueSource config) {
402405
String appId = config.get(USER_AGENT_APP_ID);
403406
String resolvedAppId = appId == null ? resolveAppId(config) : appId;
404407
clientProperties.putProperty(APP_ID, resolvedAppId);
408+
clientProperties.putProperty(UserAgentConstant.API_METADATA, config.get(API_METADATA));
405409
return SdkUserAgentBuilder.buildClientUserAgentString(SystemUserAgent.getOrCreate(), clientProperties);
406410
}
407411

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,11 @@ public final class SdkClientOption<T> extends ClientOption<T> {
342342
public static final SdkClientOption<ResponseChecksumValidation> RESPONSE_CHECKSUM_VALIDATION =
343343
new SdkClientOption<>(ResponseChecksumValidation.class);
344344

345+
/**
346+
* The API metadata for user agent (service-id#version).
347+
*/
348+
public static final SdkClientOption<String> API_METADATA = new SdkClientOption<>(String.class);
349+
345350
/**
346351
* An optional identification value to be appended to the user agent header. The value should be less than 50 characters in
347352
* length and is null by default.

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package software.amazon.awssdk.core.internal.useragent;
1717

18+
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.API_METADATA;
1819
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.APP_ID;
1920
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.ENV_METADATA;
2021
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.HTTP;
@@ -65,6 +66,7 @@ public static String buildClientUserAgentString(SystemUserAgent systemValues,
6566
}
6667

6768
appendNonEmptyField(uaString, UA_METADATA, UA_VERSION);
69+
appendNonEmptyField(uaString, API_METADATA, userAgentProperties.getProperty(API_METADATA));
6870
appendNonEmptyField(uaString, OS_METADATA, systemValues.osMetadata());
6971
appendNonEmptyField(uaString, LANG_METADATA, systemValues.langMetadata());
7072
appendAdditionalJvmMetadata(uaString, systemValues);

0 commit comments

Comments
 (0)