Skip to content

Commit

Permalink
KTOR-5283 Support Default Value for missing Env Variables in YAML
Browse files Browse the repository at this point in the history
  • Loading branch information
rsinukov committed Dec 7, 2022
1 parent bfde300 commit e20b653
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 5 deletions.
Expand Up @@ -15,7 +15,7 @@ public class YamlConfigLoader : ConfigLoader {
/**
* Tries loading an application configuration from the specified [path].
*
* @return configuration or null if the path is not found or configuration format is not supported.
* @return configuration or null if the path is not found or a configuration format is not supported.
*/
override fun load(path: String?): ApplicationConfig? {
return YamlConfig(path)?.apply { checkEnvironmentVariables() }
Expand All @@ -24,14 +24,14 @@ public class YamlConfigLoader : ConfigLoader {

/**
* Loads a configuration from the YAML file, if found.
* On JVM, loads a configuration from application resources, if exist; otherwise, reads a configuration from a file.
* On JVM, loads a configuration from application resources, if exists; otherwise, reads a configuration from a file.
* On Native, always reads a configuration from a file.
*/
public expect fun YamlConfig(path: String?): YamlConfig?

/**
* Implements [ApplicationConfig] by loading a configuration from a YAML file.
* Values can reference to environment variables with `$ENV_VAR` syntax.
* Values can reference to environment variables with `$ENV_VAR` or `"$ENV_VAR:default_value"` syntax.
*/
public class YamlConfig(private val yaml: YamlMap) : ApplicationConfig {

Expand Down Expand Up @@ -125,9 +125,18 @@ public class YamlConfig(private val yaml: YamlMap) : ApplicationConfig {
private fun resolveValue(value: String): String {
val isEnvVariable = value.startsWith("\$")
if (!isEnvVariable) return value
val key = value.drop(1)
val keyWithDefault = value.drop(1)
val separatorIndex = keyWithDefault.indexOf(':')
val (key, default) = if (separatorIndex == -1) {
keyWithDefault to null
} else {
keyWithDefault.substring(0, separatorIndex) to keyWithDefault.substring(separatorIndex + 1)
}
return getEnvironmentValue(key)
?: throw ApplicationConfigurationException("Environment variable \"$key\" not found")
?: default
?: throw ApplicationConfigurationException(
"Environment variable \"$key\" not found and no default value is present"
)
}

internal expect fun getEnvironmentValue(key: String): String?
Expand Up @@ -133,6 +133,34 @@ class YamlConfigTest {
}
}

@Test
fun testMissingEnvironmentVariableWithDefault() {
val content = """
ktor:
variable: "${'$'}NON_EXISTING_VARIABLE:DEFAULT_VALUE"
""".trimIndent()
val yaml = Yaml.decodeYamlFromString(content)
val config = YamlConfig(yaml as YamlMap)
config.checkEnvironmentVariables()
assertEquals("DEFAULT_VALUE", config.property("ktor.variable").getString())
}

@Test
fun testExistingEnvironmentVariableWithDefault() {
val content = """
ktor:
variable: "${'$'}PATH:DEFAULT_VALUE"
""".trimIndent()
val yaml = Yaml.decodeYamlFromString(content)
val config = YamlConfig(yaml as YamlMap)
config.checkEnvironmentVariables()

val value = config.property("ktor.variable").getString()
assertTrue(value.isNotEmpty())
assertFalse(value.contains("PATH"))
assertFalse(value.contains("DEFAULT_VALUE"))
}

@Test
fun testToMap() {
val content = """
Expand Down

0 comments on commit e20b653

Please sign in to comment.