Skip to content
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

feat: provide client config property for region provider #1488

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
8 changes: 8 additions & 0 deletions .changes/05ac561b-963f-4953-bb4f-1fc19fc1207c.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"id": "05ac561b-963f-4953-bb4f-1fc19fc1207c",
"type": "feature",
"description": "Add `regionProvider` property to client config",
"issues": [
"awslabs/aws-sdk-kotlin#1478"
]
}
21 changes: 21 additions & 0 deletions aws-runtime/aws-config/api/aws-config.api
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,27 @@ public abstract class aws/sdk/kotlin/runtime/config/AbstractAwsSdkClientFactory
public static synthetic fun fromEnvironment$default (Laws/sdk/kotlin/runtime/config/AbstractAwsSdkClientFactory;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public abstract interface class aws/sdk/kotlin/runtime/config/AwsSdkClientConfig : aws/smithy/kotlin/runtime/client/SdkClientConfig {
public abstract fun getApplicationId ()Ljava/lang/String;
public abstract fun getRegion ()Ljava/lang/String;
public abstract fun getRegionProvider ()Laws/sdk/kotlin/runtime/region/RegionProvider;
public abstract fun getUseDualStack ()Z
public abstract fun getUseFips ()Z
}

public abstract interface class aws/sdk/kotlin/runtime/config/AwsSdkClientConfig$Builder {
public abstract fun getApplicationId ()Ljava/lang/String;
public abstract fun getRegion ()Ljava/lang/String;
public abstract fun getRegionProvider ()Laws/sdk/kotlin/runtime/region/RegionProvider;
public abstract fun getUseDualStack ()Ljava/lang/Boolean;
public abstract fun getUseFips ()Ljava/lang/Boolean;
public abstract fun setApplicationId (Ljava/lang/String;)V
public abstract fun setRegion (Ljava/lang/String;)V
public abstract fun setRegionProvider (Laws/sdk/kotlin/runtime/region/RegionProvider;)V
public abstract fun setUseDualStack (Ljava/lang/Boolean;)V
public abstract fun setUseFips (Ljava/lang/Boolean;)V
}

public final class aws/sdk/kotlin/runtime/config/AwsSdkSetting {
public static final field INSTANCE Laws/sdk/kotlin/runtime/config/AwsSdkSetting;
public final fun getAwsAccessKeyId ()Laws/smithy/kotlin/runtime/config/EnvironmentSetting;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

package aws.sdk.kotlin.runtime.config

import aws.sdk.kotlin.runtime.client.AwsSdkClientConfig
import aws.sdk.kotlin.runtime.config.compression.resolveDisableRequestCompression
import aws.sdk.kotlin.runtime.config.compression.resolveRequestMinCompressionSizeBytes
import aws.sdk.kotlin.runtime.config.endpoints.resolveUseDualStack
Expand Down Expand Up @@ -74,7 +73,7 @@ public abstract class AbstractAwsSdkClientFactory<
block?.let(config::apply)

config.logMode = config.logMode ?: ClientSettings.LogMode.resolve(platform = platform)
config.region = config.region ?: resolveRegion(profile = profile)
config.region = config.region ?: config.regionProvider?.getRegion() ?: resolveRegion(profile = profile)
config.useFips = config.useFips ?: resolveUseFips(profile = profile)
config.useDualStack = config.useDualStack ?: resolveUseDualStack(profile = profile)
config.applicationId = config.applicationId ?: resolveUserAgentAppId(platform, profile)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/

package aws.sdk.kotlin.runtime.client
package aws.sdk.kotlin.runtime.config

import aws.sdk.kotlin.runtime.region.RegionProvider
import aws.smithy.kotlin.runtime.client.SdkClientConfig

/**
Expand All @@ -19,6 +20,17 @@ public interface AwsSdkClientConfig : SdkClientConfig {
*/
public val region: String?

/**
* An optional region provider that determines the AWS region for client operations. When specified, this provider
* takes precedence over the default region provider chain, unless a static region is explicitly configured.
*
* Region Resolution Priority:
* 1. Static region (if specified)
* 2. Custom region provider (if configured)
* 3. Default region provider chain
*/
public val regionProvider: RegionProvider?

/**
* Flag to toggle whether to use [FIPS](https://aws.amazon.com/compliance/fips/) endpoints when making requests.
* Disabled by default.
Expand Down Expand Up @@ -54,6 +66,17 @@ public interface AwsSdkClientConfig : SdkClientConfig {
*/
public var region: String?

/**
* An optional region provider that determines the AWS region for client operations. When specified, this provider
* takes precedence over the default region provider chain, unless a static region is explicitly configured.
*
* Region Resolution Priority:
* 1. Static region (if specified)
* 2. Custom region provider (if configured)
* 3. Default region provider chain
*/
public var regionProvider: RegionProvider?

/**
* Flag to toggle whether to use [FIPS](https://aws.amazon.com/compliance/fips/) endpoints when making requests.
* Disabled by default.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

package aws.sdk.kotlin.runtime.config

import aws.sdk.kotlin.runtime.client.AwsSdkClientConfig
import aws.sdk.kotlin.runtime.config.profile.loadAwsSharedConfig
import aws.sdk.kotlin.runtime.config.useragent.resolveUserAgentAppId
import aws.sdk.kotlin.runtime.config.utils.mockPlatform
import aws.sdk.kotlin.runtime.region.RegionProvider
import aws.smithy.kotlin.runtime.client.*
import aws.smithy.kotlin.runtime.retries.StandardRetryStrategy
import aws.smithy.kotlin.runtime.util.PlatformProvider
Expand Down Expand Up @@ -129,6 +129,7 @@ private interface TestClient : SdkClient {
override val clientName: String = builder.clientName
override val logMode: LogMode = builder.logMode ?: LogMode.Default
override val region: String? = builder.region
override var regionProvider: RegionProvider? = builder.regionProvider
override var useFips: Boolean = builder.useFips ?: false
override var useDualStack: Boolean = builder.useDualStack ?: false
override val applicationId: String? = builder.applicationId
Expand All @@ -141,6 +142,7 @@ private interface TestClient : SdkClient {
override var clientName: String = "Test"
override var logMode: LogMode? = LogMode.Default
override var region: String? = null
override var regionProvider: RegionProvider? = null
override var useFips: Boolean? = null
override var useDualStack: Boolean? = null
override var applicationId: String? = null
Expand Down
18 changes: 0 additions & 18 deletions aws-runtime/aws-core/api/aws-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,3 @@ public final class aws/sdk/kotlin/runtime/client/AwsClientOption {
public final fun getRegion ()Laws/smithy/kotlin/runtime/collections/AttributeKey;
}

public abstract interface class aws/sdk/kotlin/runtime/client/AwsSdkClientConfig : aws/smithy/kotlin/runtime/client/SdkClientConfig {
public abstract fun getApplicationId ()Ljava/lang/String;
public abstract fun getRegion ()Ljava/lang/String;
public abstract fun getUseDualStack ()Z
public abstract fun getUseFips ()Z
}

public abstract interface class aws/sdk/kotlin/runtime/client/AwsSdkClientConfig$Builder {
public abstract fun getApplicationId ()Ljava/lang/String;
public abstract fun getRegion ()Ljava/lang/String;
public abstract fun getUseDualStack ()Ljava/lang/Boolean;
public abstract fun getUseFips ()Ljava/lang/Boolean;
public abstract fun setApplicationId (Ljava/lang/String;)V
public abstract fun setRegion (Ljava/lang/String;)V
public abstract fun setUseDualStack (Ljava/lang/Boolean;)V
public abstract fun setUseFips (Ljava/lang/Boolean;)V
}

Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ object AwsRuntimeTypes {
val AwsErrorMetadata = symbol("AwsErrorMetadata")
val AwsServiceException = symbol("AwsServiceException")
val ClientException = symbol("ClientException")

object Client : RuntimeTypePackage(AwsKotlinDependency.AWS_CORE, "client") {
val AwsSdkClientConfig = symbol("AwsSdkClientConfig")
}
}

object Endpoint : RuntimeTypePackage(AwsKotlinDependency.AWS_ENDPOINT) {
Expand All @@ -36,6 +32,8 @@ object AwsRuntimeTypes {
object Config : RuntimeTypePackage(AwsKotlinDependency.AWS_CONFIG) {
val AbstractAwsSdkClientFactory = symbol("AbstractAwsSdkClientFactory", "config")

val AwsSdkClientConfig = symbol("AwsSdkClientConfig", "config")

object Endpoints : RuntimeTypePackage(AwsKotlinDependency.AWS_CONFIG, "config.endpoints") {
val AccountIdEndpointMode = symbol("AccountIdEndpointMode")
val resolveEndpointUrl = symbol("resolveEndpointUrl")
Expand All @@ -54,6 +52,11 @@ object AwsRuntimeTypes {
val StaticCredentialsProvider = symbol("StaticCredentialsProvider")
val manage = symbol("manage", "auth.credentials.internal", isExtension = true)
}

object Region : RuntimeTypePackage(AwsKotlinDependency.AWS_CONFIG, "region") {
val RegionProvider = symbol("RegionProvider")
val resolveRegion = symbol("resolveRegion")
}
}

object Http : RuntimeTypePackage(AwsKotlinDependency.AWS_HTTP) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,33 @@ class AwsServiceConfigIntegration : KotlinIntegration {
val RegionProp: ConfigProperty = ConfigProperty {
name = "region"
symbol = KotlinTypes.String.toBuilder().nullable().build()
baseClass = AwsRuntimeTypes.Core.Client.AwsSdkClientConfig
baseClass = AwsRuntimeTypes.Config.AwsSdkClientConfig
useNestedBuilderBaseClass()
documentation = """
The AWS region (e.g. `us-west-2`) to make requests to. See about AWS
[global infrastructure](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/) for more
information
""".trimIndent()

propertyType = ConfigPropertyType.Custom(
render = { prop, writer ->
writer.write(
"override val #1L: #2T? = builder.#1L ?: #3T { builder.regionProvider?.getRegion() ?: #4T() }",
prop.propertyName,
prop.symbol,
KotlinTypes.Coroutines.runBlocking,
AwsRuntimeTypes.Config.Region.resolveRegion,
)
},
)

order = -100
}

val UserAgentAppId: ConfigProperty = ConfigProperty {
name = "applicationId"
symbol = KotlinTypes.String.asNullable()
baseClass = AwsRuntimeTypes.Core.Client.AwsSdkClientConfig
baseClass = AwsRuntimeTypes.Config.AwsSdkClientConfig
useNestedBuilderBaseClass()
documentation = """
An optional application specific identifier.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package aws.sdk.kotlin.codegen

import software.amazon.smithy.kotlin.codegen.core.CodegenContext
import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration
import software.amazon.smithy.kotlin.codegen.model.asNullable
import software.amazon.smithy.kotlin.codegen.rendering.util.ConfigProperty

/**
* Adds region provider integration to the client config
*/
class RegionProviderIntegration : KotlinIntegration {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Why is this separate from the RegionSupport integration that exists in smithy-kotlin? They serve the exact same purpose, just via slightly different mechanisms. Moreover, any non-AWS service which is regionalized and supports a static region config prop should also support a regionProvider prop shouldn't it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kind of my fault, I told @xinsong-cui to start coding in a new integration to make it less confusing and to work around some of the issues that were fixed by the refactor. I agree that it should go in the regionSupport integration.

companion object {
val RegionProviderProp: ConfigProperty = ConfigProperty {
name = "regionProvider"
symbol = AwsRuntimeTypes.Config.Region.RegionProvider.asNullable()
baseClass = AwsRuntimeTypes.Config.AwsSdkClientConfig
useNestedBuilderBaseClass()
documentation = """
An optional region provider that determines the AWS region for client operations. When specified, this provider
takes precedence over the default region provider chain, unless a static region is explicitly configured.

The region resolution order is:
1. Static region (if specified)
2. Custom region provider (if configured)
3. Default region provider chain
""".trimIndent()
}
}

override fun additionalServiceConfigProps(ctx: CodegenContext): List<ConfigProperty> = buildList {
add(RegionProviderProp)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class BindAwsEndpointBuiltins : KotlinIntegration {
Flag to toggle whether to use [FIPS](https://aws.amazon.com/compliance/fips/) endpoints when making requests.
` Disabled by default.
""".trimIndent()
baseClass = AwsRuntimeTypes.Core.Client.AwsSdkClientConfig
baseClass = AwsRuntimeTypes.Config.AwsSdkClientConfig
useNestedBuilderBaseClass()
}

Expand All @@ -50,7 +50,7 @@ class BindAwsEndpointBuiltins : KotlinIntegration {
See [https://docs.aws.amazon.com/sdkref/latest/guide/feature-endpoints.html] for more information.
` Disabled by default.
""".trimIndent()
baseClass = AwsRuntimeTypes.Core.Client.AwsSdkClientConfig
baseClass = AwsRuntimeTypes.Config.AwsSdkClientConfig
useNestedBuilderBaseClass()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ aws.sdk.kotlin.codegen.AddUserAgentMetadataIntegration
aws.sdk.kotlin.codegen.AwsServiceConfigIntegration
aws.sdk.kotlin.codegen.GradleGenerator
aws.sdk.kotlin.codegen.AwsRetryHeaderIntegration
aws.sdk.kotlin.codegen.RegionProviderIntegration
aws.sdk.kotlin.codegen.customization.ReplaceServiceExceptionBase
aws.sdk.kotlin.codegen.customization.DefaultMiddleware
aws.sdk.kotlin.codegen.customization.AccountIdEndpointBuiltinCustomization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class AwsServiceConfigIntegrationTest {
val contents = writer.toString()

val expectedProps = """
override val region: String? = builder.region
override val region: String? = builder.region ?: runBlocking { builder.regionProvider?.getRegion() ?: resolveRegion() }
override val credentialsProvider: CredentialsProvider = builder.credentialsProvider ?: DefaultChainCredentialsProvider(httpClient = httpClient, region = region).manage()
"""
contents.shouldContainOnlyOnceWithDiff(expectedProps)
Expand Down
3 changes: 3 additions & 0 deletions tests/codegen/event-stream/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ tasks.generateSmithyProjections {
// ensure the generated tests use the same version of the runtime as the aws aws-runtime
val smithyKotlinRuntimeVersion = libs.versions.smithy.kotlin.runtime.version.get()
System.setProperty("smithy.kotlin.codegen.clientRuntimeVersion", smithyKotlinRuntimeVersion)

val smithyKotlinCoroutinesVersion = libs.versions.coroutines.version.get()
System.setProperty("smithy.kotlin.codegen.kotlinCoroutinesVersion", smithyKotlinCoroutinesVersion)
}
}

Expand Down
Loading