Skip to content

Add useragent for service and version #6212

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 1, 2025
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-e1e404e.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Adding the version of the used service to the useragent"
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import software.amazon.awssdk.codegen.poet.client.EnvironmentTokenSystemSettingsClass;
import software.amazon.awssdk.codegen.poet.client.SdkClientOptions;
import software.amazon.awssdk.codegen.poet.client.specs.ServiceVersionInfoSpec;
import software.amazon.awssdk.codegen.poet.client.specs.ServiceVersionUserAgentSpec;
import software.amazon.awssdk.codegen.poet.common.UserAgentUtilsSpec;

public class CommonInternalGeneratorTasks extends BaseGeneratorTasks {
Expand All @@ -42,6 +43,7 @@ protected List<GeneratorTask> createTasks() throws Exception {
tasks.add(createEnvironmentTokenSystemSettingTask());
}
tasks.add(createServiceVersionInfoTask());
tasks.add(createServiceVersionUserAgentTask());
return tasks;
}

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

private GeneratorTask createServiceVersionUserAgentTask() {
return new PoetGeneratorTask(clientOptionsDir(), params.getModel().getFileHeader(),
new ServiceVersionUserAgentSpec(params.getModel()));
}

private String clientOptionsDir() {
return params.getPathProvider().getClientInternalDirectory();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ public ClassName getServiceVersionInfoClass() {
return ClassName.get(model.getMetadata().getFullClientInternalPackageName(), "ServiceVersionInfo");
}

public ClassName getServiceVersionUserAgentClass() {
return ClassName.get(model.getMetadata().getFullClientInternalPackageName(), "ServiceVersionUserAgent");
}

public ClassName getEnvironmentTokenSystemSettingsClass() {
return ClassName.get(model.getMetadata().getFullClientInternalPackageName(), "EnvironmentTokenSystemSettings");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,13 @@ private MethodSpec constructor(TypeSpec.Builder classBuilder) {
.addStatement("this.clientHandler = new $T(clientConfiguration)", AwsAsyncClientHandler.class)
.addStatement("this.clientConfiguration = clientConfiguration.toBuilder()"
+ ".option($T.SDK_CLIENT, this)"
+ ".build()", SdkClientOption.class);
+ ".option($T.API_METADATA, $T.USER_AGENT)"
+ ".build()",
SdkClientOption.class,
SdkClientOption.class,
ClassName.get(model.getMetadata().getFullClientInternalPackageName(),
"ServiceVersionUserAgent"));

FieldSpec protocolFactoryField = protocolSpec.protocolFactory(model);
if (model.getMetadata().isJsonProtocol()) {
builder.addStatement("this.$N = init($T.builder()).build()", protocolFactoryField.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,12 @@ private MethodSpec constructor() {
.addStatement("this.clientHandler = new $T(clientConfiguration)", protocolSpec.getClientHandlerClass())
.addStatement("this.clientConfiguration = clientConfiguration.toBuilder()"
+ ".option($T.SDK_CLIENT, this)"
+ ".build()", SdkClientOption.class);
+ ".option($T.API_METADATA, $T.USER_AGENT)"
+ ".build()",
SdkClientOption.class,
SdkClientOption.class,
ClassName.get(model.getMetadata().getFullClientInternalPackageName(),
"ServiceVersionUserAgent"));

FieldSpec protocolFactoryField = protocolSpec.protocolFactory(model);
if (model.getMetadata().isJsonProtocol()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.codegen.poet.client.specs;

import static software.amazon.awssdk.core.util.VersionInfo.SDK_VERSION;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import javax.lang.model.element.Modifier;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.poet.ClassSpec;
import software.amazon.awssdk.codegen.poet.PoetExtension;
import software.amazon.awssdk.codegen.poet.PoetUtils;


public class ServiceVersionUserAgentSpec implements ClassSpec {
private final PoetExtension poetExtension;
private final IntermediateModel model;

public ServiceVersionUserAgentSpec(IntermediateModel model) {
this.poetExtension = new PoetExtension(model);
this.model = model;
}

@Override
public TypeSpec poetSpec() {
TypeSpec.Builder builder = TypeSpec.classBuilder("ServiceVersionUserAgent")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addAnnotation(PoetUtils.generatedAnnotation())
.addField(userAgentField())
.addMethod(privateConstructor());
return builder.build();
}

private String transformServiceId(String serviceId) {
// According to User Agent 2.0 spec, replace spaces with underscores
return serviceId.replace(" ", "_");
}

private FieldSpec userAgentField() {
return FieldSpec.builder(String.class, "USER_AGENT", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer("$S", transformServiceId(model.getMetadata().getServiceId()) + "#" + SDK_VERSION)
.addJavadoc("Returns a user agent containing the service and "
+ "version info")
.build();

}

protected MethodSpec privateConstructor() {
return MethodSpec.constructorBuilder()
.addModifiers(Modifier.PRIVATE)
.build();
}

@Override
public ClassName className() {
return poetExtension.getServiceVersionUserAgentClass();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.codegen.poet.client;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import java.io.InputStream;
import java.util.Scanner;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.codegen.poet.ClassSpec;
import software.amazon.awssdk.codegen.poet.ClientTestModels;
import software.amazon.awssdk.codegen.poet.client.specs.ServiceVersionUserAgentSpec;
import software.amazon.awssdk.core.util.VersionInfo;

public class ServiceVersionUserAgentSpecTest {

// Fixture test that compares generated ServiceVersionUserAgent class against expected output.
// The fixture file uses {{VERSION}} as a placeholder which gets replaced with the current
// SDK version at test time, since the generated code injects the actual version at build time.
@Test
void testServiceVersionUserAgentClass() {
String currVersion = VersionInfo.SDK_VERSION;
ClassSpec serviceVersionUserAgentSpec = new ServiceVersionUserAgentSpec(ClientTestModels.restJsonServiceModels());

String expectedContent = loadFixtureFile("test-service-version-user-agent-class.java");
expectedContent = expectedContent.replace("{{VERSION}}", currVersion);

String actualContent = generateContent(serviceVersionUserAgentSpec);

assertThat(actualContent).isEqualToIgnoringWhitespace(expectedContent);
}

private String loadFixtureFile(String filename) {
InputStream is = getClass().getResourceAsStream("specs/" + filename);
return new Scanner(is).useDelimiter("\\A").next();
}

private String generateContent(ClassSpec spec) {
TypeSpec typeSpec = spec.poetSpec();
JavaFile javaFile = JavaFile.builder(spec.className().packageName(), typeSpec).build();
return javaFile.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package software.amazon.awssdk.services.json.internal;

import java.lang.String;
import software.amazon.awssdk.annotations.Generated;

@Generated("software.amazon.awssdk:codegen")
public final class ServiceVersionUserAgent {
/**
* Returns a user agent containing the service and version info
*/
public static final String USER_AGENT = "Json_Service#{{VERSION}}";

private ServiceVersionUserAgent() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import software.amazon.awssdk.protocols.json.JsonOperationMetadata;
import software.amazon.awssdk.retries.api.RetryStrategy;
import software.amazon.awssdk.services.json.internal.JsonServiceClientConfigurationBuilder;
import software.amazon.awssdk.services.json.internal.ServiceVersionUserAgent;
import software.amazon.awssdk.services.json.model.APostOperationRequest;
import software.amazon.awssdk.services.json.model.APostOperationResponse;
import software.amazon.awssdk.services.json.model.APostOperationWithOutputRequest;
Expand Down Expand Up @@ -140,7 +141,8 @@ final class DefaultJsonAsyncClient implements JsonAsyncClient {

protected DefaultJsonAsyncClient(SdkClientConfiguration clientConfiguration) {
this.clientHandler = new AwsAsyncClientHandler(clientConfiguration);
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this).build();
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this)
.option(SdkClientOption.API_METADATA, ServiceVersionUserAgent.USER_AGENT).build();
this.protocolFactory = init(AwsJsonProtocolFactory.builder()).build();
this.executor = clientConfiguration.option(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import software.amazon.awssdk.protocols.json.JsonOperationMetadata;
import software.amazon.awssdk.retries.api.RetryStrategy;
import software.amazon.awssdk.services.json.internal.JsonServiceClientConfigurationBuilder;
import software.amazon.awssdk.services.json.internal.ServiceVersionUserAgent;
import software.amazon.awssdk.services.json.model.APostOperationRequest;
import software.amazon.awssdk.services.json.model.APostOperationResponse;
import software.amazon.awssdk.services.json.model.APostOperationWithOutputRequest;
Expand Down Expand Up @@ -143,7 +144,8 @@ final class DefaultJsonAsyncClient implements JsonAsyncClient {

protected DefaultJsonAsyncClient(SdkClientConfiguration clientConfiguration) {
this.clientHandler = new AwsAsyncClientHandler(clientConfiguration);
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this).build();
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this)
.option(SdkClientOption.API_METADATA, ServiceVersionUserAgent.USER_AGENT).build();
this.protocolFactory = init(AwsCborProtocolFactory.builder()).build();
this.jsonProtocolFactory = init(AwsJsonProtocolFactory.builder()).build();
this.executor = clientConfiguration.option(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import software.amazon.awssdk.retries.api.RetryStrategy;
import software.amazon.awssdk.services.json.batchmanager.JsonAsyncBatchManager;
import software.amazon.awssdk.services.json.internal.JsonServiceClientConfigurationBuilder;
import software.amazon.awssdk.services.json.internal.ServiceVersionUserAgent;
import software.amazon.awssdk.services.json.model.APostOperationRequest;
import software.amazon.awssdk.services.json.model.APostOperationResponse;
import software.amazon.awssdk.services.json.model.APostOperationWithOutputRequest;
Expand Down Expand Up @@ -153,7 +154,8 @@ final class DefaultJsonAsyncClient implements JsonAsyncClient {

protected DefaultJsonAsyncClient(SdkClientConfiguration clientConfiguration) {
this.clientHandler = new AwsAsyncClientHandler(clientConfiguration);
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this).build();
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this)
.option(SdkClientOption.API_METADATA, ServiceVersionUserAgent.USER_AGENT).build();
this.protocolFactory = init(AwsJsonProtocolFactory.builder()).build();
this.executor = clientConfiguration.option(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR);
this.executorService = clientConfiguration.option(SdkClientOption.SCHEDULED_EXECUTOR_SERVICE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import software.amazon.awssdk.protocols.json.JsonOperationMetadata;
import software.amazon.awssdk.retries.api.RetryStrategy;
import software.amazon.awssdk.services.json.internal.JsonServiceClientConfigurationBuilder;
import software.amazon.awssdk.services.json.internal.ServiceVersionUserAgent;
import software.amazon.awssdk.services.json.model.APostOperationRequest;
import software.amazon.awssdk.services.json.model.APostOperationResponse;
import software.amazon.awssdk.services.json.model.APostOperationWithOutputRequest;
Expand Down Expand Up @@ -108,7 +109,8 @@ final class DefaultJsonClient implements JsonClient {

protected DefaultJsonClient(SdkClientConfiguration clientConfiguration) {
this.clientHandler = new AwsSyncClientHandler(clientConfiguration);
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this).build();
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this)
.option(SdkClientOption.API_METADATA, ServiceVersionUserAgent.USER_AGENT).build();
this.protocolFactory = init(AwsJsonProtocolFactory.builder()).build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import software.amazon.awssdk.protocols.query.AwsQueryProtocolFactory;
import software.amazon.awssdk.retries.api.RetryStrategy;
import software.amazon.awssdk.services.query.internal.QueryServiceClientConfigurationBuilder;
import software.amazon.awssdk.services.query.internal.ServiceVersionUserAgent;
import software.amazon.awssdk.services.query.model.APostOperationRequest;
import software.amazon.awssdk.services.query.model.APostOperationResponse;
import software.amazon.awssdk.services.query.model.APostOperationWithOutputRequest;
Expand Down Expand Up @@ -122,7 +123,8 @@ final class DefaultQueryAsyncClient implements QueryAsyncClient {

protected DefaultQueryAsyncClient(SdkClientConfiguration clientConfiguration) {
this.clientHandler = new AwsAsyncClientHandler(clientConfiguration);
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this).build();
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this)
.option(SdkClientOption.API_METADATA, ServiceVersionUserAgent.USER_AGENT).build();
this.protocolFactory = init();
this.executorService = clientConfiguration.option(SdkClientOption.SCHEDULED_EXECUTOR_SERVICE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import software.amazon.awssdk.protocols.query.AwsQueryProtocolFactory;
import software.amazon.awssdk.retries.api.RetryStrategy;
import software.amazon.awssdk.services.query.internal.QueryServiceClientConfigurationBuilder;
import software.amazon.awssdk.services.query.internal.ServiceVersionUserAgent;
import software.amazon.awssdk.services.query.model.APostOperationRequest;
import software.amazon.awssdk.services.query.model.APostOperationResponse;
import software.amazon.awssdk.services.query.model.APostOperationWithOutputRequest;
Expand Down Expand Up @@ -113,7 +114,8 @@ final class DefaultQueryClient implements QueryClient {

protected DefaultQueryClient(SdkClientConfiguration clientConfiguration) {
this.clientHandler = new AwsSyncClientHandler(clientConfiguration);
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this).build();
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this)
.option(SdkClientOption.API_METADATA, ServiceVersionUserAgent.USER_AGENT).build();
this.protocolFactory = init();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import software.amazon.awssdk.protocols.xml.AwsXmlProtocolFactory;
import software.amazon.awssdk.protocols.xml.XmlOperationMetadata;
import software.amazon.awssdk.retries.api.RetryStrategy;
import software.amazon.awssdk.services.xml.internal.ServiceVersionUserAgent;
import software.amazon.awssdk.services.xml.internal.XmlServiceClientConfigurationBuilder;
import software.amazon.awssdk.services.xml.model.APostOperationRequest;
import software.amazon.awssdk.services.xml.model.APostOperationResponse;
Expand Down Expand Up @@ -114,7 +115,8 @@ final class DefaultXmlAsyncClient implements XmlAsyncClient {

protected DefaultXmlAsyncClient(SdkClientConfiguration clientConfiguration) {
this.clientHandler = new AwsAsyncClientHandler(clientConfiguration);
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this).build();
this.clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.SDK_CLIENT, this)
.option(SdkClientOption.API_METADATA, ServiceVersionUserAgent.USER_AGENT).build();
this.protocolFactory = init();
this.executor = clientConfiguration.option(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR);
}
Expand Down
Loading
Loading