Skip to content

Commit

Permalink
Feed ContextResolverMode into decoders
Browse files Browse the repository at this point in the history
  • Loading branch information
sksamuel committed May 27, 2023
1 parent 0aabb74 commit 8d3c2c8
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 15 deletions.
18 changes: 11 additions & 7 deletions hoplite-core/src/main/kotlin/com/sksamuel/hoplite/ConfigLoader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.sksamuel.hoplite.internal.DecodeMode
import com.sksamuel.hoplite.parsers.ParserRegistry
import com.sksamuel.hoplite.preprocessor.Preprocessor
import com.sksamuel.hoplite.report.Print
import com.sksamuel.hoplite.resolver.ContextResolverMode
import com.sksamuel.hoplite.resolver.Resolver
import com.sksamuel.hoplite.secrets.Obfuscator
import com.sksamuel.hoplite.secrets.PrefixObfuscator
Expand Down Expand Up @@ -43,6 +44,7 @@ class ConfigLoader(
val resolvers: List<Resolver> = emptyList(),
val sealedTypeDiscriminatorField: String? = null,
val allowNullOverride: Boolean = false,
val contextResolverMode: ContextResolverMode = ContextResolverMode.ErrorOnUnresolved,
) {

init {
Expand Down Expand Up @@ -177,6 +179,7 @@ class ConfigLoader(
environment = environment,
resolvers = resolvers,
sealedTypeDiscriminatorField = sealedTypeDiscriminatorField,
contextResolverMode = contextResolverMode,
).decode(kclass, environment, resourceOrFiles, propertySources, configSources)
}

Expand Down Expand Up @@ -218,18 +221,19 @@ class ConfigLoader(
cascadeMode = cascadeMode,
preprocessors = preprocessors,
preprocessingIterations = preprocessingIterations,
decoderRegistry = decoderRegistry,
paramMappers = paramMappers, // not needed to load nodes
flattenArraysToString = false,
allowUnresolvedSubstitutions = allowUnresolvedSubstitutions, // not used when loading nodes
resolvers = resolvers,
decoderRegistry = decoderRegistry, // not needed to load nodes
paramMappers = paramMappers,
flattenArraysToString = false, // not used when loading nodes
allowUnresolvedSubstitutions = allowUnresolvedSubstitutions, // not used when loading nodes
secretsPolicy = null, // not used when loading nodes
decodeMode = DecodeMode.Lenient, // not used when loading nodes
useReport = false, // not used when loading nodes
obfuscator = StrictObfuscator("*"), // not used when loading nodes
useReport = false, // not used when loading nodes
obfuscator = StrictObfuscator("*"),
reportPrintFn = reportPrintFn ?: { },
environment = environment,
resolvers = resolvers,
sealedTypeDiscriminatorField = sealedTypeDiscriminatorField,
contextResolverMode = contextResolverMode,
).load(resourceOrFiles, propertySources, configSources)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class ConfigLoaderBuilder private constructor() {
private var allowNullOverride = false
private var allowUnresolvedSubstitutions = false
private var sealedTypeDiscriminatorField: String? = null
private var contextResolverMode = ContextResolverMode.Error
private var contextResolverMode = ContextResolverMode.ErrorOnUnresolved

private val propertySources = mutableListOf<PropertySource>()
private val preprocessors = mutableListOf<Preprocessor>()
Expand Down Expand Up @@ -357,6 +357,7 @@ class ConfigLoaderBuilder private constructor() {
reportPrintFn = reportPrintFn,
flattenArraysToString = flattenArraysToString,
sealedTypeDiscriminatorField = sealedTypeDiscriminatorField,
contextResolverMode = contextResolverMode,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ data class DecoderContext(
val environment: Environment? = null,
val resolvers: Resolving = Resolving.empty,
// determines if we should error when a context resolver cannot find a substitution
val contextResolverMode: ContextResolverMode = ContextResolverMode.Error,
val contextResolverMode: ContextResolverMode = ContextResolverMode.ErrorOnUnresolved,
val sealedTypeDiscriminatorField: String? = null,
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.sksamuel.hoplite.parsers.ParserRegistry
import com.sksamuel.hoplite.preprocessor.Preprocessor
import com.sksamuel.hoplite.report.Print
import com.sksamuel.hoplite.report.Reporter
import com.sksamuel.hoplite.resolver.ContextResolverMode
import com.sksamuel.hoplite.resolver.Resolver
import com.sksamuel.hoplite.resolver.Resolving
import com.sksamuel.hoplite.secrets.Obfuscator
Expand All @@ -44,6 +45,7 @@ class ConfigParser(
private val reportPrintFn: Print,
private val environment: Environment?,
private val sealedTypeDiscriminatorField: String?,
private val contextResolverMode: ContextResolverMode,
) {

private val loader = PropertySourceLoader(classpathResourceLoader, parserRegistry, allowEmptyTree)
Expand All @@ -59,6 +61,7 @@ class ConfigParser(
environment = environment,
resolvers = Resolving(resolvers, root),
sealedTypeDiscriminatorField = sealedTypeDiscriminatorField,
contextResolverMode = contextResolverMode,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import com.sksamuel.hoplite.fp.valid
* and the [SystemPropertyContextResolver] will replace ${{ sysprop:name }} with the system property `name`.
*
* If the supplied context or path cannot be resolved, an error will be returned if the
* [ContextResolverMode] is set to [ContextResolverMode.Error] (which is the default). To disable
* errors, set this value to [ContextResolverMode.Silent].
* [ContextResolverMode] is set to [ContextResolverMode.ErrorOnUnresolved] (which is the default). To disable
* errors, set this value to [ContextResolverMode.SkipUnresolved].
*/
abstract class ContextResolver : Resolver {

Expand Down Expand Up @@ -79,7 +79,7 @@ abstract class ContextResolver : Resolver {

return replacement.flatMap {
when {
it == null && context.contextResolverMode == ContextResolverMode.Silent -> node.valid()
it == null && context.contextResolverMode == ContextResolverMode.SkipUnresolved -> node.valid()
it == null -> ConfigFailure.ResolverFailure("Could not resolve '$path'").invalid()
else -> node.copy(value = node.value.replaceRange(result.range, it)).valid()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package com.sksamuel.hoplite.resolver
enum class ContextResolverMode {

// do not fail if the substitution path is not found
Silent,
SkipUnresolved,

// fail if the substitution path is not found
Error,
ErrorOnUnresolved,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.sksamuel.hoplite.resolver

import com.sksamuel.hoplite.ConfigLoaderBuilder
import com.sksamuel.hoplite.ExperimentalHoplite
import com.sksamuel.hoplite.parsers.PropsPropertySource
import io.kotest.assertions.throwables.shouldThrowAny
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldInclude
import java.util.Properties

@OptIn(ExperimentalHoplite::class)
class ContextResolverModeTest : FunSpec({

data class Config(val bar: String)

test("should error for unresolved substitutions when context resolver mode is ErrorOnUnresolved") {

val props = Properties()
props["bar"] = "\${{ ref:foo }}"

shouldThrowAny {
ConfigLoaderBuilder.newBuilder()
.addPropertySource(PropsPropertySource(props))
.withContextResolverMode(ContextResolverMode.ErrorOnUnresolved)
.build()
.loadConfigOrThrow<Config>()
}.message shouldInclude "Could not resolve 'foo'"
}

test("should not error for unresolved substitutions when context resolver mode is SkipUnresolved") {

val props = Properties()
props["bar"] = "\${{ ref:foo }}"

val config = ConfigLoaderBuilder.newBuilder()
.addPropertySource(PropsPropertySource(props))
.withContextResolverMode(ContextResolverMode.SkipUnresolved)
.build()
.loadConfigOrThrow<Config>()

config shouldBe Config(bar = "\${{ ref:foo }}")
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import java.util.Properties

class SubstitutionResolverTest : FunSpec({
class ReferenceContextResolverTest : FunSpec({

test("should find matches") {

Expand Down Expand Up @@ -46,4 +46,5 @@ class SubstitutionResolverTest : FunSpec({
val config = ReferenceContextResolver.resolve(node, root, DecoderContext.zero)
(config.getUnsafe() as StringNode).value shouldBe "boatymcboatface"
}

})

0 comments on commit 8d3c2c8

Please sign in to comment.