Skip to content

Commit

Permalink
Add safe ZIO methods builders of ConfigProvider (#1398)
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanFinochenko authored Apr 28, 2024
1 parent 655ae01 commit 013712e
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 11 deletions.
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ lazy val scala212projects = Seq[ProjectReference](
zioConfigCatsJVM,
zioConfigRefinedJVM,
zioConfigMagnoliaJVM,
zioConfigTypesafeMagnoliaTestsJVM,
zioConfigZioAwsJVM,
zioConfigXmlJVM,
examplesJVM
Expand Down Expand Up @@ -409,7 +410,7 @@ lazy val zioConfigTypesafeMagnoliaTests = crossProject(JVMPlatform)
),
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(zioConfig % "compile->compile;test->test", zioConfigTypesafe, zioConfigMagnolia)
.dependsOn(zioConfig % "compile->compile;test->test", zioConfigTypesafe, zioConfigMagnolia, zioConfigDerivation)
lazy val zioConfigTypesafeMagnoliaTestsJVM = zioConfigTypesafeMagnoliaTests.jvm

lazy val docs = project
Expand Down
22 changes: 21 additions & 1 deletion core/shared/src/main/scala/zio/config/syntax/ConfigSyntax.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,26 @@ package zio.config.syntax

import zio.Config.Error.{And, InvalidData, MissingData, Or, SourceUnavailable, Unsupported}
import zio.config.TupleConversion
import zio.{Chunk, Config, ConfigProvider, IO}
import zio.config.syntax.ConfigSyntax.ConfigProviderZIO
import zio.{Chunk, Config, ConfigProvider, IO, Task}

import java.util.UUID
import scala.util.control.NonFatal

// Backward compatible approach to minimise the client changes
final case class Read[A](config: Config[A], configProvider: ConfigProvider)

final case class ReadZIO[A](config: Config[A], configProvider: ConfigProviderZIO)

object ConfigSyntax {
type ConfigProviderZIO = IO[Config.Error, ConfigProvider]

implicit class ConfigProviderZIOOps(buildConfigProvider: Task[ConfigProvider]) {
def toConfigProviderZIO: ConfigProviderZIO =
buildConfigProvider.mapError(throwable => Config.Error.Unsupported(message = throwable.toString))
}
}

// To be moved to ZIO ?
// Or may be zio-config can be considered as an extension to ZIO
trait ConfigSyntax {
Expand All @@ -18,6 +30,9 @@ trait ConfigSyntax {
final def read[A](reader: Read[A]): IO[Config.Error, A] =
reader.configProvider.load(reader.config)

final def read[A](reader: ReadZIO[A]): IO[Config.Error, A] =
reader.configProvider.flatMap(_.load(reader.config))

implicit class ConfigErrorOps(error: Config.Error) {
self =>

Expand Down Expand Up @@ -186,6 +201,11 @@ trait ConfigSyntax {
// Example: read(config from ConfigProvider.fromMap(""))
def from(configProvider: ConfigProvider): Read[A] =
Read(config, configProvider)

import ConfigSyntax.ConfigProviderZIOOps

def from(configProvider: Task[ConfigProvider]): ReadZIO[A] =
ReadZIO(config, configProvider.toConfigProviderZIO)
}

implicit class FromConfigProviderOps(c: ConfigProvider.type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package zio.config.magnolia

import zio.ConfigProvider
import zio.config._
import zio.config.derivation.name
import zio.config.magnolia._
import zio.test.Assertion._
import zio.test.{ZIOSpecDefault, _}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package zio.config.typesafe

import zio.Config
import zio.config.magnolia.deriveConfig
import zio.config.read
import zio.test.Assertion.equalTo
import zio.test.{Spec, ZIOSpecDefault, assertZIO}

object TypesafeConfigProviderZIOTest extends ZIOSpecDefault {

final case class DataBaseConfig(url: String)

val configDataBaseConfig: Config[DataBaseConfig] = deriveConfig[DataBaseConfig]

val hocconConfig: String = s"""url = "some_url""""

override def spec: Spec[Any, Config.Error] =
suite("TypesafeConfigProviderZIOTest")(
test("safe read config") {
val result = read(configDataBaseConfig from TypesafeConfigProvider.fromHoconStringZIO(hocconConfig))
val expected = DataBaseConfig("some_url")
assertZIO(result)(equalTo(expected))
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package zio.config.typesafe

import com.typesafe.config._
import zio.config.IndexedFlat.{ConfigPath, KeyComponent}
import zio.{Chunk, ConfigProvider}
import zio.{Chunk, ConfigProvider, Task, ZIO}

import java.io.File
import scala.jdk.CollectionConverters._
Expand Down Expand Up @@ -62,6 +62,24 @@ object TypesafeConfigProvider {
)
}

def fromResourcePathZIO(enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
ZIO.attempt(fromResourcePath(enableCommaSeparatedValueAsList))

def fromHoconFileZIO(file: File, enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
ZIO.attempt(fromHoconFile(file, enableCommaSeparatedValueAsList))

def fromHoconFilePathZIO(filePath: String, enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
ZIO.attempt(fromHoconFilePath(filePath, enableCommaSeparatedValueAsList))

def fromHoconStringZIO(input: String, enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
ZIO.attempt(fromHoconString(input, enableCommaSeparatedValueAsList))

def fromTypesafeConfigZIO(
config: com.typesafe.config.Config,
enableCommaSeparatedValueAsList: Boolean = false
): Task[ConfigProvider] =
ZIO.attempt(fromTypesafeConfig(config, enableCommaSeparatedValueAsList))

private[config] def getIndexedMap(
input: com.typesafe.config.Config
): Map[Chunk[KeyComponent], String] = {
Expand Down
18 changes: 18 additions & 0 deletions typesafe/shared/src/main/scala/zio/config/typesafe/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,24 @@ package object typesafe {
enableCommaSeparatedValueAsList: Boolean = false
): ConfigProvider =
TypesafeConfigProvider.fromTypesafeConfig(rawConfig, enableCommaSeparatedValueAsList)

def fromResourcePathZIO(enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
TypesafeConfigProvider.fromResourcePathZIO(enableCommaSeparatedValueAsList)

def fromHoconFileZIO(file: File, enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
TypesafeConfigProvider.fromHoconFileZIO(file, enableCommaSeparatedValueAsList)

def fromHoconFilePathZIO(filePath: String, enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
TypesafeConfigProvider.fromHoconFilePathZIO(filePath, enableCommaSeparatedValueAsList)

def fromHoconStringZIO(input: String, enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
TypesafeConfigProvider.fromHoconStringZIO(input, enableCommaSeparatedValueAsList)

def fromTypesafeConfigZIO(
rawConfig: com.typesafe.config.Config,
enableCommaSeparatedValueAsList: Boolean = false
): Task[ConfigProvider] =
TypesafeConfigProvider.fromTypesafeConfigZIO(rawConfig, enableCommaSeparatedValueAsList)
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package zio.config.yaml

import scala.annotation.nowarn
import org.snakeyaml.engine.v2.api.{Load, LoadSettings}
import zio.config._
import zio.config.syntax.IndexKey
import zio.{Chunk, ConfigProvider}
import zio.{Chunk, ConfigProvider, Task, ZIO}

import java.io.{BufferedReader, ByteArrayInputStream, File, FileInputStream, InputStreamReader, Reader}
import java.lang.{Boolean => JBoolean, Double => JDouble, Float => JFloat, Integer => JInteger, Long => JLong}
Expand All @@ -13,12 +12,8 @@ import java.nio.file.Path
import java.{util => ju}
import scala.jdk.CollectionConverters._

@nowarn("cat=unused-imports")
object YamlConfigProvider {

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

/**
* Retrieve a `ConfigSource` from yaml path.
*
Expand Down Expand Up @@ -111,6 +106,24 @@ object YamlConfigProvider {
fromYamlReader(new BufferedReader(new InputStreamReader(configStream)), enableCommaSeparatedValueAsList)
}

def fromYamlFileZIO(file: File, enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
ZIO.attempt(fromYamlFile(file, enableCommaSeparatedValueAsList))

def fromYamlPathZIO(path: Path, enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
ZIO.attempt(fromYamlPath(path, enableCommaSeparatedValueAsList))

def fromYamlReaderZIO(
reader: Reader,
enableCommaSeparatedValueAsList: Boolean = false
): Task[ConfigProvider] =
ZIO.attempt(fromYamlReader(reader, enableCommaSeparatedValueAsList))

def fromYamlStringZIO(
yamlString: String,
enableCommaSeparatedValueAsList: Boolean = false
): Task[ConfigProvider] =
ZIO.attempt(fromYamlString(yamlString, enableCommaSeparatedValueAsList))

private[yaml] def getIndexedConfigProvider(
data: AnyRef,
enableCommaSeparatedValueAsList: Boolean = false
Expand Down
18 changes: 18 additions & 0 deletions yaml/shared/src/main/scala/zio/config/yaml/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ package object yaml {
enableCommaSeparatedValueAsList: Boolean = false
): ConfigProvider =
YamlConfigProvider.getIndexedConfigProvider(loadYaml(repr), enableCommaSeparatedValueAsList)

def fromYamlFileZIO(file: File, enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
YamlConfigProvider.fromYamlFileZIO(file, enableCommaSeparatedValueAsList)

def fromYamlPathZIO(path: Path, enableCommaSeparatedValueAsList: Boolean = false): Task[ConfigProvider] =
YamlConfigProvider.fromYamlPathZIO(path, enableCommaSeparatedValueAsList)

def fromYamlReaderZIO(
reader: Reader,
enableCommaSeparatedValueAsList: Boolean = false
): Task[ConfigProvider] =
YamlConfigProvider.fromYamlReaderZIO(reader, enableCommaSeparatedValueAsList)

def fromYamlStringZIO(
yamlString: String,
enableCommaSeparatedValueAsList: Boolean = false
): Task[ConfigProvider] =
YamlConfigProvider.fromYamlStringZIO(yamlString, enableCommaSeparatedValueAsList)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ object YamlConfigSpec extends ZIOSpecDefault {
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)))
},
test("save read yaml config") {
final case class DataBaseConfig(url: String)
val configDataBaseConfig: Config[DataBaseConfig] = Config.string("url").to[DataBaseConfig]

val yamlConfig: String = s"""url: "some_url""""
val result = read(configDataBaseConfig from ConfigProvider.fromYamlStringZIO(yamlConfig))
val expected = DataBaseConfig("some_url")
assertZIO(result)(equalTo(expected))
}
)
}

0 comments on commit 013712e

Please sign in to comment.