Skip to content

Commit 67b155c

Browse files
authored
chore: refactor environment settings and retry settings out of aws-sdk-kotlin into smithy-kotlin (#841)
1 parent 5670914 commit 67b155c

File tree

6 files changed

+226
-0
lines changed

6 files changed

+226
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"id": "c0bae93d-f508-4b1e-a810-a528285f599f",
3+
"type": "misc",
4+
"description": "Refactor environment settings and retry modes out of aws-sdk-kotlin"
5+
}

runtime/runtime-core/api/runtime-core.api

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ public final class aws/smithy/kotlin/runtime/ServiceException$ErrorType : java/l
6666
public static fun values ()[Laws/smithy/kotlin/runtime/ServiceException$ErrorType;
6767
}
6868

69+
public final class aws/smithy/kotlin/runtime/config/EnvironmentSettingKt {
70+
}
71+
6972
public final class aws/smithy/kotlin/runtime/content/ByteArrayContent : aws/smithy/kotlin/runtime/content/ByteStream$Buffer {
7073
public fun <init> ([B)V
7174
public fun bytes ()[B
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package aws.smithy.kotlin.runtime.config
6+
7+
import aws.smithy.kotlin.runtime.ClientException
8+
import aws.smithy.kotlin.runtime.InternalApi
9+
import aws.smithy.kotlin.runtime.util.PlatformEnvironProvider
10+
11+
@InternalApi
12+
public typealias EnvSettingFactory<T> = (String, String) -> EnvironmentSetting<T>
13+
14+
/**
15+
* Describes a setting whose value may be retrieved from the local environment, usually via a JVM system property or an
16+
* environment variable.
17+
* @param T The type of values for this property
18+
* @param parse A function that maps string values into the type of this setting
19+
* @param sysProp The name of the JVM system property where this setting may be configured
20+
* @param envVar The name of the environment variable where this setting may be configured
21+
* @param defaultValue The default value (if one exists)
22+
*/
23+
@InternalApi
24+
public data class EnvironmentSetting<T>(
25+
public val parse: (String) -> T,
26+
public val sysProp: String,
27+
public val envVar: String,
28+
public val defaultValue: T? = null,
29+
) {
30+
@InternalApi
31+
public companion object {
32+
public operator fun <T> invoke(asTyped: (String) -> T): EnvSettingFactory<T> =
33+
{ sysProp: String, envVar: String -> EnvironmentSetting(asTyped, sysProp, envVar) }
34+
}
35+
36+
public fun orElse(defaultValue: T): EnvironmentSetting<T> = copy(defaultValue = defaultValue)
37+
}
38+
39+
/**
40+
* Resolves an environment setting from the environment. This method attempts to resolve the setting via the system
41+
* property first, falling back to the environment variable if necessary. If neither is set, it returns the setting's
42+
* default value.
43+
* @param platform The [PlatformEnvironProvider] to use for reading system properties and environment variables.
44+
*/
45+
@InternalApi
46+
public fun <T> EnvironmentSetting<T>.resolve(platform: PlatformEnvironProvider): T? {
47+
val stringValue = platform.getProperty(sysProp) ?: platform.getenv(envVar)
48+
return stringValue?.let(parse) ?: defaultValue
49+
}
50+
51+
/* ktlint-disable spacing-between-declarations-with-annotations */
52+
@InternalApi public val boolEnvSetting: EnvSettingFactory<Boolean> = EnvironmentSetting(String::toBoolean)
53+
@InternalApi public val intEnvSetting: EnvSettingFactory<Int> = EnvironmentSetting(String::toInt)
54+
@InternalApi public val longEnvSetting: EnvSettingFactory<Long> = EnvironmentSetting(String::toLong)
55+
@InternalApi public val strEnvSetting: EnvSettingFactory<String> = EnvironmentSetting { it }
56+
/* ktlint-enable spacing-between-declarations-with-annotations */
57+
58+
@InternalApi
59+
public inline fun <reified T : Enum<T>> enumEnvSetting(sysProp: String, envVar: String): EnvironmentSetting<T> {
60+
val parse = { strValue: String ->
61+
val allValues = enumValues<T>()
62+
allValues
63+
.firstOrNull { it.name.equals(strValue, ignoreCase = true) }
64+
?: throw ClientException("Value $strValue is not supported, should be one of ${allValues.joinToString(", ")}")
65+
}
66+
return EnvironmentSetting(parse, sysProp, envVar)
67+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package aws.smithy.kotlin.runtime.config
6+
7+
import aws.smithy.kotlin.runtime.util.PlatformEnvironProvider
8+
import org.junit.jupiter.api.Test
9+
import kotlin.test.assertEquals
10+
import kotlin.test.assertNull
11+
12+
class EnvironmentSettingTest {
13+
@Test
14+
fun itResolvesSysPropSettingFirst() {
15+
val setting = strEnvSetting("foo.bar", "FOO_BAR").orElse("error")
16+
val testPlatform = mockPlatform(mapOf("foo.bar" to "success"), mapOf("FOO_BAR" to "error"))
17+
18+
val actual = setting.resolve(testPlatform)
19+
assertEquals("success", actual)
20+
}
21+
22+
@Test
23+
fun itResolvesEnvVarSettingSecond() {
24+
val setting = strEnvSetting("foo.bar", "FOO_BAR").orElse("error")
25+
val testPlatform = mockPlatform(envVars = mapOf("FOO_BAR" to "success"))
26+
27+
val actual = setting.resolve(testPlatform)
28+
assertEquals("success", actual)
29+
}
30+
31+
@Test
32+
fun itResolvesDefaultSettingThird() {
33+
val setting = strEnvSetting("foo.bar", "FOO_BAR").orElse("success")
34+
val testPlatform = mockPlatform()
35+
36+
val actual = setting.resolve(testPlatform)
37+
assertEquals("success", actual)
38+
}
39+
40+
@Test
41+
fun itReturnsNullWithNoValue() {
42+
val setting = strEnvSetting("foo.bar", "FOO_BAR")
43+
val testPlatform = mockPlatform()
44+
45+
assertNull(setting.resolve(testPlatform))
46+
}
47+
48+
@Test
49+
fun itResolvesBooleans() {
50+
val setting = boolEnvSetting("foo.bar", "FOO_BAR")
51+
val testPlatform = mockPlatform(mapOf("foo.bar" to "TRUE"))
52+
53+
val actual = setting.resolve(testPlatform)
54+
assertEquals(true, actual)
55+
}
56+
57+
@Test
58+
fun itResolvesIntegers() {
59+
val setting = intEnvSetting("foo.bar", "FOO_BAR")
60+
val testPlatform = mockPlatform(mapOf("foo.bar" to "42"))
61+
62+
val actual = setting.resolve(testPlatform)
63+
assertEquals(42, actual)
64+
}
65+
66+
@Test
67+
fun itResolvesLongs() {
68+
val setting = longEnvSetting("foo.bar", "FOO_BAR")
69+
val testPlatform = mockPlatform(mapOf("foo.bar" to "-4294967296"))
70+
71+
val actual = setting.resolve(testPlatform)
72+
assertEquals(-4294967296L, actual)
73+
}
74+
75+
@Test
76+
fun itResolvesEnums() {
77+
val setting = enumEnvSetting<Suit>("foo.bar", "FOO_BAR")
78+
val testPlatform = mockPlatform(mapOf("foo.bar" to "spades"))
79+
80+
val actual = setting.resolve(testPlatform)
81+
assertEquals(Suit.Spades, actual)
82+
}
83+
}
84+
85+
private fun mockPlatform(sysProps: Map<String, String> = mapOf(), envVars: Map<String, String> = mapOf()) =
86+
object : PlatformEnvironProvider {
87+
override fun getAllEnvVars(): Map<String, String> = envVars
88+
override fun getenv(key: String): String? = envVars[key]
89+
override fun getAllProperties(): Map<String, String> = sysProps
90+
override fun getProperty(key: String): String? = sysProps[key]
91+
}
92+
93+
enum class Suit {
94+
Hearts,
95+
Diamonds,
96+
Clubs,
97+
Spades,
98+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package aws.smithy.kotlin.runtime.client.config
6+
7+
import aws.smithy.kotlin.runtime.InternalApi
8+
import aws.smithy.kotlin.runtime.config.EnvironmentSetting
9+
import aws.smithy.kotlin.runtime.config.enumEnvSetting
10+
import aws.smithy.kotlin.runtime.config.intEnvSetting
11+
12+
@InternalApi
13+
public object ClientSettings {
14+
/**
15+
* The maximum number of request attempts to perform. This is one more than the number of retries, so
16+
* maxAttempts = 1 will have 0 retries.
17+
*/
18+
public val MaxAttempts: EnvironmentSetting<Int> = intEnvSetting("sdk.maxAttempts", "SDK_MAX_ATTEMPTS")
19+
20+
/**
21+
* Which RetryMode to use for the default RetryPolicy, when one is not specified at the client level.
22+
*/
23+
public val RetryMode: EnvironmentSetting<RetryMode> = enumEnvSetting<RetryMode>("sdk.retryMode", "SDK_RETRY_MODE")
24+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package aws.smithy.kotlin.runtime.client.config
6+
7+
import aws.smithy.kotlin.runtime.InternalApi
8+
9+
/**
10+
* The retry mode to be used for the client's retry strategy.
11+
*/
12+
@InternalApi
13+
public enum class RetryMode {
14+
/**
15+
* The legacy retry mode is supported for compatibility with other SDKs and existing configurations for them,
16+
* but it works exactly the same as the standard retry mode for this SDK.
17+
*/
18+
LEGACY,
19+
20+
/**
21+
* The standard retry mode. With this, the client will use the [StandardRetryStrategy][aws.smithy.kotlin.runtime.retries.StandardRetryStrategy]
22+
*/
23+
STANDARD,
24+
25+
/**
26+
* Not implemented yet. https://github.com/awslabs/aws-sdk-kotlin/issues/701
27+
*/
28+
ADAPTIVE,
29+
}

0 commit comments

Comments
 (0)