From 51a754c713e73e877ccccac087931dec7fdae9ba Mon Sep 17 00:00:00 2001 From: Anna-Karin Salander Date: Tue, 1 Oct 2024 11:29:21 -0700 Subject: [PATCH 1/4] Adds the user agent appId metadata tag to client and system configuration options --- .../feature-AWSSDKforJavav2-0277feb.json | 6 + .../awssdk/profiles/ProfileProperty.java | 6 + .../amazon/awssdk/core/SdkSystemSetting.java | 8 +- .../core/client/builder/SdkClientBuilder.java | 21 ++++ .../builder/SdkDefaultClientBuilder.java | 26 +++- .../internal/useragent/AppIdResolver.java | 66 ++++++++++ .../useragent/SdkUserAgentBuilder.java | 6 + .../internal/useragent/UserAgentConstant.java | 1 + .../internal/useragent/AppIdResolverTest.java | 114 +++++++++++++++++ .../useragent/AppIdUserAgentTest.java | 117 ++++++++++++++++++ .../InternalUserAgentTest.java | 2 +- 11 files changed, 368 insertions(+), 5 deletions(-) create mode 100644 .changes/next-release/feature-AWSSDKforJavav2-0277feb.json create mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/AppIdResolver.java create mode 100644 core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/AppIdResolverTest.java create mode 100644 test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/useragent/AppIdUserAgentTest.java rename test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/{customizeduseragent => useragent}/InternalUserAgentTest.java (98%) diff --git a/.changes/next-release/feature-AWSSDKforJavav2-0277feb.json b/.changes/next-release/feature-AWSSDKforJavav2-0277feb.json new file mode 100644 index 000000000000..898e23895f2c --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-0277feb.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "Adds an option to set 'appId' metadata to the client builder or to system settings and config files. This metadata string value will be added to the user agent string as `app/somevalue`" +} diff --git a/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileProperty.java b/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileProperty.java index 4e7e760eb607..0ec8103ff87d 100644 --- a/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileProperty.java +++ b/core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileProperty.java @@ -177,6 +177,12 @@ public final class ProfileProperty { */ public static final String ENDPOINT_URL = "endpoint_url"; + /** + * Configure an optional identification value to be appended to the user agent header. + * The value should be less than 50 characters in length and is null by default. + */ + public static final String SDK_UA_APP_ID = "sdk_ua_app_id"; + private ProfileProperty() { } } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/SdkSystemSetting.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/SdkSystemSetting.java index e995165fdcc9..87af58b9ad7b 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/SdkSystemSetting.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/SdkSystemSetting.java @@ -224,7 +224,13 @@ public enum SdkSystemSetting implements SystemSetting { * Defines a file path from which partition metadata should be loaded. If this isn't specified, the partition * metadata deployed with the SDK client will be used instead. */ - AWS_PARTITIONS_FILE("aws.partitionsFile", null) + AWS_PARTITIONS_FILE("aws.partitionsFile", null), + + /** + * Configure an optional identification value to be appended to the user agent header. + * The value should be less than 50 characters in length and is null by default. + */ + AWS_SDK_UA_APP_ID("sdk.ua.appId", null) ; diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkClientBuilder.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkClientBuilder.java index d106d6d27879..f984105f898b 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkClientBuilder.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkClientBuilder.java @@ -22,6 +22,7 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.utils.builder.SdkBuilder; @@ -95,4 +96,24 @@ default B addPlugin(SdkPlugin plugin) { default List plugins() { throw new UnsupportedOperationException(); } + + /** + * Configure an optional identification value to be appended to the user agent header. + * The value should be less than 50 characters in length and is null by default. + *

+ * Users can additionally supply the appId value through environment and JVM settings, and + * it will be resolved using the following order of precedence (highest first): + *

    + *
  1. This client builder configuration
  2. + *
  3. The {@code AWS_SDK_UA_APP_ID} environment variable
  4. + *
  5. The {@code sdk.ua.appId} JVM system property
  6. + *
  7. The {@code sdk_ua_app_id} setting in the profile file for the active profile
  8. + *
+ *

+ * This configuration option supersedes {@link SdkAdvancedClientOption#USER_AGENT_PREFIX} and + * {@link SdkAdvancedClientOption#USER_AGENT_SUFFIX} and should be used instead of those options. + */ + default B appId(String appId) { + throw new UnsupportedOperationException(); + } } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java index 7dd7e99a0c90..055f7565284d 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java @@ -48,6 +48,7 @@ import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_STRATEGY; import static software.amazon.awssdk.core.client.config.SdkClientOption.SCHEDULED_EXECUTOR_SERVICE; import static software.amazon.awssdk.core.client.config.SdkClientOption.SYNC_HTTP_CLIENT; +import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.APP_ID; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.HTTP; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.INTERNAL_METADATA_MARKER; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.IO; @@ -92,6 +93,7 @@ import software.amazon.awssdk.core.internal.retry.SdkDefaultRetryStrategy; import software.amazon.awssdk.core.internal.useragent.SdkClientUserAgentProperties; import software.amazon.awssdk.core.internal.useragent.SdkUserAgentBuilder; +import software.amazon.awssdk.core.internal.useragent.AppIdResolver; import software.amazon.awssdk.core.retry.RetryMode; import software.amazon.awssdk.core.retry.RetryPolicy; import software.amazon.awssdk.core.util.SystemUserAgent; @@ -149,7 +151,7 @@ public abstract class SdkDefaultClientBuilder, private final SdkHttpClient.Builder defaultHttpClientBuilder; private final SdkAsyncHttpClient.Builder defaultAsyncHttpClientBuilder; private final List plugins = new ArrayList<>(); - + private String appId; protected SdkDefaultClientBuilder() { @@ -422,10 +424,18 @@ private String resolveClientUserAgent(LazyValueSource config) { clientProperties.putProperty(HTTP, SdkHttpUtils.urlEncode(clientName(resolvedClientType, config.get(SYNC_HTTP_CLIENT), config.get(ASYNC_HTTP_CLIENT)))); - + clientProperties.putProperty(APP_ID, appId().orElseGet(() -> resolveAppId(config))); return SdkUserAgentBuilder.buildClientUserAgentString(SystemUserAgent.getOrCreate(), clientProperties); } + private String resolveAppId(LazyValueSource config) { + Optional appIdFromConfig = AppIdResolver.create() + .profileFile(config.get(PROFILE_FILE_SUPPLIER)) + .profileName(config.get(PROFILE_NAME)) + .resolve(); + return appIdFromConfig.orElse(null); + } + private static String clientName(ClientType clientType, SdkHttpClient syncHttpClient, SdkAsyncHttpClient asyncHttpClient) { if (clientType == SYNC) { return syncHttpClient == null ? "null" : syncHttpClient.clientName(); @@ -446,7 +456,11 @@ private RetryStrategy resolveRetryStrategy(LazyValueSource config) { .resolve(); return SdkDefaultRetryStrategy.forRetryMode(retryMode); } - + + public Optional appId() { + return Optional.ofNullable(appId); + } + /** * Finalize which sync HTTP client will be used for the created client. */ @@ -644,6 +658,12 @@ public final List plugins() { return Collections.unmodifiableList(plugins); } + @Override + public final B appId(String appId) { + this.appId = appId; + return thisBuilder(); + } + /** * Return "this" for method chaining. */ diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/AppIdResolver.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/AppIdResolver.java new file mode 100644 index 000000000000..b4577769b57b --- /dev/null +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/AppIdResolver.java @@ -0,0 +1,66 @@ +/* + * 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.core.internal.useragent; + +import java.util.Optional; +import java.util.function.Supplier; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.profiles.ProfileFile; +import software.amazon.awssdk.profiles.ProfileFileSystemSetting; +import software.amazon.awssdk.profiles.ProfileProperty; +import software.amazon.awssdk.utils.OptionalUtils; + +@SdkInternalApi +public final class AppIdResolver { + + private Supplier profileFile; + private String profileName; + + private AppIdResolver() { + } + + public static AppIdResolver create() { + return new AppIdResolver(); + } + + public AppIdResolver profileFile(Supplier profileFile) { + this.profileFile = profileFile; + return this; + } + + public AppIdResolver profileName(String profileName) { + this.profileName = profileName; + return this; + } + + public Optional resolve() { + return OptionalUtils.firstPresent(fromSystemSettings(), + () -> fromProfileFile(profileFile, profileName)); + } + + private Optional fromSystemSettings() { + return SdkSystemSetting.AWS_SDK_UA_APP_ID.getStringValue(); + } + + private Optional fromProfileFile(Supplier profileFile, String profileName) { + profileFile = profileFile != null ? profileFile : ProfileFile::defaultProfileFile; + profileName = profileName != null ? profileName : ProfileFileSystemSetting.AWS_PROFILE.getStringValueOrThrow(); + return profileFile.get() + .profile(profileName) + .flatMap(p -> p.property(ProfileProperty.SDK_UA_APP_ID)); + } +} diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java index 213653cbd307..7e5178ee25fa 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java @@ -15,6 +15,7 @@ package software.amazon.awssdk.core.internal.useragent; +import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.APP_ID; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.CONFIG_METADATA; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.ENV_METADATA; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.HTTP; @@ -77,6 +78,11 @@ public static String buildClientUserAgentString(SystemUserAgent systemValues, appendFieldAndSpace(uaString, CONFIG_METADATA, uaPair(RETRY_MODE, retryMode)); } + String appId = userAgentProperties.getProperty(APP_ID); + if (!StringUtils.isEmpty(appId)) { + appendFieldAndSpace(uaString, APP_ID, appId); + } + removeFinalWhitespace(uaString); return uaString.toString(); } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/UserAgentConstant.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/UserAgentConstant.java index 179f54df965b..80f235d267f4 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/UserAgentConstant.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/UserAgentConstant.java @@ -36,6 +36,7 @@ public final class UserAgentConstant { public static final String FRAMEWORK_METADATA = "lib"; public static final String METADATA = "md"; public static final String INTERNAL_METADATA_MARKER = "internal"; + public static final String APP_ID = "app"; //Separators used in SDK user agent public static final String SLASH = "/"; diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/AppIdResolverTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/AppIdResolverTest.java new file mode 100644 index 000000000000..86c054ccf26d --- /dev/null +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/AppIdResolverTest.java @@ -0,0 +1,114 @@ +/* + * 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.core.internal.useragent; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.profiles.ProfileFile; +import software.amazon.awssdk.profiles.ProfileProperty; +import software.amazon.awssdk.testutils.EnvironmentVariableHelper; +import software.amazon.awssdk.utils.Pair; +import software.amazon.awssdk.utils.StringInputStream; +import software.amazon.awssdk.utils.StringUtils; + +class AppIdResolverTest { + + private static final EnvironmentVariableHelper ENVIRONMENT_VARIABLE_HELPER = new EnvironmentVariableHelper(); + private static final String PROFILE = "test"; + + @AfterEach + public void cleanup() { + ENVIRONMENT_VARIABLE_HELPER.reset(); + System.clearProperty(SdkSystemSetting.AWS_SDK_UA_APP_ID.property()); + } + + @ParameterizedTest(name = "{index} - {0}") + @MethodSource("inputValues") + void resolveAppIdFromEnvironment(String description, String systemProperty, String envVar, + ProfileFile profileFile, String expected) { + + setUpSystemSettings(systemProperty, envVar); + + AppIdResolver resolver = AppIdResolver.create().profileName(PROFILE); + if (profileFile != null) { + resolver.profileFile(() -> profileFile); + } + + if (expected != null) { + assertThat(resolver.resolve()).isNotEmpty().contains(expected); + } else { + assertThat(resolver.resolve()).isEmpty(); + } + } + + private static Stream inputValues() { + ProfileFile emptyProfile = configFile("profile test", Pair.of("foo", "bar")); + + Function testProfileConfig = + s -> configFile("profile test", Pair.of(ProfileProperty.SDK_UA_APP_ID, s)); + + return Stream.of( + Arguments.of("Without input, resolved value is null", null, null, null, null), + Arguments.of("Setting system property only gives result", "SystemPropertyAppId", null, null, "SystemPropertyAppId"), + Arguments.of("Setting env var only gives result", null, "EnvVarAppId", null, "EnvVarAppId"), + Arguments.of("System property takes precedence over env var", "SystemPropertyAppId", "EnvVarAppId", null, + "SystemPropertyAppId"), + Arguments.of("Setting profile file only gives result", null, null, testProfileConfig.apply("profileAppId"), + "profileAppId"), + Arguments.of("When profile file exists but has no input, resolved value is null", null, null, emptyProfile, null), + Arguments.of("System property takes precedence over profile file", "SystemPropertyAppId", null, + testProfileConfig.apply("profileAppId"), "SystemPropertyAppId"), + Arguments.of("Env var takes precedence over profile file", null, "EnvVarAppId", + testProfileConfig.apply("profileAppId"), "EnvVarAppId"), + Arguments.of("System prop var takes precedence over profile file", null, "EnvVarAppId", + testProfileConfig.apply("profileAppId"), "EnvVarAppId") + ); + } + + private static void setUpSystemSettings(String systemProperty, String envVar) { + if (!StringUtils.isEmpty(systemProperty)) { + System.setProperty(SdkSystemSetting.AWS_SDK_UA_APP_ID.property(), systemProperty); + } + if (!StringUtils.isEmpty(envVar)) { + ENVIRONMENT_VARIABLE_HELPER.set(SdkSystemSetting.AWS_SDK_UA_APP_ID.environmentVariable(), envVar); + } + } + + private static ProfileFile configFile(String name, Pair... pairs) { + String values = Arrays.stream(pairs) + .map(pair -> String.format("%s=%s", pair.left(), pair.right())) + .collect(Collectors.joining(System.lineSeparator())); + String contents = String.format("[%s]\n%s", name, values); + + return configFile(contents); + } + + private static ProfileFile configFile(String credentialFile) { + return ProfileFile.builder() + .content(new StringInputStream(credentialFile)) + .type(ProfileFile.Type.CONFIGURATION) + .build(); + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/useragent/AppIdUserAgentTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/useragent/AppIdUserAgentTest.java new file mode 100644 index 000000000000..07b413c1848d --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/useragent/AppIdUserAgentTest.java @@ -0,0 +1,117 @@ +/* + * 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.services.useragent; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersClient; +import software.amazon.awssdk.services.restjsonendpointproviders.RestJsonEndpointProvidersClientBuilder; +import software.amazon.awssdk.utils.StringUtils; + +class AppIdUserAgentTest { + private CapturingInterceptor interceptor; + + private static final String USER_AGENT_HEADER_NAME = "User-Agent"; + + @BeforeEach + public void setup() { + this.interceptor = new CapturingInterceptor(); + } + + @AfterEach + public void cleanup() { + System.clearProperty(SdkSystemSetting.AWS_SDK_UA_APP_ID.property()); + } + + @ParameterizedTest(name = "{index} - {0}") + @MethodSource("inputValues") + void resolveAppIdFromEnvironment(String description, String clientAppId, String systemProperty, String expected) { + if (!StringUtils.isEmpty(systemProperty)) { + System.setProperty(SdkSystemSetting.AWS_SDK_UA_APP_ID.property(), systemProperty); + } + + RestJsonEndpointProvidersClientBuilder clientBuilder = syncClientBuilder(); + + if (!StringUtils.isEmpty(clientAppId)) { + clientBuilder.appId(clientAppId); + } + + assertThatThrownBy(() -> clientBuilder.build().allTypes(r -> {})) + .hasMessageContaining("stop"); + + Map> headers = interceptor.context.httpRequest().headers(); + assertThat(headers).containsKey(USER_AGENT_HEADER_NAME); + String userAgent = headers.get(USER_AGENT_HEADER_NAME).get(0); + + if (expected != null) { + assertThat(userAgent).contains("app/" + expected); + } else { + assertThat(userAgent).doesNotContain("app/"); + } + } + + private static Stream inputValues() { + return Stream.of( + Arguments.of("Without appId input, nothing is added to user agent", null, null, null), + Arguments.of("Values resolved from environment are propagated to user agent", null, + "SystemPropertyAppId", "SystemPropertyAppId"), + Arguments.of("Client value is propagated to user agent", "ClientAppId", null, "ClientAppId"), + Arguments.of("Client value takes precedence over environment values", "ClientAppId", "SystemPropertyAppId", + "ClientAppId") + ); + } + + private RestJsonEndpointProvidersClientBuilder syncClientBuilder() { + return RestJsonEndpointProvidersClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create("akid", "skid"))) + .overrideConfiguration(c -> c.addExecutionInterceptor(interceptor)); + } + + public static class CapturingInterceptor implements ExecutionInterceptor { + private Context.BeforeTransmission context; + private ExecutionAttributes executionAttributes; + + @Override + public void beforeTransmission(Context.BeforeTransmission context, ExecutionAttributes executionAttributes) { + this.context = context; + this.executionAttributes = executionAttributes; + throw new RuntimeException("stop"); + } + + public ExecutionAttributes executionAttributes() { + return executionAttributes; + } + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/customizeduseragent/InternalUserAgentTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/useragent/InternalUserAgentTest.java similarity index 98% rename from test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/customizeduseragent/InternalUserAgentTest.java rename to test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/useragent/InternalUserAgentTest.java index ccb2f28f8b10..8462ff843a84 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/customizeduseragent/InternalUserAgentTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/useragent/InternalUserAgentTest.java @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -package software.amazon.awssdk.services.customizeduseragent; +package software.amazon.awssdk.services.useragent; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; From 2a8c90002ee5dbe079922b17a1b9d2cfdbee2d65 Mon Sep 17 00:00:00 2001 From: Anna-Karin Salander Date: Tue, 1 Oct 2024 13:35:18 -0700 Subject: [PATCH 2/4] appId length check --- .../useragent/SdkUserAgentBuilder.java | 13 ++++++++ ...lverTest.java => AppIdResolutionTest.java} | 3 +- .../useragent/SdkUserAgentBuilderTest.java | 30 +++++++++++++------ 3 files changed, 36 insertions(+), 10 deletions(-) rename core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/{AppIdResolverTest.java => AppIdResolutionTest.java} (99%) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java index 7e5178ee25fa..a844c265be7e 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java @@ -34,7 +34,9 @@ import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.core.internal.http.pipeline.stages.ApplyUserAgentStage; import software.amazon.awssdk.core.util.SystemUserAgent; +import software.amazon.awssdk.utils.Logger; import software.amazon.awssdk.utils.StringUtils; /** @@ -44,6 +46,8 @@ @SdkProtectedApi public final class SdkUserAgentBuilder { + private static final Logger log = Logger.loggerFor(SdkUserAgentBuilder.class); + private SdkUserAgentBuilder() { } @@ -80,6 +84,7 @@ public static String buildClientUserAgentString(SystemUserAgent systemValues, String appId = userAgentProperties.getProperty(APP_ID); if (!StringUtils.isEmpty(appId)) { + checkLengthAndWarn(appId); appendFieldAndSpace(uaString, APP_ID, appId); } @@ -130,4 +135,12 @@ private static void appendAdditionalJvmMetadata(StringBuilder builder, SystemUse appendNonEmptyField(builder, METADATA, lang); } } + + private static void checkLengthAndWarn(String appId) { + if (appId.length() > 50) { + log.warn(() -> String.format("The configured appId '%s' is longer than the recommended maximum length of 50. " + + "This could result in not being able to transmit and log the whole user agent string, " + + "including the complete value of this string.", appId)); + } + } } diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/AppIdResolverTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/AppIdResolutionTest.java similarity index 99% rename from core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/AppIdResolverTest.java rename to core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/AppIdResolutionTest.java index 86c054ccf26d..158302e6540d 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/AppIdResolverTest.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/AppIdResolutionTest.java @@ -21,6 +21,7 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.junit.Test; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -33,7 +34,7 @@ import software.amazon.awssdk.utils.StringInputStream; import software.amazon.awssdk.utils.StringUtils; -class AppIdResolverTest { +class AppIdResolutionTest { private static final EnvironmentVariableHelper ENVIRONMENT_VARIABLE_HELPER = new EnvironmentVariableHelper(); private static final String PROFILE = "test"; diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilderTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilderTest.java index d8f9343843a8..9df34a525a5c 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilderTest.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilderTest.java @@ -16,6 +16,7 @@ package software.amazon.awssdk.core.internal.useragent; import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.APP_ID; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.HTTP; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.INTERNAL_METADATA_MARKER; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.IO; @@ -50,8 +51,8 @@ private static Stream inputValues() { "OpenJDK_64-Bit_Server_VM#21.0.2+13-LTS", "vendor#Amazon.com_Inc.", "en_US", Arrays.asList("Kotlin", "Scala")); - SdkClientUserAgentProperties minimalProperties = sdkProperties(null, null, null, null); - SdkClientUserAgentProperties maximalProperties = sdkProperties("standard", "arbitrary", "async", "Netty"); + SdkClientUserAgentProperties minimalProperties = sdkProperties(null, null, null, null, null); + SdkClientUserAgentProperties maximalProperties = sdkProperties("standard", "arbitrary", "async", "Netty", "someAppId"); return Stream.of( Arguments.of("default sysagent, empty requestvalues", @@ -62,40 +63,47 @@ private static Stream inputValues() { "aws-sdk-java/2.26.22-SNAPSHOT ua/2.0 os/Mac_OS_X#14.6.1 lang/java#21.0.2 " + "md/OpenJDK_64-Bit_Server_VM#21.0.2+13-LTS md/vendor#Amazon.com_Inc. md/en_US md/Kotlin md/Scala " + "exec-env/lambda cfg/retry-mode#standard", - sdkProperties("standard", null, null, null), + sdkProperties("standard", null, null, null, null), maximalSysAgent), Arguments.of("standard sysagent, request values - internalMarker", "aws-sdk-java/2.26.22-SNAPSHOT md/internal ua/2.0 os/Mac_OS_X#14.6.1 lang/java#21.0.2 " + "md/OpenJDK_64-Bit_Server_VM#21.0.2+13-LTS md/vendor#Amazon.com_Inc. md/en_US md/Kotlin md/Scala exec-env/lambda", - sdkProperties(null, "arbitrary", null, null), + sdkProperties(null, "arbitrary", null, null, null), maximalSysAgent), Arguments.of("standard sysagent, request values - io", "aws-sdk-java/2.26.22-SNAPSHOT md/io#async ua/2.0 os/Mac_OS_X#14.6.1 lang/java#21.0.2 " + "md/OpenJDK_64-Bit_Server_VM#21.0.2+13-LTS md/vendor#Amazon.com_Inc. md/en_US md/Kotlin md/Scala exec-env/lambda", - sdkProperties(null, null, "async", null), + sdkProperties(null, null, "async", null, null), maximalSysAgent), Arguments.of("standard sysagent, request values - http", "aws-sdk-java/2.26.22-SNAPSHOT md/http#Apache ua/2.0 os/Mac_OS_X#14.6.1 lang/java#21.0.2 " + "md/OpenJDK_64-Bit_Server_VM#21.0.2+13-LTS md/vendor#Amazon.com_Inc. md/en_US md/Kotlin md/Scala exec-env/lambda", - sdkProperties(null, null, null, "Apache"), + sdkProperties(null, null, null, "Apache", null), maximalSysAgent), Arguments.of("standard sysagent, request values - authSource", "aws-sdk-java/2.26.22-SNAPSHOT ua/2.0 os/Mac_OS_X#14.6.1 lang/java#21.0.2 " + "md/OpenJDK_64-Bit_Server_VM#21.0.2+13-LTS md/vendor#Amazon.com_Inc. md/en_US md/Kotlin md/Scala " + "exec-env/lambda", - sdkProperties(null, null, null, null), + sdkProperties(null, null, null, null, null), + maximalSysAgent), + Arguments.of("standard sysagent, request values - appId", + "aws-sdk-java/2.26.22-SNAPSHOT ua/2.0 os/Mac_OS_X#14.6.1 lang/java#21.0.2 " + + "md/OpenJDK_64-Bit_Server_VM#21.0.2+13-LTS md/vendor#Amazon.com_Inc. md/en_US md/Kotlin md/Scala " + + "exec-env/lambda app/someAppId", + sdkProperties(null, null, null, null, "someAppId"), maximalSysAgent), Arguments.of("standard sysagent, request values - maximal", "aws-sdk-java/2.26.22-SNAPSHOT md/io#async md/http#Netty md/internal ua/2.0 os/Mac_OS_X#14.6.1 " + "lang/java#21.0.2 " + "md/OpenJDK_64-Bit_Server_VM#21.0.2+13-LTS md/vendor#Amazon.com_Inc. md/en_US md/Kotlin md/Scala " - + "exec-env/lambda cfg/retry-mode#standard", + + "exec-env/lambda cfg/retry-mode#standard app/someAppId", maximalProperties, maximalSysAgent) ); } - private static SdkClientUserAgentProperties sdkProperties(String retryMode, String internalMarker, String io, String http) { + private static SdkClientUserAgentProperties sdkProperties(String retryMode, String internalMarker, String io, + String http, String appId) { SdkClientUserAgentProperties properties = new SdkClientUserAgentProperties(); if (retryMode != null) { @@ -114,6 +122,10 @@ private static SdkClientUserAgentProperties sdkProperties(String retryMode, Stri properties.putProperty(HTTP, http); } + if (appId != null) { + properties.putProperty(APP_ID, appId); + } + return properties; } From 3819b2df60fe6e9a6e48a959d60844bf73198b9d Mon Sep 17 00:00:00 2001 From: Anna-Karin Salander Date: Thu, 3 Oct 2024 10:55:58 -0700 Subject: [PATCH 3/4] Fix imports --- .../awssdk/core/client/builder/SdkDefaultClientBuilder.java | 2 +- .../awssdk/core/internal/useragent/SdkUserAgentBuilder.java | 1 - .../awssdk/core/client/builder/DefaultClientBuilderTest.java | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java index 055f7565284d..965b060e1eda 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java @@ -91,9 +91,9 @@ import software.amazon.awssdk.core.internal.http.pipeline.stages.CompressRequestStage; import software.amazon.awssdk.core.internal.interceptor.HttpChecksumValidationInterceptor; import software.amazon.awssdk.core.internal.retry.SdkDefaultRetryStrategy; +import software.amazon.awssdk.core.internal.useragent.AppIdResolver; import software.amazon.awssdk.core.internal.useragent.SdkClientUserAgentProperties; import software.amazon.awssdk.core.internal.useragent.SdkUserAgentBuilder; -import software.amazon.awssdk.core.internal.useragent.AppIdResolver; import software.amazon.awssdk.core.retry.RetryMode; import software.amazon.awssdk.core.retry.RetryPolicy; import software.amazon.awssdk.core.util.SystemUserAgent; diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java index a844c265be7e..c2e311c92a2f 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java @@ -34,7 +34,6 @@ import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.core.internal.http.pipeline.stages.ApplyUserAgentStage; import software.amazon.awssdk.core.util.SystemUserAgent; import software.amazon.awssdk.utils.Logger; import software.amazon.awssdk.utils.StringUtils; diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/builder/DefaultClientBuilderTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/builder/DefaultClientBuilderTest.java index 25e83b36383c..9a9b8c8a8c3d 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/builder/DefaultClientBuilderTest.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/builder/DefaultClientBuilderTest.java @@ -382,7 +382,7 @@ public void explicitAsyncHttpClientProvided_ClientIsNotManagedBySdk() { public void clientBuilderFieldsHaveBeanEquivalents() throws Exception { // Mutating properties might not have bean equivalents. This is probably fine, since very few customers require // bean-equivalent methods and it's not clear what they'd expect them to be named anyway. Ignore these methods for now. - Set NON_BEAN_EQUIVALENT_METHODS = ImmutableSet.of("addPlugin", "plugins", "putAuthScheme"); + Set NON_BEAN_EQUIVALENT_METHODS = ImmutableSet.of("addPlugin", "plugins", "putAuthScheme", "appId"); SdkClientBuilder builder = testClientBuilder(); BeanInfo beanInfo = Introspector.getBeanInfo(builder.getClass()); From 633dd51a221ebe03ded75d9c4c1c35d56ffeccdb Mon Sep 17 00:00:00 2001 From: Anna-Karin Salander Date: Wed, 9 Oct 2024 10:34:07 -0700 Subject: [PATCH 4/4] moving appId to client override config --- .../core/client/builder/SdkClientBuilder.java | 20 ------------ .../builder/SdkDefaultClientBuilder.java | 18 +++-------- .../config/ClientOverrideConfiguration.java | 32 +++++++++++++++++++ .../core/client/config/SdkClientOption.java | 18 +++++++++++ .../useragent/AppIdUserAgentTest.java | 4 ++- 5 files changed, 58 insertions(+), 34 deletions(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkClientBuilder.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkClientBuilder.java index f984105f898b..b798724bde04 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkClientBuilder.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkClientBuilder.java @@ -22,7 +22,6 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.core.SdkPlugin; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; -import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.utils.builder.SdkBuilder; @@ -97,23 +96,4 @@ default List plugins() { throw new UnsupportedOperationException(); } - /** - * Configure an optional identification value to be appended to the user agent header. - * The value should be less than 50 characters in length and is null by default. - *

- * Users can additionally supply the appId value through environment and JVM settings, and - * it will be resolved using the following order of precedence (highest first): - *

    - *
  1. This client builder configuration
  2. - *
  3. The {@code AWS_SDK_UA_APP_ID} environment variable
  4. - *
  5. The {@code sdk.ua.appId} JVM system property
  6. - *
  7. The {@code sdk_ua_app_id} setting in the profile file for the active profile
  8. - *
- *

- * This configuration option supersedes {@link SdkAdvancedClientOption#USER_AGENT_PREFIX} and - * {@link SdkAdvancedClientOption#USER_AGENT_SUFFIX} and should be used instead of those options. - */ - default B appId(String appId) { - throw new UnsupportedOperationException(); - } } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java index 965b060e1eda..6596a7065a7a 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java @@ -48,6 +48,7 @@ import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_STRATEGY; import static software.amazon.awssdk.core.client.config.SdkClientOption.SCHEDULED_EXECUTOR_SERVICE; import static software.amazon.awssdk.core.client.config.SdkClientOption.SYNC_HTTP_CLIENT; +import static software.amazon.awssdk.core.client.config.SdkClientOption.USER_AGENT_APP_ID; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.APP_ID; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.HTTP; import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.INTERNAL_METADATA_MARKER; @@ -151,7 +152,6 @@ public abstract class SdkDefaultClientBuilder, private final SdkHttpClient.Builder defaultHttpClientBuilder; private final SdkAsyncHttpClient.Builder defaultAsyncHttpClientBuilder; private final List plugins = new ArrayList<>(); - private String appId; protected SdkDefaultClientBuilder() { @@ -415,7 +415,7 @@ private String resolveClientUserAgent(LazyValueSource config) { SdkClientUserAgentProperties clientProperties = new SdkClientUserAgentProperties(); ClientType clientType = config.get(CLIENT_TYPE); - ClientType resolvedClientType = clientType == null ? ClientType.UNKNOWN : config.get(CLIENT_TYPE); + ClientType resolvedClientType = clientType == null ? ClientType.UNKNOWN : clientType; clientProperties.putProperty(RETRY_MODE, StringUtils.lowerCase(resolveRetryMode(config.get(RETRY_POLICY), config.get(RETRY_STRATEGY)))); @@ -424,7 +424,9 @@ private String resolveClientUserAgent(LazyValueSource config) { clientProperties.putProperty(HTTP, SdkHttpUtils.urlEncode(clientName(resolvedClientType, config.get(SYNC_HTTP_CLIENT), config.get(ASYNC_HTTP_CLIENT)))); - clientProperties.putProperty(APP_ID, appId().orElseGet(() -> resolveAppId(config))); + String appId = config.get(USER_AGENT_APP_ID); + String resolvedAppId = appId == null ? resolveAppId(config) : appId; + clientProperties.putProperty(APP_ID, resolvedAppId); return SdkUserAgentBuilder.buildClientUserAgentString(SystemUserAgent.getOrCreate(), clientProperties); } @@ -457,10 +459,6 @@ private RetryStrategy resolveRetryStrategy(LazyValueSource config) { return SdkDefaultRetryStrategy.forRetryMode(retryMode); } - public Optional appId() { - return Optional.ofNullable(appId); - } - /** * Finalize which sync HTTP client will be used for the created client. */ @@ -658,12 +656,6 @@ public final List plugins() { return Collections.unmodifiableList(plugins); } - @Override - public final B appId(String appId) { - this.appId = appId; - return thisBuilder(); - } - /** * Return "this" for method chaining. */ diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java index 204a695397fc..04c418c1b08d 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java @@ -34,6 +34,7 @@ import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_POLICY; import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_STRATEGY; import static software.amazon.awssdk.core.client.config.SdkClientOption.SCHEDULED_EXECUTOR_SERVICE; +import static software.amazon.awssdk.core.client.config.SdkClientOption.USER_AGENT_APP_ID; import static software.amazon.awssdk.utils.ScheduledExecutorUtils.unmanagedScheduledExecutor; import static software.amazon.awssdk.utils.ScheduledExecutorUtils.unwrapUnmanagedScheduledExecutor; @@ -120,6 +121,7 @@ public final class ClientOverrideConfiguration options.add(CONFIGURED_RETRY_STRATEGY); options.add(CONFIGURED_RETRY_CONFIGURATOR); options.add(CONFIGURED_RETRY_MODE); + options.add(USER_AGENT_APP_ID); CLIENT_OVERRIDE_OPTIONS = Collections.unmodifiableSet(options); Set> resolvedOptions = new HashSet<>(); @@ -381,6 +383,14 @@ public Optional compressionConfiguration() { return Optional.ofNullable(compressionConfig); } + /** + * An optional user specified identification value to be appended to the user agent header. + * For more information, see {@link SdkClientOption#USER_AGENT_APP_ID}. + */ + public Optional appId() { + return Optional.ofNullable(config.option(USER_AGENT_APP_ID)); + } + @Override public String toString() { return ToString.builder("ClientOverrideConfiguration") @@ -395,6 +405,7 @@ public String toString() { .add("profileName", defaultProfileName().orElse(null)) .add("scheduledExecutorService", scheduledExecutorService().orElse(null)) .add("compressionConfiguration", compressionConfiguration().orElse(null)) + .add("appId", appId().orElse(null)) .build(); } @@ -757,6 +768,16 @@ default Builder compressionConfiguration(Consumer extends ClientOption { public static final SdkClientOption COMPRESSION_CONFIGURATION = new SdkClientOption<>(CompressionConfiguration.class); + /** + * An optional identification value to be appended to the user agent header. + * The value should be less than 50 characters in length and is null by default. + *

+ * Users can additionally supply the appId value through environment and JVM settings, and + * it will be resolved using the following order of precedence (highest first): + *

    + *
  1. This client option configuration
  2. + *
  3. The {@code AWS_SDK_UA_APP_ID} environment variable
  4. + *
  5. The {@code sdk.ua.appId} JVM system property
  6. + *
  7. The {@code sdk_ua_app_id} setting in the profile file for the active profile
  8. + *
+ *

+ * This configuration option supersedes {@link SdkAdvancedClientOption#USER_AGENT_PREFIX} and + * {@link SdkAdvancedClientOption#USER_AGENT_SUFFIX} and should be used instead of those options. + */ + public static final SdkClientOption USER_AGENT_APP_ID = new SdkClientOption<>(String.class); + /** * Option to specify a reference to the SDK client in use. */ diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/useragent/AppIdUserAgentTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/useragent/AppIdUserAgentTest.java index 07b413c1848d..58ff279b1a30 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/useragent/AppIdUserAgentTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/useragent/AppIdUserAgentTest.java @@ -29,6 +29,7 @@ import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; @@ -62,7 +63,8 @@ void resolveAppIdFromEnvironment(String description, String clientAppId, String RestJsonEndpointProvidersClientBuilder clientBuilder = syncClientBuilder(); if (!StringUtils.isEmpty(clientAppId)) { - clientBuilder.appId(clientAppId); + ClientOverrideConfiguration config = clientBuilder.overrideConfiguration().toBuilder().appId(clientAppId).build(); + clientBuilder.overrideConfiguration(config); } assertThatThrownBy(() -> clientBuilder.build().allTypes(r -> {}))