ConfigDocs
holds the descriptions and details of a ConfigDescriptor
which can be used to produce documentation.
ConfigDocs
holds the descriptions and details of a ConfigDescriptor
which can be used to produce documentation.
Every ConfigSource at the core is just a Reader
,
which is essentially a function that goes from PropertyTreePath
to an actual PropertyTree
.
Every ConfigSource at the core is just a Reader
,
which is essentially a function that goes from PropertyTreePath
to an actual PropertyTree
.
i.e, f: PropertyTreePath[String] => IO[ReadError[String], PropertyTree[String, String]
Later on for each key
represented as PropertyTreePath[String]
internally, f
is used to
applied to get the value as a PropertyTree
itself.
Internal details:
This function f
can be retrieved under an ZManaged effect. This implies it may involve an IO with managing resources
to even form this function. Example: In order to retrieve a property-tree corresponding to a key (PropertyTreePath),
it requires a database connection in the very first instance.
// pseudo-logic, doesn't compile
val source: ConfigSource = ConfigSource.Reader( ZManaged(getDatabaseConnection) .flatMap(connection => (key: PropertyTreePath[String] => IO.effect(connection.getStatement.executeQuery(s"get key from table"))) )
Note that ConfigSource
has a generalised memoize
function that allows you to memoize the effect required to form the
function. In the context of the above example, with source.memoize
we acquire only a single connection to retrieve
the values for all the keys in your product/coproduct for an instance of read
.
A Table is a recursive structure that is more easier to be interpreted as Json or Markdown than trying
to convert ConfigDocs
to a readable format.
A Table is a recursive structure that is more easier to be interpreted as Json or Markdown than trying
to convert ConfigDocs
to a readable format.
Use functions in this Config
object when you need to retrieve your instance of config in terms of zio.Layer.
Use functions in this Config
object when you need to retrieve your instance of config in terms of zio.Layer.
For example:
final case class MyConfig(dburl: String, port: Int) val myConfigDesc: ConfigDescriptor[MyConfig] = (string("dburl") zip int("port")).to[MyConfig] val myConfig: Layer[ReadError[String], Config[MyConfig]] = Config.fromSystemEnv(myConfigDesc)
By using Config.fromSystemEnv(myConfigDesc)
, it internally extends your description which is myConfigDesc
to include the ConfigSource
.
In the above example, it is the ConfigSource
corresponding to
sys.env.
It then calls
zio.config.read with this new description that includes the source information.
Extending an existing config description to include a ConfigSource
is as simple as
myConfigDesc from configSource
Also, note that Config[MyConfig]
in the above example is a simple type alias to Has[MyConfig]
.
If you want to retrieve your config as scala.Either instead of zio.Layer, then you will have to extend
your description to include the information on ConfigSource
manually.
For example:
import zio.config._, ConfigDescriptor._ final case class MyConfig(dburl: String, port: Int) val myConfig: ConfigDescriptor[MyConfig] = (string("dburl") zip int("port")).to[MyConfig] val constantSource: ConfigSource = ConfigSource.fromMap(Map("dburl" -> "xyz", "port" -> "8080")) val result: Either[ReadError[String], MyConfig] = read(myConfig from constantSource)
Note: With the above approach, we got a simple scala.Either instead of retrieving them in terms of ZIO.
Instead of the above approach, if we use Config.fromMap(constantMap, myConfig)
,
then we will get a Layer[ReadError[String], MyConfig]
The above approach is especially useful when we have a custom ConfigSource
.
For instance, we can form a custom ConfigSource
by composing a few existing ConfigSources.
For example:
import zio.config._, ConfigDescriptor._ final case class MyConfig(dburl: String, port: Int) val myConfig: ConfigDescriptor[MyConfig] = (string("dburl") zip int("port")).to[MyConfig] val sysEnvSource: UIO[MyConfig] = ConfigSource.fromSystemEnv val constantSource: ConfigSource = ConfigSource.fromMap(Map("dburl" -> "xyz", "port" -> "8080")) val result: IO[ReadError[String], MyConfig] = configSource.flatMap(source => read(myConfig from sysEnvSource.orElse(constantSource))
In the above example, the results returned an UIO because of the existence of ConfigSource corresponding to
sys.env.
Convert a ConfigDescriptor to a Layer, which further requires a ConfigSource as input.
Convert a ConfigDescriptor to a Layer, which further requires a ConfigSource as input.
Note: Use configLayer_
instead of configLayer
if ConfigSource information is already embedded in the descriptor.
Example usage:
final case class MyConfig(age: Int) val appConfigLayer = ConfigSource.fromMap(Map("age" -> "20")).toLayer >>> configLayer(int("age").to[MyConfig]) val app: ZIO[MyConfig with zio.console.Console,java.io.IOException, Unit] = getConfig[MyConfig].flatMap(Console.printLine) app.provideSomeLayer[Console](appConfigLayer) // ZIO[zio.console.Console, Exception, Unit]
This can also be simplified to
val appConfigLayer = configLayer_(int("age").to[MyConfig] from ConfigSource.fromMap(Map("age" -> "20"))) app.provideSomeLayer[Console](appConfigLayer)
The preference depends on how you want to design the entry point to managing config of your app.
Convert a ConfigDescriptor to a Layer.
Convert a ConfigDescriptor to a Layer.
Example usage:
final case class MyConfig(age: Int, name: String) object MyConfig { val config = (int("age") zip string("name")).to[MyConfig] from ConfigSource.fromMap(Map("age" -> "20", "name" -> "afsal")) } val app: ZIO[Has[MyConfig] with zio.console.Console, java.io.IOException, Unit] = getConfig[MyConfig].flatMap(putStrLn) val io: ZIO[zio.console.Console, Exception, Unit] = app.provideSomeLayer[Console](configLayer_(MyConfig.config)) println(zio.Runtime.default.unsafeRun(io))
Generate documentation based on the ConfigDescriptor
, where a
ConfigDescriptor
is a structure representing the logic to fetch the application config
from various sources.
Generate documentation based on the ConfigDescriptor
, where a
ConfigDescriptor
is a structure representing the logic to fetch the application config
from various sources.
Once we generate the docs, this can be converted to a light weight Table
structure which is much more easier to be converted
to markdown or json formats.
Example :
val configDescriptor: ConfigDescriptor[MyAppConfig] = ???
generatedDocs(configDescriptor).toTable.toGithubFlavouredMarkdown
Generate a report based on the ConfigDescriptor
and an A
, where a
ConfigDescriptor
represents the logic to fetch the application config
from various sources, and A
represents the actual config value that was retrieved.
Generate a report based on the ConfigDescriptor
and an A
, where a
ConfigDescriptor
represents the logic to fetch the application config
from various sources, and A
represents the actual config value that was retrieved.
Example usage:
Example usage:
A helper method allowing you to forget passing configs as arguments to all over the place
in your app. Whereever, you need to access the config, simply
call getConfig[MyConfig].flatMap(config => ???)
.
PS: if you are familiar with Kleisli, this is similar
to using Kleisi[IO, Config, A]
, except for the fact that it is Has[Config]
instead of Config
allowing you to mixin with other dependencies keeping your Monad
the same
import zio.console._ final case class MyConfig(username: String, port: Int) val app: ZIO[Has[MyConfig] with Console, java.io.IOException, Unit] = for { config <- getConfig[MyConfig] _ <- putStrLn(config.toString) } yield () val configDesc = (string("USERNAME") zip int("PORT")).to[MyConfig] from ConfigSource.fromMap(Map()) val main: ZIO[zio.console.Console, Exception, Unit] = app.provideSomeLayer[Console](configLayer_(configDesc))