Skip to content

Commit

Permalink
Add getConfigChanges to show the default and current config values
Browse files Browse the repository at this point in the history
  • Loading branch information
xerial committed Sep 6, 2016
1 parent 9e822dd commit 95614ef
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 16 deletions.
37 changes: 31 additions & 6 deletions wvlet-config/src/main/scala/wvlet/config/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package wvlet.config
import java.io.{File, FileInputStream, FileNotFoundException}
import java.util.Properties

import wvlet.config.PropertiesConfig.ConfigKey
import wvlet.config.YamlReader.loadMapOf
import wvlet.log.LogSupport
import wvlet.log.io.IOUtil
Expand Down Expand Up @@ -75,6 +76,9 @@ case class ConfigEnv(env: String, defaultEnv: String, configPaths: Seq[String])
def withConfigPaths(paths: Seq[String]): ConfigEnv = ConfigEnv(env, defaultEnv, paths)
}

case class ConfigChange(tpe:ObjectType, key:ConfigKey, default:Any, current:Any) {
override def toString = s"[${tpe}] ${key} = ${current} (default = ${default})"
}

import Config._

Expand All @@ -93,12 +97,25 @@ case class Config private[config](env: ConfigEnv, holder: Map[ObjectType, Config
def getAll: Seq[ConfigHolder] = holder.values.toSeq
override def iterator: Iterator[ConfigHolder] = holder.values.iterator

def getConfigChanges : Seq[ConfigChange] = {
val b = Seq.newBuilder[ConfigChange]
for(c <- getAll) {
val defaultProps = PropertiesConfig.toConfigProperties(c.tpe, getDefaultValueOf(c.tpe))
val currentProps = PropertiesConfig.toConfigProperties(c.tpe, c.value)

for((k, props) <- defaultProps.groupBy(_.key); defaultValue <- props; current <- currentProps.filter(x => x.key == k)) {
b += ConfigChange(c.tpe, k, defaultValue.v, current.v)
}
}
b.result
}

private def find[A](tpe: ObjectType): Option[Any] = {
holder.get(tpe).map(_.value)
}

def of[ConfigType](implicit tag: ru.TypeTag[ConfigType]): ConfigType = {
val t = ObjectType.ofTypeTag(tag)
def of[ConfigType: ru.TypeTag]: ConfigType = {
val t = ObjectType.ofTypeTag(implicitly[ru.TypeTag[ConfigType]])
find(t) match {
case Some(x) =>
x.asInstanceOf[ConfigType]
Expand All @@ -107,6 +124,16 @@ case class Config private[config](env: ConfigEnv, holder: Map[ObjectType, Config
}
}

def defaultValueOf[ConfigType: ru.TypeTag] : ConfigType = {
val tpe = ObjectType.ofTypeTag(implicitly[ru.TypeTag[ConfigType]])
getDefaultValueOf(tpe).asInstanceOf[ConfigType]
}

private def getDefaultValueOf(tpe:ObjectType) : Any = {
// Create the default object of this ConfigType
ObjectBuilder(tpe.rawType).build
}

def +(h: ConfigHolder): Config = Config(env, this.holder + (h.tpe -> h))
def ++(other: Config): Config = {
Config(env, this.holder ++ other.holder)
Expand All @@ -124,9 +151,7 @@ case class Config private[config](env: ConfigEnv, holder: Map[ObjectType, Config
*/
def registerDefault[ConfigType: ru.TypeTag] : Config = {
val tpe = ObjectType.ofTypeTag(implicitly[ru.TypeTag[ConfigType]])
// Create the default object of this ConfigType
val c = ObjectBuilder(tpe.rawType).build
this + ConfigHolder(tpe, c)
this + ConfigHolder(tpe, defaultValueOf[ConfigType])
}

def registerFromYaml[ConfigType: ru.TypeTag : ClassTag](yamlFile: String): Config = {
Expand Down Expand Up @@ -171,7 +196,7 @@ case class Config private[config](env: ConfigEnv, holder: Map[ObjectType, Config
}

def overrideWithProperties(props: Properties, onUnusedProperties: Properties => Unit = REPORT_UNUSED_PROPERTIES): Config = {
ConfigOverwriter.overrideWithProperties(this, props, onUnusedProperties)
PropertiesConfig.overrideWithProperties(this, props, onUnusedProperties)
}

def overrideWithPropertiesFile(propertiesFile: String, onUnusedProperties: Properties => Unit = REPORT_UNUSED_PROPERTIES): Config = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,23 @@ import wvlet.obj.ObjectBuilder.CanonicalNameFormatter
import wvlet.obj.{ObjectBuilder, ObjectSchema, ObjectType, TaggedObjectType}

import scala.util.{Failure, Success, Try}
import scala.reflect.runtime.{universe => ru}

/**
* Allow overwriting config objects with Java Properties
* Helper class to overwrite config objects using Java Properties
*/
object ConfigOverwriter extends LogSupport {
object PropertiesConfig extends LogSupport {

private[config] case class Prefix(prefix:String, tag: Option[String]) {
case class Prefix(prefix:String, tag: Option[String]) {
override def toString = tag match {
case Some(t) => s"${prefix}@${t}"
case None => prefix
}
}
private[config] case class ConfigKey(prefix: Prefix, param: String) {
case class ConfigKey(prefix: Prefix, param: String) {
override def toString = s"${prefix}.${param}"
}
private[config] case class ConfigProperty(key: ConfigKey, v: Any)
case class ConfigProperty(key: ConfigKey, v: Any)

private[config] def extractPrefix(t: ObjectType): Prefix = {
def canonicalize(s: String): String = {
Expand Down Expand Up @@ -69,16 +70,16 @@ object ConfigOverwriter extends LogSupport {
}
}

private[config] def configToProps(configHolder: ConfigHolder): Seq[ConfigProperty] = {
val prefix = extractPrefix(configHolder.tpe)
val schema = ObjectSchema.of(configHolder.tpe)
private[config] def toConfigProperties(tpe:ObjectType, config: Any): Seq[ConfigProperty] = {
val prefix = extractPrefix(tpe)
val schema = ObjectSchema.of(tpe)
val b = Seq.newBuilder[ConfigProperty]
for (p <- schema.parameters) yield {
val key = ConfigKey(prefix, CanonicalNameFormatter.format(p.name))
Try(p.get(configHolder.value)) match {
Try(p.get(config)) match {
case Success(v) => b += ConfigProperty(key, v)
case Failure(e) =>
warn(s"Failed to read parameter ${p} from ${configHolder}")
warn(s"Failed to read parameter ${p} from ${config}")
}
}
b.result()
Expand Down
25 changes: 25 additions & 0 deletions wvlet-config/src/test/scala/wvlet/config/ConfigTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,31 @@ class ConfigTest extends WvletSpec {
c2 shouldBe DefaultConfig(1, "world")
}

"show the default configuration" in {
val config = Config(env = "default", configPaths = configPaths)
.registerFromYaml[SampleConfig]("myconfig.yml")

val default = config.defaultValueOf[SampleConfig]
val current = config.of[SampleConfig]

info(s"default: ${default}, current: ${current}")

val changes = config.getConfigChanges
changes.size shouldBe 2
info(s"changes:\n${changes.mkString("\n")}")
val keys : Seq[String] = changes.map(_.key.toString)
keys should contain ("sample.id")
keys should contain ("sample.fullname")

val id = changes.find(_.key.toString == "sample.id").get
id.default shouldBe 0
id.current shouldBe 1

val fullname = changes.find(_.key.toString == "sample.fullname").get
fullname.default shouldBe ""
fullname.current shouldBe "default-config"
}

"override values with properties" taggedAs ("props") in {
val p = new Properties
p.setProperty("sample.id", "10")
Expand Down

0 comments on commit 95614ef

Please sign in to comment.