Skip to content

Commit a33d838

Browse files
Merge pull request #1256 from hkateu/cirisconfiguration
Cirisconfiguration
2 parents f840006 + e4eef71 commit a33d838

File tree

5 files changed

+215
-2
lines changed

5 files changed

+215
-2
lines changed

build.sbt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,10 +558,13 @@ lazy val scala_libraries_config = (project in file("scala-libraries-config"))
558558
munitDep,
559559
"com.github.japgolly.clearconfig" %% "core" % "3.1.0",
560560
catsEffect,
561-
"io.circe" %% "circe-yaml" % "1.15.0",
561+
"io.circe" %% "circe-yaml" % "0.15.1",
562562
circeDep,
563563
circeParserDep
564-
)
564+
),
565+
libraryDependencies += "is.cir" %% "ciris" % "3.5.0",
566+
libraryDependencies += "is.cir" %% "ciris-circe" % "3.5.0",
567+
libraryDependencies += "is.cir" %% "ciris-circe-yaml" % "3.5.0"
565568
)
566569

567570
lazy val scala_strings = (project in file("scala-strings"))
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"username": "username",
3+
"password": "password"
4+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
username: username
2+
password: password
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package cirisconfig
2+
3+
import ciris.*
4+
import cats.syntax.all.*
5+
import cats.effect.*
6+
import java.nio.file.Path
7+
import ciris.circe.circeConfigDecoder
8+
import io.circe.Decoder
9+
import cats.effect.Async
10+
import ciris.circe.yaml.circeYamlConfigDecoder
11+
12+
object Configuration:
13+
14+
final case class PostgresConfig(username: String, password: String)
15+
16+
def postgresConfig: ConfigValue[Effect, PostgresConfig] =
17+
(
18+
env("POSTGRES_USERNAME").as[String],
19+
env("POSTGRES_PASSWORD").as[String]
20+
).parMapN(PostgresConfig.apply)
21+
22+
final case class PostgresConfig2(
23+
username: Option[String],
24+
password: Option[String]
25+
)
26+
27+
def postgresConfig2: ConfigValue[Effect, PostgresConfig2] =
28+
(
29+
env("POSTGRES_USERNAME").as[String].option,
30+
env("POSTGRES_PASSWORD").as[String].option
31+
).parMapN(PostgresConfig2.apply)
32+
33+
case class Username(name: String)
34+
object Username:
35+
given ConfigDecoder[String, Username] =
36+
ConfigDecoder[String, String].map(Username.apply)
37+
38+
case class Password(value: String)
39+
object Password:
40+
given ConfigDecoder[String, Password] =
41+
ConfigDecoder[String, String].map(Password.apply)
42+
43+
final case class PostgresConfig3(
44+
username: Option[Username],
45+
password: Option[Password]
46+
)
47+
48+
def postgresConfig3: ConfigValue[Effect, PostgresConfig3] =
49+
(
50+
env("POSTGRES_USERNAME").as[Username].option,
51+
env("POSTGRES_PASSWORD").as[Password].option
52+
).parMapN(PostgresConfig3.apply)
53+
54+
final case class PostgresConfig4(username: Username, password: Password)
55+
56+
object PostgresConfig4:
57+
// given ConfigDecoder[String, PostgresConfig4] =
58+
// circeConfigDecoder("PostgresConfig4")
59+
60+
given ConfigDecoder[String, PostgresConfig4] =
61+
circeYamlConfigDecoder("PostgresConfig4")
62+
63+
given Decoder[PostgresConfig4] = Decoder.instance { h =>
64+
for
65+
username <- h.get[String]("username")
66+
password <- h.get[String]("password")
67+
yield PostgresConfig4(Username(username), Password(password))
68+
}
69+
70+
val postgresConfig4: ConfigValue[Effect, PostgresConfig4] =
71+
file(
72+
Path.of("src/main/resources/postgresConfig.json")
73+
).as[PostgresConfig4]
74+
75+
def postgresConfig5[F[_]: Async]: F[Either[ConfigError, PostgresConfig4]] =
76+
file(
77+
Path.of("src/main/resources/postgresConfig.json")
78+
).as[PostgresConfig4].attempt[F]
79+
80+
def postgresConfig6[F[_]: Async]: F[Either[ConfigError, PostgresConfig4]] =
81+
file(
82+
Path.of("src/main/resources/postgresConfig.yaml")
83+
).as[PostgresConfig4].attempt[F]
84+
85+
// handling secrets
86+
case class Password2(value: Secret[String])
87+
object Password2:
88+
def apply(value: String) =
89+
new Password2(Secret(value))
90+
91+
def postgresConfig7[F[_]: Async]: F[Either[ConfigError, PostgresConfig4]] =
92+
file(
93+
Path.of("src/main/resources/missing.yaml")
94+
).as[PostgresConfig4]
95+
.default {
96+
PostgresConfig4(Username("username"), Password("password"))
97+
}
98+
.attempt[F]
99+
100+
object program extends IOApp.Simple:
101+
import Configuration.*
102+
103+
override def run: IO[Unit] =
104+
// blows up application
105+
// postgresConfig.load[IO].map(println).void
106+
107+
// handled errors
108+
// postgresConfig2.load[IO].map(println).void
109+
110+
// added typeclasses
111+
// postgresConfig3.load[IO].map(println).void
112+
113+
// config loading from json file
114+
// postgresConfig4.load[IO].map(println).void
115+
116+
// managing errors
117+
// postgresConfig5[IO].map{config =>
118+
// config match
119+
// case Right(value) => println(value)
120+
// case Left(err) => err.messages.map(println)
121+
// }
122+
123+
// config loading from yaml file
124+
// postgresConfig6[IO].map{config =>
125+
// config match
126+
// case Right(value) => println(value)
127+
// case Left(err) => err.messages.map(println)
128+
// }
129+
130+
// config loading with secret
131+
// IO(println(Password2(Secret("password"))))
132+
133+
// config loading with fallback values
134+
postgresConfig7[IO].map { config =>
135+
config match
136+
case Right(value) => println(value)
137+
case Left(err) => err.messages.map(println)
138+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package cirisconfig
2+
3+
import org.scalatest.flatspec.AnyFlatSpec
4+
import org.scalatest.matchers.should.Matchers
5+
import org.scalatest.prop.TableDrivenPropertyChecks
6+
import Configuration.*
7+
import ciris.*
8+
import cats.effect.IO
9+
10+
class ConfigurationSuite
11+
extends AnyFlatSpec
12+
with Matchers
13+
with TableDrivenPropertyChecks:
14+
15+
"postgresConfig" should "be of type ConfigValue[Effect, PostgresConfig]" in {
16+
postgresConfig shouldBe an[ConfigValue[Effect, PostgresConfig]]
17+
}
18+
19+
"postgresConfig2" should "be of type ConfigValue[Effect, PostgresConfig2]" in {
20+
postgresConfig2 shouldBe an[ConfigValue[Effect, PostgresConfig2]]
21+
}
22+
23+
"postgresConfig3" should "be of type ConfigValue[Effect, PostgresConfig3]" in {
24+
postgresConfig3 shouldBe an[ConfigValue[Effect, PostgresConfig3]]
25+
}
26+
27+
"postgresConfig4, " should "be of type ConfigValue[Effect, PostgresConfig4]" in {
28+
postgresConfig4 shouldBe an[ConfigValue[Effect, PostgresConfig4]]
29+
}
30+
31+
"postgresConfig5, postgresConfig6, postgresConfig7" should "be of type IO[Either[ConfigError, PostgresConfig4]]" in {
32+
forAll(
33+
Table(
34+
"postgresConfig567",
35+
postgresConfig5[IO],
36+
postgresConfig6[IO],
37+
postgresConfig7[IO]
38+
)
39+
) { v =>
40+
v shouldBe an[IO[Either[ConfigError, PostgresConfig4]]]
41+
}
42+
}
43+
44+
"PostgresConfig" should "be of contain type String" in {
45+
val pconfig = PostgresConfig("u", "p")
46+
pconfig.username shouldBe an[String]
47+
pconfig.password shouldBe an[String]
48+
}
49+
50+
"PostgresConfig2" should "be of contain type Option[String]" in {
51+
val pconfig2 = PostgresConfig2(Some("u"), Some("p"))
52+
pconfig2.username shouldBe an[Option[String]]
53+
pconfig2.password shouldBe an[Option[String]]
54+
}
55+
56+
"PostgresConfig3" should "be of contain type Option[Username] and Option[Password]" in {
57+
val pconfig3 = PostgresConfig3(Some(Username("u")), Some(Password("p")))
58+
pconfig3.username shouldBe an[Option[Username]]
59+
pconfig3.password shouldBe an[Option[Password]]
60+
}
61+
62+
"PostgresConfig4" should "be of contain type Username and Password" in {
63+
val pconfig4 = PostgresConfig4(Username("u"), Password("p"))
64+
pconfig4.username shouldBe an[Username]
65+
pconfig4.password shouldBe an[Password]
66+
}

0 commit comments

Comments
 (0)