Resolving Variable Substitutions Between Multiple Config Files Using Typesafe Config in Scala

248 views Asked by At

I'm encountering an issue while attempting to load two configs in Scala using the Typesafe Config library, where the second config needs to reference variables in the first one.

Here is my code:

class ScalaCentralSharedLibConfigManager() {
  val configString =
    """
      |logger {
      |  elasticsearch {
      |    servers {
      |      prod {
      |        host = "http://example.com"
      |      }
      |    }
      |  }
      |}
      """.stripMargin
  var internalConsulConfig: Config = ConfigFactory.parseString(configString)
  var internalConfig: Config = ConfigFactory.load("sharedlibapplication.conf").resolveWith(internalConsulConfig)

Within my sharedlibapplication.conf I have the following:

elasticSearch {
   prod {
      host = ${logger.elasticsearch.servers.prod.host}
   }
}

When I run the code, I get the error: "Could not resolve substitution to a value: ${logger.elasticsearch.servers.prod.host}"

Any guidance or assistance with this issue would be much appreciated.

1

There are 1 answers

0
Ivan Stanislavciuc On

The problem is that method load is performing resolution of all internal references before you even get to the method resolveWith(internalConsulConfig), so the latter does nothing in you case.

So you can do it this way instead

val shared = ConfigFactory.parseResources("sharedlibapplication.conf")
val prod = ConfigFactory.parseString(???)
val resolved = ConfigFactory.load(shared.withFallback(prod))

Then you can see that it works by calling

println(resolved.getConfig("logger").root().render(ConfigRenderOptions.concise()))

with following output produced.

{"elasticsearch":{"servers":{"prod":{"host":"http://example.com"}}}}


However, I'd recommend to stick to the built in mechanism of sharing config files of libraries via reference.conf in combination with application.conf, which is the main config file that fallbacks to all combined reference.conf. This mechanism is used by libraries like akka for example and well understood by many developers.

So if you use the same and well known principles, it will be better understood by other developers in your team for example. A special resolution of config files, like the one described above, would be the last option for me.

Here's a simple example how you can do it for your case with the help of reference.conf with additional environment variable defined.

reference.conf:

elasticSearch {
  host = http://localhost
  host = ${?ELASTIC_SEARCH_HOST}
}

Then just setting the environment variable ELASTIC_SEARCH_HOST=http://example.com with a config object loaded as ConfigFactory.load(), which is doing the default resolution of config files with environment variables support, will be enough to provide correct settings into your library code.