Skip to content

Commit 07f9924

Browse files
committed
resolve #49 "Fully validate given config on construction"
1 parent 4eea0bf commit 07f9924

File tree

6 files changed

+119
-42
lines changed

6 files changed

+119
-42
lines changed

build.sbt

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ coverageHighlighting := { scalaBinaryVersion.value == "2.11" }
2828
lazy val codeDefs = taskKey[Unit]("Copies code definitions to resources/")
2929
codeDefs := {
3030
for (ext Seq("java", "scala")) {
31-
val src = s"src/main/$ext/tscfg/codeDefs"
31+
val src = s"src/main/$ext/tscfg/codeDefs/resources/"
3232
val dst = s"src/main/resources/codeDefs/"
3333
println(s"Copying $src to $dst")
3434
IO.copyDirectory(file(src), file(dst))

changelog.md

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
2019-07-27 - 0.9.92
22

3-
- toward #49 "Fully validate given config on construction"
4-
- java: throw a com.typesafe.config.ConfigException with all the
5-
com.typesafe.config.ConfigException's collected in its message
6-
- scala: done as well but TODO needs cleanup (check that $_reqConfig
7-
is only output if called)
8-
Note: option `scala:fp` removed: full paths always reported
3+
- resolve #49 "Fully validate given config on construction"
4+
- for both java and scala, wrapper construction will now throw a
5+
com.typesafe.config.ConfigException with a summary of all the
6+
com.typesafe.config.ConfigException's collected as it traverses
7+
the required config entries. Example of such summary:
8+
9+
Invalid configuration:
10+
'service.poolSize': com.typesafe.config.ConfigException$Missing(No configuration setting found for key 'poolSize')
11+
'service.url': com.typesafe.config.ConfigException$Missing(No configuration setting found for key 'url')
12+
'service.debug': com.typesafe.config.ConfigException$WrongType(String: 5: debug has type NUMBER rather than BOOLEAN)
13+
'service.doLog': com.typesafe.config.ConfigException$WrongType(String: 6: doLog has type STRING rather than BOOLEAN)
14+
'service.factor': com.typesafe.config.ConfigException$WrongType(String: 7: factor has type BOOLEAN rather than NUMBER)
15+
16+
- NOTE: option `scala:fp` removed: full paths are now always reported.
17+
- TODO some cleanup (check that $_reqConfig is only output if called)
918

1019
- capture java and scala wrapper supporting methods in proper classes
1120
to facilitate validation at compile time.

src/main/java/tscfg/codeDefs/JavaDefs.java src/main/java/tscfg/codeDefs/resources/JavaDefs.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// $COVERAGE-OFF$
2-
package tscfg.codeDefs;
2+
package tscfg.codeDefs.resources;
33

44
/**
55
* Captures various definitions to be included in the generated wrapper.

src/main/scala/tscfg/codeDefs/ScalaDefs.scala src/main/scala/tscfg/codeDefs/resources/ScalaDefs.scala

+22-25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// $COVERAGE-OFF$
2-
package tscfg.codeDefs
2+
package tscfg.codeDefs.resources
33

44
/**
55
* Captures various definitions to be included in the generated wrapper.
@@ -11,20 +11,17 @@ object ScalaDefs {
1111

1212
//<$TsCfgValidator>
1313
private final class $TsCfgValidator {
14-
private val undefinedPaths = scala.collection.mutable.ArrayBuffer[java.lang.String]()
14+
private val badPaths = scala.collection.mutable.ArrayBuffer[java.lang.String]()
1515

16-
def addUndefinedPath(path: java.lang.String): Unit = {
17-
undefinedPaths += path
16+
def addBadPath(path: java.lang.String, e: com.typesafe.config.ConfigException): Unit = {
17+
badPaths += s"'$path': ${e.getClass.getName}(${e.getMessage})"
1818
}
1919

2020
def validate(): Unit = {
21-
if (undefinedPaths.nonEmpty) {
22-
throw new RuntimeException(
23-
undefinedPaths.map(path s"`$path`").mkString(
24-
"Undefined paths in given configuration: ",
25-
", ", ""
26-
)
27-
)
21+
if (badPaths.nonEmpty) {
22+
throw new com.typesafe.config.ConfigException(
23+
badPaths.mkString("Invalid configuration:\n ", "\n ", "")
24+
){}
2825
}
2926
}
3027
}
@@ -42,8 +39,8 @@ object ScalaDefs {
4239
if (c == null) null
4340
else try c.getConfig(path)
4441
catch {
45-
case _:com.typesafe.config.ConfigException.Missing
46-
$tsCfgValidator.addUndefinedPath(parentPath + path)
42+
case e:com.typesafe.config.ConfigException
43+
$tsCfgValidator.addBadPath(parentPath + path, e)
4744
null
4845
}
4946
}
@@ -54,8 +51,8 @@ object ScalaDefs {
5451
if (c == null) null
5552
else try c.getString(path)
5653
catch {
57-
case _:com.typesafe.config.ConfigException.Missing
58-
$tsCfgValidator.addUndefinedPath(parentPath + path)
54+
case e:com.typesafe.config.ConfigException
55+
$tsCfgValidator.addBadPath(parentPath + path, e)
5956
null
6057
}
6158
}
@@ -66,8 +63,8 @@ object ScalaDefs {
6663
if (c == null) 0
6764
else try c.getInt(path)
6865
catch {
69-
case _:com.typesafe.config.ConfigException.Missing
70-
$tsCfgValidator.addUndefinedPath(parentPath + path)
66+
case e:com.typesafe.config.ConfigException
67+
$tsCfgValidator.addBadPath(parentPath + path, e)
7168
0
7269
}
7370
}
@@ -78,8 +75,8 @@ object ScalaDefs {
7875
if (c == null) false
7976
else try c.getBoolean(path)
8077
catch {
81-
case _:com.typesafe.config.ConfigException.Missing
82-
$tsCfgValidator.addUndefinedPath(parentPath + path)
78+
case e:com.typesafe.config.ConfigException
79+
$tsCfgValidator.addBadPath(parentPath + path, e)
8380
false
8481
}
8582
}
@@ -90,8 +87,8 @@ object ScalaDefs {
9087
if (c == null) 0
9188
else try c.getDouble(path)
9289
catch {
93-
case _:com.typesafe.config.ConfigException.Missing
94-
$tsCfgValidator.addUndefinedPath(parentPath + path)
90+
case e:com.typesafe.config.ConfigException
91+
$tsCfgValidator.addBadPath(parentPath + path, e)
9592
0
9693
}
9794
}
@@ -102,8 +99,8 @@ object ScalaDefs {
10299
if (c == null) 0
103100
else try c.getLong(path)
104101
catch {
105-
case _:com.typesafe.config.ConfigException.Missing
106-
$tsCfgValidator.addUndefinedPath(parentPath + path)
102+
case e:com.typesafe.config.ConfigException
103+
$tsCfgValidator.addBadPath(parentPath + path, e)
107104
0
108105
}
109106
}
@@ -114,8 +111,8 @@ object ScalaDefs {
114111
if (c == null) 0
115112
else try c.getBytes(path)
116113
catch {
117-
case _:com.typesafe.config.ConfigException.Missing
118-
$tsCfgValidator.addUndefinedPath(parentPath + path)
114+
case e:com.typesafe.config.ConfigException
115+
$tsCfgValidator.addBadPath(parentPath + path, e)
119116
0
120117
}
121118
}

src/test/scala/tscfg/generators/java/JavaMainSpec.scala

+1-5
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,6 @@ class JavaMainSpec extends Specification {
597597
c.i === Optional.empty()
598598
}
599599

600-
/*
601600
"example 2" in {
602601
val c = new JavaIssue41Cfg(ConfigFactory.parseString(
603602
"""
@@ -622,10 +621,9 @@ class JavaMainSpec extends Specification {
622621
c.c.f.get().h === "c.f.h"
623622
c.i.get() === List(1.0,2.0,3.0).asJava
624623
}
625-
*/
626624
}
627625

628-
"issue47 (assumeAllRequired)" should {
626+
"issue 49 (using issue47.spec.conf --all-required)" should {
629627
"fail with missing service entry" in {
630628
def a: Unit = new JavaIssue47Cfg(ConfigFactory.parseString(""))
631629
a must throwA[com.typesafe.config.ConfigException].like {
@@ -684,7 +682,6 @@ class JavaMainSpec extends Specification {
684682
|}""".stripMargin))
685683
a must throwA[com.typesafe.config.ConfigException].like {
686684
case e: com.typesafe.config.ConfigException
687-
println(s"wrong types message:\n${e.getMessage}\n")
688685
forall(List("poolSize", "debug", "doLog", "factor")) { k
689686
e.getMessage must contain(s"'service.$k': com.typesafe.config.ConfigException$$WrongType")
690687
}
@@ -697,7 +694,6 @@ class JavaMainSpec extends Specification {
697694
|""".stripMargin))
698695
a must throwA[com.typesafe.config.ConfigException].like {
699696
case e: com.typesafe.config.ConfigException
700-
println(s"wrong type for object message:\n${e.getMessage}\n")
701697
e.getMessage must contain("'service': com.typesafe.config.ConfigException$WrongType")
702698
}
703699
}

src/test/scala/tscfg/generators/scala/ScalaMainSpec.scala

+79-4
Original file line numberDiff line numberDiff line change
@@ -519,10 +519,9 @@ class ScalaMainSpec extends Specification {
519519
"issue 36" should {
520520
def a = ScalaIssue36Cfg(ConfigFactory.parseString("obj.baz.bar = quz"))
521521
"report full path for missing required parameter 'obj.foo.bar'" in {
522-
a must throwA[java.lang.RuntimeException].like {
523-
case e: java.lang.RuntimeException
524-
e.getMessage must contain(s"Undefined paths in given configuration")
525-
e.getMessage must contain(s"`obj.foo.bar`")
522+
a must throwA[com.typesafe.config.ConfigException].like {
523+
case e: com.typesafe.config.ConfigException
524+
e.getMessage must contain("'obj.foo.bar': com.typesafe.config.ConfigException$Missing")
526525
}
527526
}
528527
}
@@ -534,4 +533,80 @@ class ScalaMainSpec extends Specification {
534533
c.memory === 53687091200L
535534
}
536535
}
536+
537+
"issue 49 (using issue47.spec.conf --all-required)" should {
538+
"fail with missing service entry" in {
539+
def a: Unit = ScalaIssue47Cfg(ConfigFactory.parseString(""))
540+
a must throwA[com.typesafe.config.ConfigException].like {
541+
case e: com.typesafe.config.ConfigException
542+
e.getMessage must contain("'service': com.typesafe.config.ConfigException$Missing")
543+
}
544+
}
545+
"fail with missing url entry" in {
546+
def a: Unit = ScalaIssue47Cfg(ConfigFactory.parseString(
547+
"""
548+
|service {
549+
| # url = "http://example.net/rest"
550+
| poolSize = 32
551+
| debug = true
552+
| doLog = false
553+
| factor = 0.75
554+
|}""".stripMargin))
555+
a must throwA[com.typesafe.config.ConfigException].like {
556+
case e: com.typesafe.config.ConfigException
557+
e.getMessage must contain("'service.url': com.typesafe.config.ConfigException$Missing")
558+
}
559+
}
560+
"fail with missing poolSize entry" in {
561+
def a: Unit = ScalaIssue47Cfg(ConfigFactory.parseString(
562+
"""
563+
|service {
564+
| url = "http://example.net/rest"
565+
| # poolSize = 32
566+
| debug = true
567+
| doLog = false
568+
| factor = 0.75
569+
|}""".stripMargin))
570+
a must throwA[com.typesafe.config.ConfigException].like {
571+
case e: com.typesafe.config.ConfigException
572+
e.getMessage must contain("'service.poolSize': com.typesafe.config.ConfigException$Missing")
573+
}
574+
}
575+
"fail with all entries missing in service object" in {
576+
def a: Unit = ScalaIssue47Cfg(ConfigFactory.parseString("service {}"))
577+
a must throwA[com.typesafe.config.ConfigException].like {
578+
case e: com.typesafe.config.ConfigException
579+
forall(List("url", "poolSize", "debug", "doLog", "factor")) { k
580+
e.getMessage must contain(s"'service.$k': com.typesafe.config.ConfigException$$Missing")
581+
}
582+
}
583+
}
584+
"fail with wrong types" in {
585+
def a: Unit = ScalaIssue47Cfg(ConfigFactory.parseString(
586+
"""
587+
|service {
588+
| url = 31 # anything can be a string, so not check on this one.
589+
| poolSize = true
590+
| debug = 3
591+
| doLog = "str"
592+
| factor = false
593+
|}""".stripMargin))
594+
a must throwA[com.typesafe.config.ConfigException].like {
595+
case e: com.typesafe.config.ConfigException
596+
forall(List("poolSize", "debug", "doLog", "factor")) { k
597+
e.getMessage must contain(s"'service.$k': com.typesafe.config.ConfigException$$WrongType")
598+
}
599+
}
600+
}
601+
"fail with wrong type for object" in {
602+
def a: Unit = ScalaIssue47Cfg(ConfigFactory.parseString(
603+
"""
604+
|service = 1
605+
|""".stripMargin))
606+
a must throwA[com.typesafe.config.ConfigException].like {
607+
case e: com.typesafe.config.ConfigException
608+
e.getMessage must contain("'service': com.typesafe.config.ConfigException$WrongType")
609+
}
610+
}
611+
}
537612
}

0 commit comments

Comments
 (0)