Skip to content

Instantly share code, notes, and snippets.

@mvniekerk
Last active July 27, 2018 15:45
Show Gist options
  • Save mvniekerk/c36a65d667581bb43d71f4665520931f to your computer and use it in GitHub Desktop.
Save mvniekerk/c36a65d667581bb43d71f4665520931f to your computer and use it in GitHub Desktop.
Vert.x Web Router / Kotlin infix helper methods
import io.vertx.core.Vertx
import io.vertx.ext.web.Router
import za.co.koperfontein.amapogo.safetyq.mongo.MONGO_ENTRY_PAGE_ENABLE_DISABLE
import za.co.koperfontein.amapogo.safetyq.mongo.MONGO_ENTRY_PAGE_GET
import za.co.koperfontein.amapogo.safetyq.mongo.MONGO_ENTRY_PAGE_VALUES_UPDATE
// Example usage
class PageHttpRoutes(router: Router, vertx: Vertx): RegisterHttpRoutes(router, vertx) {
override fun registerRoutes() {
// GET example using path params
this get "/api/entry/:entryId/pages/:pageName" pathParams mapOf("entryId" to "id", "pageName" to "page") from MONGO_ENTRY_PAGE_GET
// PATCH example using path params and body params
this patch "/api/entry/:entryId/pages/:pageName/values" pathParams mapOf("entryId" to "id", "pageName" to "page") bodyParams
mapOf("author" to "creator") to MONGO_ENTRY_PAGE_VALUES_UPDATE
// PUT example that sends path and body params as is to the downstream event bus listener
this put "/api/entry/:entryId/pages/:pageName/enabled" pathParams mapOf("entryId" to "id", "pageName" to "page") to MONGO_ENTRY_PAGE_ENABLE_DISABLE
}
}
abstract class RegisterHttpRoutes(val router: Router, val vertx: Vertx) {
abstract fun registerRoutes()
infix fun get(address: String): RequestHandler = RequestHandler(address, vertx, router, "GET")
infix fun post(address: String): RequestHandler = RequestHandler(address, vertx, router, "POST")
infix fun patch(address: String): RequestHandler = RequestHandler(address, vertx, router, "PATCH")
infix fun put(address: String): RequestHandler = RequestHandler(address, vertx, router, "PUT")
infix fun Route.with(handler: Handler<RoutingContext>): Route = this.handler(handler)
class RequestHandler(val address: String, val vertx: Vertx, val router: Router, val method: String) {
var pathMapping: Map<String, String> = mapOf()
var bodyMapping: Map<String, String> = mapOf()
var vertxAddress: String = ""
infix fun from(address: String): RequestHandler = this.also { vertxAddress = address; listen()}
infix fun to(address: String): RequestHandler = this.also { vertxAddress = address; listen()}
infix fun pathParams(params: Map<String, String>): RequestHandler = this.also { pathMapping = params }
infix fun bodyMapping(params: Map<String, String>): RequestHandler = this.also { bodyMapping = params }
fun listen() {
when (method) {
"GET" -> getListen()
"POST" -> postListen()
"PATCH" -> patchListen()
"PUT" -> putListen()
}
}
fun setupParams(ctx: RoutingContext): Map<String, Any> =
mutableMapOf<String, Any>().also {
ctx.pathParams().keys.filter { k -> k !in pathMapping.keys }.forEach { e -> it.put(e, ctx.pathParam(e))}
pathMapping.entries.forEach { e ->
val v = ctx.pathParam(e.key)
if (v != null) it.put(e.value, v)
}
ctx.bodyAsJson?.map?.run {
this.keys.filter { k -> k !in bodyMapping.keys }.forEach { e -> it.put(e, this.get(e)!!)}
bodyMapping.entries.forEach { e ->
val v = get(e.key)
if (v != null) {it.put(e.value, v)}
}
}
}
val handleResult = { ctx: RoutingContext, mm: AsyncResult<Message<JsonObject>> ->
ctx.response().run {
when (mm.failed()) {
true -> {
val cause = mm.cause()
when (cause) {
is ReplyException -> setStatusCode(cause.failureCode()).setStatusMessage(cause.message).end()
else -> setStatusCode(500).setStatusMessage(mm.cause().message).end()
}
}
else -> {
putHeader("Content-Type", "application/json")
setStatusCode(200)
val body = mm.result().body()
when {
body.size() == 1 && body.containsKey("values") -> end(body.getJsonArray("values").toBuffer())
body.size() == 1 && body.containsKey("valueMap") -> end(body.getJsonObject("valueMap").toBuffer())
else -> end(body.toBuffer())
}
}
}
}
}
private fun getListen() {
println("Listening to ${address} diverted to ${vertxAddress} method ${method}")
router.route(address).method(HttpMethod.valueOf(method)).handler { ctx ->
vertx.send(vertxAddress, setupParams(ctx)) { mm -> handleResult(ctx, mm)}
}
}
private fun postListen() {
println("Listening to ${address} diverted to ${vertxAddress} method ${method}")
router.route().handler(BodyHandler.create()).method(HttpMethod.POST).path(address).handler { ctx ->
vertx.send(vertxAddress, setupParams(ctx)) { mm -> handleResult(ctx, mm)}
}
}
private fun patchListen() {
println("Listening to ${address} diverted to ${vertxAddress} method ${method}")
router.route().handler(BodyHandler.create()).method(HttpMethod.PATCH).path(address).handler { ctx ->
vertx.send(vertxAddress, setupParams(ctx)) { mm -> handleResult(ctx, mm)}
}
}
private fun putListen() {
println("Listening to ${address} diverted to ${vertxAddress} method ${method}")
router.route().handler(BodyHandler.create()).method(HttpMethod.PUT).path(address).handler { ctx ->
vertx.send(vertxAddress, setupParams(ctx)) { mm -> handleResult(ctx, mm)}
}
}
}
}
@mvniekerk
Copy link
Author

RegisterHttpRoutes.kt is a helper Kotlin class that makes your Vert.x routing look nice.

What it does is the following:
HTTP request -[JSON Body, path params]> transformation -[JSON]> VertX Event Bus Listener -[JSON]> HTTP response

The transformation makes a JsonObject from the sent parameters (body, path) from the HTTP call.
The transformation is first off a copy of the sent params, but then also the keys can also be copied as new key/value pairs
in the path and body params mapping.
This computed map is then the parameter for the event bus listener later on. When the computation is done the listener returns
a JSON object (which is returned to the client).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment