Managing configuration

Now we want to hold database configuration somewhere. For local development, it may be fine to have those configurations hardcoded. 

When we connect to the database, we need to specify the following parameters at the very least:

  • Username
  • Password
  • Host
  • Database name

Where should we store them? 

One option is of course to hardcode those values. That would be fine for a local environment, but what about when deploying this service somewhere? 

You'll go, I cant come! XDSpringBoot do, or we could attempt to read them from the environment variables. Anyway, we'll need an object that would encapsulate this logic, as shown in the following code: 

object Config {
object Db {
val username = System.getenv("DATABASE_USERNAME") ?: "postgres"
val password = System.getenv("DATABASE_PASSWORD") ?: ""
val database = System.getenv("DATABASE_NAME") ?: "cats_db"
val host = System.getenv("DATABASE_HOST") ?: ""

override fun toString(): String {
return mapOf("username" to username,
"password" to password,
"database" to database,
"host" to host).toString()
}
}

override fun toString(): String {
return mapOf(
"Db" to Db
).toString()
}
}

That's of course only one approach you could take.

We now will create JDBCClient by using this configuration code:

fun CoroutineVerticle.getDbClient(): JDBCClient {
val postgreSQLClientConfig = JsonObject(
"url" to "jdbc:postgresql://${Config.Db.host}:5432/${Config.Db.database}",
"username" to Config.Db.username,
"password" to Config.Db.password)
return JDBCClient.createShared(vertx, postgreSQLClientConfig)
}

Here, we chose an extension function that will work on all CoroutineVerticles.

To simplify working with the JDBCClient, we'll add a method called query() to it:

fun JDBCClient.query(q: String, vararg params: Any): Deferred<JsonObject> {
val deferred = CompletableDeferred<JsonObject>()
this.getConnection { conn ->
conn.handle({
result().queryWithParams(q, params.toJsonArray(), { res ->
res.handle({
deferred.complete(res.result().toJson())
}, {
deferred.completeExceptionally(res.cause())
})
})
}, {
deferred.completeExceptionally(conn.cause())
})
}

return deferred
}

We'll also add the toJsonArray() method since that's what our JDBCClient works with:

private fun <T> Array<T>.toJsonArray(): JsonArray {
val json = JsonArray()

for (e in this) {
json.add(e)
}

return json
}

Note here how Kotlin generics are being used to simplify the conversion while staying type-safe.

And we'll add a handle() function, which will provide us with a simple API to handle asynchronous errors:

inline fun <T> AsyncResult<T>.handle(success: AsyncResult<T>.() -> Unit, failure: () -> Unit) {
if (this.succeeded()) {
success()
}
else {
this.cause().printStackTrace()
failure()
}
}

To make sure everything works correctly, we'll add a check to our /alive route:

val router = Router.router(vertx)
val dbClient = getDbClient()
...
router.get("/alive").asyncHandler {
val dbAlive = dbClient.query("select true as alive")
val json = json {
obj (
"alive" to true,
// This is JSON, but we can access it as an array
"db" to dbAlive.await()["rows"]
)
}
it.respond(json)
}

The lines you need to add are marked in bold. 

After adding those lines and opening  http://localhost:8080/alive you should get the following JSON code:

{"alive":true, "db":[{"alive":true}]}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.12.157