Skip to content

Commit

Permalink
Improve YML and upgrade ZIO (#1102)
Browse files Browse the repository at this point in the history
* Improve YML provider and upgrade ZIO

- Make sure YML can accept keys with period characters
- Make sure YML tests handle non-empty-list, empty list and table data types
- Update ZIO to 2.0.10
  • Loading branch information
afsalthaj committed Mar 6, 2023
1 parent 0f35f42 commit 9face84
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 27 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ addCommandAlias(

val awsVersion = "1.12.360"
val zioAwsVersion = "5.19.8.4"
val zioVersion = "2.0.9+53-886ca54a-SNAPSHOT"
val zioVersion = "2.0.10"
val magnoliaVersion = "0.17.0"
val refinedVersion = "0.10.2"
val pureconfigVersion = "0.16.0"
Expand Down
43 changes: 23 additions & 20 deletions yaml/shared/src/main/scala/zio/config/yaml/YamlConfigProvider.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import scala.jdk.CollectionConverters._

@silent("Unused import")
object YamlConfigProvider {

import scala.collection.compat._
import VersionSpecificSupport._

Expand Down Expand Up @@ -81,9 +82,10 @@ object YamlConfigProvider {
* }}}
*/
def fromYamlReader(
reader: Reader
): ConfigProvider =
reader: Reader
): ConfigProvider = {
getIndexedConfigProvider(loadYaml(reader))
}

/**
* Retrieve a `ConfigSource` from yaml path.
Expand All @@ -108,27 +110,30 @@ object YamlConfigProvider {
fromYamlReader(new BufferedReader(new InputStreamReader(configStream)))
}

// Use zio-config-parser for xml, yaml, hcl and json
private[yaml] def getIndexedConfigProvider(data: AnyRef): ConfigProvider = {
def flattened(data: AnyRef, chunk: Chunk[String]): Map[Chunk[String], String] =
data match {
case null => Map.empty
case null => Map.empty
case t: JInteger => Map(chunk -> t.toString())
case t: JLong => Map(chunk -> t.toString())
case t: JFloat => Map(chunk -> t.toString())
case t: JDouble => Map(chunk -> t.toString())
case t: String => Map(chunk -> t.toString())
case t: JLong => Map(chunk -> t.toString())
case t: JFloat => Map(chunk -> t.toString())
case t: JDouble => Map(chunk -> t.toString())
case t: String => Map(chunk -> t.toString())
case t: JBoolean => Map(chunk -> t.toString())

case t: java.lang.Iterable[_] =>
val list = t.asInstanceOf[java.lang.Iterable[AnyRef]].asScala.toList

list.zipWithIndex
.map({ case (anyRef, index) =>
flattened(anyRef, chunk.mapLast(last => last + IndexKey(index)))
})
.reduceOption(_ ++ _)
.getOrElse(Map.empty)
if (list.isEmpty) {
Map(chunk -> "<nil>")
} else {
list.zipWithIndex
.map({ case (anyRef, index) =>
flattened(anyRef, chunk.mapLast(last => last + IndexKey(index)))
})
.reduceOption(_ ++ _)
.getOrElse(Map.empty)
}

case t: ju.Map[_, _] =>
val map = t.asInstanceOf[ju.Map[String, AnyRef]].asScala.toMap
Expand All @@ -140,17 +145,15 @@ object YamlConfigProvider {
.reduceOption(_ ++ _)
.getOrElse(Map.empty)

// It is definitely comparitively less user friendly to return IO[ConfigProvider].
// This is not the case in pure-config or zio-config-3.x although it had the same behaviour of doing a flatMap
// Example: provider.flatMap(_.load(config)) or provider1ZIO.zip(provider2ZIO).map{(case (provider1, provider2) => provider1.orElse(provider2))}
//
case a =>
case a =>
throw new RuntimeException(
s"Invalid yaml. ${a}"
)
}

ConfigProvider.fromMap(flattened(data, Chunk.empty).map({ case (k, v) => (k.mkString("."), v) }))
lazy val hiddenDelim = "\uFEFF"

ConfigProvider.fromMap(flattened(data, Chunk.empty).map({ case (k, v) => (k.mkString(hiddenDelim), v) }), pathDelim = hiddenDelim)

}

Expand Down
24 changes: 18 additions & 6 deletions yaml/shared/src/test/scala/zio/config/yaml/YamlConfigSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ object YamlConfigSpec extends ZIOSpecDefault {
case class Child(sum: List[Sum])

sealed trait Sum extends Product with Serializable
case class A(a: String) extends Sum
case class B(b: Boolean) extends Sum
case class A(a: String, h: List[Int]) extends Sum
case class B(b: Boolean, e: List[C], f: Map[String, Int]) extends Sum

final case class C(d: Int) extends Sum

val config =
Config
.listOf(
"sum",
((Config.string("a").to[A].nested("A")) orElseEither
(Config.boolean("b").to[B].nested("B")))
((Config.string("a").zip(Config.listOf("h", Config.int)).to[A].nested("A")) orElseEither
(Config.boolean("b").zip(Config.listOf("e", Config.int.to[C]).nested("d")).zip(Config.table("f", Config.int)).to[B].nested("B")))
.map(_.merge)
)
.to[Child]
Expand All @@ -29,13 +31,23 @@ object YamlConfigSpec extends ZIOSpecDefault {
"""|sum:
| - A:
| a: "str"
| h: []
| - B:
| b: false""".stripMargin
| b: false
| d:
| e:
| - 1
| - 2
| f:
| hi: 1
| bi: 2
|
| """.stripMargin
)

val zio = provider.load(config)

val expected = Child(List(A("str"), B(false)))
val expected = Child(List(A("str", Nil), B(false, List(C(1), C(2)), Map("hi" -> 1, "bi" -> 2))))

assertZIO(zio.exit)(succeeds(equalTo(expected)))
}
Expand Down

0 comments on commit 9face84

Please sign in to comment.