Skip to content

Commit

Permalink
forex api config + refactor (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
psuzn authored Jan 2, 2024
1 parent 59ed0a4 commit dfe5c3a
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 80 deletions.
3 changes: 2 additions & 1 deletion backend/src/main/kotlin/me/sujanpoudel/playdeals/Conf.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ data class Conf(
val api: Api,
val environment: Environment,
val backgroundTask: BackgroundTask,
val firebaseAuthCredential: String
val firebaseAuthCredential: String,
val forexApiKey: String
) {
data class DB(
val host: String,
Expand Down
87 changes: 41 additions & 46 deletions backend/src/main/kotlin/me/sujanpoudel/playdeals/common/Conf.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("UNCHECKED_CAST")

package me.sujanpoudel.playdeals.common

import me.sujanpoudel.playdeals.Conf
Expand All @@ -7,74 +9,67 @@ import java.util.Base64

class BootstrapException(val violations: List<String>) : RuntimeException()

fun buildConf(env: Map<String, String>) = com.github.michaelbull.result.runCatching {
fun buildConf(envs: Map<String, String>) = com.github.michaelbull.result.runCatching {
val violations = mutableListOf<String>()

val environment = env.getOrDefault("ENV", Environment.PRODUCTION.name).asEnumOrNull<Environment>()

if (environment == null) {
violations += "Invalid ENV"
}

val dashboardEnabled = env.getOrDefault("DASHBOARD", "true").toBooleanStrictOrNull()
if (dashboardEnabled == null) {
violations += "Invalid DASHBOARD"
@Suppress("UNCHECKED_CAST")
fun <T> env(
envVarName: String,
default: String? = null,
converter: (String) -> T? = { it as? T }
): T? = (
envs[envVarName] ?: default ?: run {
violations += "No '$envVarName' env var defined!".also { logger.error { it } }
null
}
)?.let(converter) ?: run {
violations += "Invalid '$envVarName'"
null
}

val appPort = env.getOrDefault("APP_PORT", "8888").toIntOrNull()

if (appPort == null) {
violations += "Invalid APP_PORT"
}
val environment = env("ENV", Environment.PRODUCTION.name) { it.asEnumOrNull<Environment>() }

val dbPort = env.getOrDefault("DB_PORT", "5432").toIntOrNull()
if (dbPort == null) {
violations += "Invalid DB_PORT"
}
val appPort = env("APP_PORT", "8888") { it.toIntOrNull() }
val cors = env<String>("CORS", ".*.")

val dbName = env.getOrDefault("DB_NAME", "play_deals")
val dbPoolSize = (env["DB_POOL_SIZE"] ?: "5").toIntOrNull()
if (dbPoolSize == null) {
violations += "Invalid DB_POOL_SIZE"
}
val dbPort = env("DB_PORT", "5432") { it.toIntOrNull() }
val dbName = env<String>("DB_NAME", "play_deals")
val dbPoolSize = env("DB_POOL_SIZE", "5") { it.toIntOrNull() }
val dbHost = env<String>("DB_HOST")
val dbUsername = env<String>("DB_USERNAME")
val dbPassword = env<String>("DB_PASSWORD", "password")

fun envVar(envVarName: String): String? {
val value = env[envVarName]
val dashboardEnabled = env("DASHBOARD", "true") { it.toBooleanStrictOrNull() }
val dashboardUser = env<String>("DASHBOARD_USER", "admin")
val dashboardPassword = env<String>("DASHBOARD_PASS", "admin")

return if (value.isNullOrBlank()) {
violations += "No $envVarName env var defined!".also { logger.error { it } }
null
} else {
value
}
val firebaseAuthCredential = env("FIREBASE_ADMIN_AUTH_CREDENTIALS") {
Base64.getDecoder().decode(it).decodeToString()
}

val dbHost: String = envVar("DB_HOST").orEmpty()
val dbUsername: String = envVar("DB_USERNAME").orEmpty()
val firebaseAuthCredential = envVar("FIREBASE_ADMIN_AUTH_CREDENTIALS")?.let {
Base64.getDecoder().decode(it).decodeToString()
}.orEmpty()
val forexApiKey = env<String>("FOREX_API_KEY")

if (violations.isNotEmpty()) {
throw BootstrapException(violations)
} else {
Conf(
api = Conf.Api(appPort!!, cors = env.getOrDefault("CORS", ".*.")),
api = Conf.Api(appPort!!, cors = cors!!),
environment = environment!!,
db = Conf.DB(
host = dbHost,
host = dbHost!!,
port = dbPort!!,
name = dbName,
username = dbUsername,
password = env.getOrDefault("DB_PASSWORD", "password"),
name = dbName!!,
username = dbUsername!!,
password = dbPassword!!,
poolSize = dbPoolSize!!
),
backgroundTask = Conf.BackgroundTask(
dashboardEnabled!!,
env.getOrDefault("DASHBOARD_USER", "admin"),
env.getOrDefault("DASHBOARD_PASS", "admin")
dashboardEnabled = dashboardEnabled!!,
dashboardUserName = dashboardUser!!,
dashboardPassword = dashboardPassword!!
),
firebaseAuthCredential = firebaseAuthCredential
firebaseAuthCredential = firebaseAuthCredential!!,
forexApiKey = forexApiKey!!
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ abstract class IntegrationTest(private val vertx: Vertx) {
"admin",
"admin"
),
firebaseAuthCredential = ""
firebaseAuthCredential = "",
forexApiKey = ""
)

var di = configureDI(vertx, conf)
Expand Down
68 changes: 36 additions & 32 deletions backend/src/test/kotlin/me/sujanpoudel/playdeals/MainTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package me.sujanpoudel.playdeals
import com.github.michaelbull.result.unwrap
import com.github.michaelbull.result.unwrapError
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.collections.shouldNotBeEmpty
import io.kotest.matchers.shouldBe
import me.sujanpoudel.playdeals.common.BootstrapException
Expand All @@ -14,6 +13,7 @@ class MainTest {
@Test
fun `Should return a proper conf with all values from env`() {
val env = mutableMapOf(
"ENV" to "DEVELOPMENT",
"APP_PORT" to "123",
"CORS" to "*.example.com",

Expand All @@ -28,14 +28,14 @@ class MainTest {
"DB_PORT" to "3333",
"DB_NAME" to "db-name",

"APP_PORT" to "123",

"FIREBASE_ADMIN_AUTH_CREDENTIALS" to "dGVzdF9jcmVk",
"ENV" to "DEVELOPMENT"
"FOREX_API_KEY" to "forex_key"
)

val conf = buildConf(env).unwrap()

conf.environment shouldBe Environment.DEVELOPMENT
conf.api.port shouldBe 123
conf.api.cors shouldBe "*.example.com"

conf.backgroundTask.dashboardEnabled shouldBe true
Expand All @@ -49,41 +49,40 @@ class MainTest {
conf.db.username shouldBe "u"
conf.db.port shouldBe 3333

conf.api.port shouldBe 123

conf.environment shouldBe Environment.DEVELOPMENT
}

@Test
fun `Should fail on first critical incorrect val from env`() {
val env = mutableMapOf(
"ENV" to "prod"
)

val err = buildConf(env).unwrapError()
err.printStackTrace()
val violations = (err as BootstrapException).violations
violations.shouldHaveSize(4) shouldContainExactlyInAnyOrder listOf(
"Invalid ENV",
"No DB_HOST env var defined!",
"No DB_USERNAME env var defined!",
"No FIREBASE_ADMIN_AUTH_CREDENTIALS env var defined!"
)
conf.firebaseAuthCredential shouldBe "test_cred"
conf.forexApiKey shouldBe "forex_key"
}

@Test
fun `Should return a proper conf with some defaults being taken`() {
val env = mutableMapOf(
"DB_PORT" to "3333",
"DB_HOST" to "localhost",
"DB_USERNAME" to "u",
"DB_PASSWORD" to "p",
"DB_PORT" to "3333",
"DB_NAME" to "db-name",
"FIREBASE_ADMIN_AUTH_CREDENTIALS" to "dGVzdF9jcmVk"
"FIREBASE_ADMIN_AUTH_CREDENTIALS" to "dGVzdF9jcmVk",
"FOREX_API_KEY" to "forex_key"
)

val conf = buildConf(env).unwrap()

conf.environment shouldBe Environment.PRODUCTION
conf.api.port shouldBe 8888
conf.api.cors shouldBe ".*."

conf.backgroundTask.dashboardEnabled shouldBe true
conf.backgroundTask.dashboardUserName shouldBe "admin"
conf.backgroundTask.dashboardPassword shouldBe "admin"

conf.db.name shouldBe "play_deals"
conf.db.host shouldBe "localhost"
conf.db.password shouldBe "p"
conf.db.poolSize shouldBe 5
conf.db.username shouldBe "u"
conf.db.port shouldBe 3333

conf.firebaseAuthCredential shouldBe "test_cred"
conf.forexApiKey shouldBe "forex_key"
}

@Test
Expand All @@ -96,11 +95,16 @@ class MainTest {
val violations = ((buildConf(env).unwrapError()) as BootstrapException).violations
violations.shouldNotBeEmpty()
violations shouldContainExactlyInAnyOrder listOf(
"Invalid APP_PORT",
"Invalid ENV",
"No DB_HOST env var defined!",
"No DB_USERNAME env var defined!",
"No FIREBASE_ADMIN_AUTH_CREDENTIALS env var defined!"
"Invalid 'ENV'",
"Invalid 'APP_PORT'",
"No 'DB_HOST' env var defined!",
"Invalid 'DB_HOST'",
"No 'DB_USERNAME' env var defined!",
"Invalid 'DB_USERNAME'",
"No 'FIREBASE_ADMIN_AUTH_CREDENTIALS' env var defined!",
"Invalid 'FIREBASE_ADMIN_AUTH_CREDENTIALS'",
"No 'FOREX_API_KEY' env var defined!",
"Invalid 'FOREX_API_KEY'"
)
}
}

0 comments on commit dfe5c3a

Please sign in to comment.