Skip to content

Commit

Permalink
Merge pull request #18 from Sqooba/0.5.1
Browse files Browse the repository at this point in the history
0.5.1
  • Loading branch information
pietrotull committed May 2, 2019
2 parents 5aca6df + f21a1dd commit 1232742
Show file tree
Hide file tree
Showing 14 changed files with 246 additions and 77 deletions.
4 changes: 1 addition & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ jdk:
branches:
only:
- master
- 0.5.0
- 0.5.1

cache:
directories:
- $HOME/.ivy2/cache
- $HOME/.sbt/boot
- $HOME/.coursier


script: "sbt clean coverage test"
after_success: "sbt coverageReport coveralls"
11 changes: 5 additions & 6 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
organization := "io.sqooba"
scalaVersion := "2.11.12"
scalaVersion := "2.13.0-M5"
name := "sq-conf"
description := "Unified configuration interface."
homepage := Some(url("https://github.com/Sqooba/sq-conf"))
licenses := Seq("Apache 2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0"))

crossScalaVersions := Seq("2.12.8", "2.11.12")
crossScalaVersions := Seq("2.13.0-M5", "2.12.8", "2.11.12")

libraryDependencies ++= Seq(
"com.typesafe" % "config" % "1.3.3",
"com.typesafe.scala-logging" %% "scala-logging" % "3.7.2",
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.0",
"ch.qos.logback" % "logback-classic" % "1.2.3" % Test,
"org.scalatest" %% "scalatest" % "3.0.3" % Test,
"org.scalatest" %% "scalatest" % "3.0.7" % Test,
"org.mockito" % "mockito-all" % "1.10.19" % Test
)

excludeDependencies ++= Seq("org.slf4j" % "slf4j-log4j12", "log4j" % "log4j")

parallelExecution in Test := false

coverageHighlighting := true
publishMavenStyle := true
publishArtifact in Test := false
pomIncludeRepository := { _ => false }
parallelExecution in Test := false

publishTo := Some(sonatypeDefaultResolver.value)

Expand Down
1 change: 0 additions & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
sbt.version=1.2.8

22 changes: 19 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,23 @@ object.
javaWrapper.getString("some.testStringValue") // returns java.lang.String
```

## Configure order of preference
Order of preference defines in which order configuration keys are read. By default the order goes:
1. valueOverrides given during creation of config
2. environment variables
3. config file
Order can be configured freely, for example:
```
val orderOfPreference: List[OrderOfPreference.Value] = List(
OrderOfPreference.CONF_FIlE,
OrderOfPreference.ENV_VARIABLE,
OrderOfPreference.VALUE_OVERRIDES)
val conf = new SqConf().configureOrder(oop)
```
Now order would be reversed in comparison to the default.

## Getting started
Sq-conf is cross compiled for scala 2.11 and 2.12. For the latest of the latest, sq-conf snapshots
Sq-conf is cross compiled for scala 2.11, 2.12 and 2.13. For the latest of the latest, sq-conf snapshots
are available in sonatype repo:
[sonatype-snapshots](https://oss.sonatype.org/content/repositories/snapshots/io/sqooba/).
Add sqConf to your project, with sbt add this simple line:
Expand All @@ -81,15 +96,16 @@ Sq-conf works also with maven. Just add this to your pom.xml:
<dependency>
<groupId>io.sqooba</groupId>
<artifactId>sq-conf_2.12</artifactId>
<version>0.4.2</version>
<version>0.5.0</version>
</dependency>
```

## Future Plans
- scala 2.13 support
- yml support
- test more complex type conversions from app.conf, also with java transformers

## Change History
- 0.5.1 Allow configuring order of preference, bug fix value overrides not being passed on to sub confix, scala 2.13 support
- 0.5.0 Add get config method to java wrapper, deployed first snapshot to sonatype repo.
- 0.4.2 New toProperties convenience method, transformer interface for java to transform from string to any type.
- 0.4.1 Remove dependency to option for the generic getter.
Expand Down
14 changes: 6 additions & 8 deletions src/main/scala/io/sqooba/conf/JavaSqConf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package io.sqooba.conf

import java.util

import scala.reflect.ClassTag

class JavaSqConf(sqConf: SqConf) {

def getSqConf: SqConf = sqConf
Expand All @@ -16,13 +18,11 @@ class JavaSqConf(sqConf: SqConf) {

def get[T](key: String): T = sqConf.get[T](key)

def getWithTransformer[T](key: String, transformer: Transformer[T]): T = {
sqConf.getValueForKey(key, transformer.transform(_))
}
def getWithTransformer[T: ClassTag](key: String, transformer: Transformer[T]): T =
sqConf.getValueAccordingOrderOfOfPreference(key, transformer.transform(_))

def getListWithTransformer[T](key: String, transformer: Transformer[T]): List[T] = {
def getListWithTransformer[T: ClassTag](key: String, transformer: Transformer[T]): List[T] =
sqConf.getListOf(key, transformer.transform(_), false)
}

def getIterable[T](key: String): java.lang.Iterable[T] = {
val list: List[T] = sqConf.getListOf[T](key)
Expand All @@ -43,7 +43,5 @@ class JavaSqConf(sqConf: SqConf) {

def toProperties: java.util.Properties = sqConf.toProperties()

def getConfig(path: String): JavaSqConf = {
sqConf.getConfig(path).asJava()
}
def getConfig(path: String): JavaSqConf = sqConf.getSubConfig(path).asJava()
}
146 changes: 103 additions & 43 deletions src/main/scala/io/sqooba/conf/SqConf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ import java.time.Duration
import java.util.Properties

import scala.collection.JavaConverters._
import scala.reflect.ClassTag

import com.typesafe.config._
import com.typesafe.config.impl.DurationParser
import com.typesafe.scalalogging.LazyLogging
import io.sqooba.conf.OrderOfPreference.OrderOfPreference

// Here we have nulls and avoid option on purpose so the java bindings work without scala standard lib
class SqConf(fileName: String = null,
file: File = null,
config: Config = null,
prefix: String = null,
valueOverrides: Map[String, String] = Map()) extends LazyLogging {
valueOverrides: Map[String, String] = Map(), orderOfPreference: List[OrderOfPreference] = SqConf.DEFAULT_ORDER_OF_PREFERENCe) extends LazyLogging {

def this() = this(null, null, null, null)

Expand All @@ -37,15 +39,17 @@ class SqConf(fileName: String = null,
}
}

def getInt(key: String): Int = getValueForKey[Int](key, x => x.toInt)
def getOrderOfPreference: List[OrderOfPreference] = orderOfPreference

def getString(key: String): String = getValueForKey[String](key, x => x)
def getInt(key: String): Int = getValueAccordingOrderOfOfPreference[Int](key, x => x.toInt)

def getBoolean(key: String): Boolean = getValueForKey[Boolean](key, x => x.toBoolean)
def getString(key: String): String = getValueAccordingOrderOfOfPreference[String](key, x => x)

def getLong(key: String): Long = getValueForKey[Long](key, x => x.toLong)
def getBoolean(key: String): Boolean = getValueAccordingOrderOfOfPreference[Boolean](key, x => x.toBoolean)

def getBigInt(key: String): BigInt = getValueForKey[BigInt](key, x => BigInt(x))
def getLong(key: String): Long = getValueAccordingOrderOfOfPreference[Long](key, x => x.toLong)

def getBigInt(key: String): BigInt = getValueAccordingOrderOfOfPreference[BigInt](key, x => BigInt(x))

def getDuration(key: String): Duration = {
val fullKey = buildKey(key)
Expand All @@ -59,14 +63,65 @@ class SqConf(fileName: String = null,
}
}

def getValueForKey[T](key: String, converter: String => T): T = {
def getValueAccordingOrderOfOfPreference[T: ClassTag](key: String, converter: String => T): T = {
var value: T = null.asInstanceOf[T]
orderOfPreference.takeWhile(oop => {
value = getValueForOrderOfOfPreferenceItem[T](key, oop, converter)
value == null
})
value
}

def getListOfValuesAccordingOrderOfPreference[T: ClassTag](key: String, converter: String => T): List[T] = {
var values: List[T] = List()

orderOfPreference.takeWhile(oop => {
values = getListOfValuesForOrderOfOfPreference[T](key, oop, converter)
values.isEmpty
})
values
}

def getListOfValuesForOrderOfOfPreference[T: ClassTag](key: String, oop: OrderOfPreference, converter: String => T): List[T] = {
val fullKey = buildKey(key)
if (valueOverrides.contains(fullKey)) {
converter(valueOverrides(fullKey))
} else {
System.getenv(keyAsEnv(fullKey)) match {
case null => converter(conf.getString(fullKey))
case env: String => converter(env)

def stringToT(string: String): List[T] = string.split(',').map(x => {
converter(x)
}).toList

oop match {
case OrderOfPreference.ENV_VARIABLE =>
System.getenv(keyAsEnv(fullKey)) match {
case null => List()
case env => stringToT(env)
}
case OrderOfPreference.CONF_FIlE => getListOf[T](fullKey, converter, false)
case OrderOfPreference.VALUE_OVERRIDES => {
if (valueOverrides.contains(fullKey)) {
stringToT(valueOverrides(fullKey))
} else {
List[T]()
}
}
}
}

def getValueForOrderOfOfPreferenceItem[T: ClassTag](key: String, oop: OrderOfPreference, converter: String => T): T = {
val fullKey = buildKey(key)

oop match {
case OrderOfPreference.ENV_VARIABLE =>
System.getenv(keyAsEnv(fullKey)) match {
case null => null.asInstanceOf[T]
case env => converter(env)
}
case OrderOfPreference.CONF_FIlE => converter(conf.getString(fullKey))
case OrderOfPreference.VALUE_OVERRIDES => {
if (valueOverrides.contains(fullKey)) {
converter(valueOverrides(fullKey))
} else {
null.asInstanceOf[T]
}
}
}
}
Expand All @@ -90,40 +145,23 @@ class SqConf(fileName: String = null,
})
}

def getListOf[T](key: String): List[T] = getListOf[T](key, null, true)
def getListOf[T](key: String): List[T] = getListOf[T](key, null, cast = true)

def getListOfInt(key: String): List[Int] = getListOfWithConversion(key, str => str.trim.toInt)
def getListOfInt(key: String): List[Int] = getListOfValuesAccordingOrderOfPreference[Int](key, str => str.trim.toInt)

def getListOfDouble(key: String): List[Double] =
getListOfWithConversion(key, str => str.trim.toDouble)
getListOfValuesAccordingOrderOfPreference[Double](key, str => str.trim.toDouble)

def getListOfLong(key: String): List[Long] =
getListOfWithConversion(key, str => str.trim.toLong)
getListOfValuesAccordingOrderOfPreference[Long](key, str => str.trim.toLong)

def getListOfString(key: String): List[String] = getListOfWithConversion(key, str => str.trim)
def getListOfString(key: String): List[String] = getListOfValuesAccordingOrderOfPreference[String](key, str => str.trim)

def getListOfBoolean(key: String): List[Boolean] =
getListOfWithConversion(key, str => str.trim.toBoolean)
getListOfValuesAccordingOrderOfPreference[Boolean](key, str => str.trim.toBoolean)

def getListOfDuration(key: String): List[Duration] = getListOfWithConversion[Duration](key, str =>
DurationParser.parseDurationString(str, key, "listOfDuration"), cast = false)

def getListOfWithConversion[T](key: String, convert: String => T, cast: Boolean = false): List[T] = {
val fullKey = buildKey(key)

def stringToT(string: String): List[T] = string.split(',').map(x => {
convert(x)
}).toList

if (valueOverrides.contains(fullKey)) {
stringToT(valueOverrides(fullKey))
} else {
System.getenv(keyAsEnv(fullKey)) match {
case null => getListOf[T](fullKey, convert, cast)
case env: String => stringToT(env)
}
}
}
def getListOfDuration(key: String): List[Duration] =
getListOfValuesAccordingOrderOfPreference[Duration](key, str => DurationParser.parseDurationString(str, key, "listOfDuration"))

def toProperties(defaults: Properties = null): Properties = {
val props = new Properties
Expand All @@ -133,16 +171,38 @@ class SqConf(fileName: String = null,
props
}

def getConfig(confPath: String): SqConf = {
new SqConf(null, null, config, confPath)
}
def getSubConfig(confPath: String): SqConf =
new SqConf(null, null, config, confPath, valueOverrides, orderOfPreference)

def withOverrides(overrides: Map[String, String]): SqConf =
new SqConf(null, null, config, prefix, appendPrefixToOverridesIfNecessary(prefix, overrides), orderOfPreference)

def withOverrides(overrides: Map[String, String]): SqConf = {
new SqConf(null, null, config, prefix, overrides)
def appendPrefixToOverridesIfNecessary(prefix: String, overrides: Map[String, String]): Map[String, String] = {
if (prefix == null || overrides.head._1.contains(prefix)) {
overrides
} else {
overrides.map(kv => {
(s"$prefix.${kv._1}", kv._2)
})
}
}

def configureOrder(order: List[OrderOfPreference]): SqConf =
new SqConf(null, null, config, prefix, valueOverrides, order)
}

object OrderOfPreference extends Enumeration {
type OrderOfPreference = Value
val VALUE_OVERRIDES, ENV_VARIABLE, CONF_FIlE = Value
}

object SqConf {

val DEFAULT_ORDER_OF_PREFERENCe: List[OrderOfPreference] = List(
OrderOfPreference.VALUE_OVERRIDES,
OrderOfPreference.ENV_VARIABLE,
OrderOfPreference.CONF_FIlE)

def forFile(file: File): SqConf = {
new SqConf(null, file, null, null)
}
Expand Down
1 change: 1 addition & 0 deletions src/test/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ subConf {
aInt = 123
}
rootString = "root"
intList = [1,2,3]
}

simplevalue = "this is a simple string"
4 changes: 2 additions & 2 deletions src/test/scala/io/sqooba/conf/EnvOverridesSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class EnvOverridesSpec extends FlatSpec with Matchers {

val conf = new SqConf

"read int from env" should "refer env variable to conf" in {
"read int from env" should "prefer env variable to conf" in {
EnvUtil.setEnv(conf.keyAsEnv("some.testIntValue"), "50")
Properties.envOrNone(conf.keyAsEnv("some.testIntValue")) shouldBe defined
val prop = conf.getInt("some.testIntValue")
Expand Down Expand Up @@ -72,6 +72,7 @@ class EnvOverridesSpec extends FlatSpec with Matchers {
val testVal = "this_is_test_val"
EnvUtil.setEnv(conf.keyAsEnv(testKey), testVal)
conf.getString(testKey) shouldBe testVal
EnvUtil.removeEnv(conf.keyAsEnv(testKey))
}

"read duration from env" should "refer env variable to conf" in {
Expand All @@ -83,5 +84,4 @@ class EnvOverridesSpec extends FlatSpec with Matchers {
prop.getSeconds shouldBe 20
EnvUtil.removeEnv(conf.keyAsEnv("some.testDurationValue"))
}

}

0 comments on commit 1232742

Please sign in to comment.