From f8b35a9171caad1a5b3c8a833477ea6b5ed9a9e9 Mon Sep 17 00:00:00 2001 From: Marco Fracassi Date: Mon, 23 Dec 2019 13:49:09 +0100 Subject: [PATCH] Add context --- README.md | 4 +- build.gradle | 2 +- src/main/kotlin/daikon/Context.kt | 6 ++ src/main/kotlin/daikon/DefaultRouteAction.kt | 2 +- src/main/kotlin/daikon/DummyRouteAction.kt | 8 ++- src/main/kotlin/daikon/HttpServer.kt | 62 ++++++++++++++----- src/main/kotlin/daikon/RouteAction.kt | 2 +- src/main/kotlin/daikon/RoutingServlet.kt | 7 +-- src/main/kotlin/daikon/ServerContext.kt | 15 +++++ .../kotlin/daikon/DefaultRouteActionTest.kt | 5 +- src/test/kotlin/daikon/HttpContextTest.kt | 18 ++++++ src/test/kotlin/daikon/HttpRoutingTest.kt | 2 +- src/test/kotlin/daikon/NopAction.kt | 2 +- 13 files changed, 106 insertions(+), 29 deletions(-) create mode 100644 src/main/kotlin/daikon/Context.kt create mode 100644 src/main/kotlin/daikon/ServerContext.kt create mode 100644 src/test/kotlin/daikon/HttpContextTest.kt diff --git a/README.md b/README.md index ca0af95..4cd03bb 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ repositories { ``` - Add the dependency ``` -implementation 'com.github.DaikonWeb:daikon:0.6.1' +implementation 'com.github.DaikonWeb:daikon:0.6.2' ``` ### Maven @@ -40,7 +40,7 @@ implementation 'com.github.DaikonWeb:daikon:0.6.1' com.github.DaikonWeb daikon - 0.6.1 + 0.6.2 ``` diff --git a/build.gradle b/build.gradle index 74041eb..50fc719 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group = 'com.github.DaikonWeb' -version '0.6.1' +version '0.6.2' repositories { mavenCentral() diff --git a/src/main/kotlin/daikon/Context.kt b/src/main/kotlin/daikon/Context.kt new file mode 100644 index 0000000..966851d --- /dev/null +++ b/src/main/kotlin/daikon/Context.kt @@ -0,0 +1,6 @@ +package daikon + +interface Context { + fun addAttribute(key: String, value: Any) + fun getAttribute(key: String) : T +} \ No newline at end of file diff --git a/src/main/kotlin/daikon/DefaultRouteAction.kt b/src/main/kotlin/daikon/DefaultRouteAction.kt index dced03d..aed210a 100644 --- a/src/main/kotlin/daikon/DefaultRouteAction.kt +++ b/src/main/kotlin/daikon/DefaultRouteAction.kt @@ -5,7 +5,7 @@ import org.eclipse.jetty.http.HttpStatus.NOT_FOUND_404 import org.eclipse.jetty.http.HttpStatus.OK_200 class DefaultRouteAction : RouteAction { - override fun handle(request: Request, response: Response) { + override fun handle(request: Request, response: Response, context: Context) { if (request.method() == GET && request.path() == "/") { response.status(OK_200) response.type("text/html") diff --git a/src/main/kotlin/daikon/DummyRouteAction.kt b/src/main/kotlin/daikon/DummyRouteAction.kt index 1122490..1fb09fc 100644 --- a/src/main/kotlin/daikon/DummyRouteAction.kt +++ b/src/main/kotlin/daikon/DummyRouteAction.kt @@ -1,7 +1,13 @@ package daikon class DummyRouteAction(private val action: (Request, Response) -> Unit) : RouteAction { - override fun handle(request: Request, response: Response) { + override fun handle(request: Request, response: Response, context: Context) { action.invoke(request, response) } +} + +class ContextRouteAction(private val action: (Request, Response, Context) -> Unit) : RouteAction { + override fun handle(request: Request, response: Response, context: Context) { + action.invoke(request, response, context) + } } \ No newline at end of file diff --git a/src/main/kotlin/daikon/HttpServer.kt b/src/main/kotlin/daikon/HttpServer.kt index 63c4ebf..b649e54 100644 --- a/src/main/kotlin/daikon/HttpServer.kt +++ b/src/main/kotlin/daikon/HttpServer.kt @@ -15,9 +15,10 @@ class HttpServer(private val port: Int = 4545, initializeActions: HttpServer.() private val routes = Routing() private val beforeActions = Routing() private val afterActions = Routing() - private val afterStartActions = mutableListOf<() -> Unit>() - private val beforeStopActions = mutableListOf<() -> Unit>() + private val afterStartActions = mutableListOf<(Context) -> Unit>() + private val beforeStopActions = mutableListOf<(Context) -> Unit>() private val basePath = mutableListOf("") + private val context = ServerContext() init { initializeActions() @@ -30,17 +31,17 @@ class HttpServer(private val port: Int = 4545, initializeActions: HttpServer.() fun start(): HttpServer { val beginStarting = now() server = Server(port) - handler.addServlet(ServletHolder(RoutingServlet(beforeActions, routes, afterActions)), "/*") + handler.addServlet(ServletHolder(RoutingServlet(beforeActions, routes, afterActions, context)), "/*") server.handler = handler server.start() val endStarting = now() println("Server up and running on port $port in ${beginStarting.until(endStarting, MILLIS)}ms") - afterStartActions.forEach { it.invoke() } + afterStartActions.forEach { it.invoke(context) } return this } override fun close() { - beforeStopActions.forEach { it.invoke() } + beforeStopActions.forEach { it.invoke(context) } server.stop() } @@ -49,6 +50,11 @@ class HttpServer(private val port: Int = 4545, initializeActions: HttpServer.() return this } + fun get(path: String, action: (Request, Response, Context) -> Unit): HttpServer { + get(path, ContextRouteAction(action)) + return this + } + fun get(path: String, action: RouteAction): HttpServer { add(GET, path, action) return this @@ -59,6 +65,11 @@ class HttpServer(private val port: Int = 4545, initializeActions: HttpServer.() return this } + fun post(path: String, action: (Request, Response, Context) -> Unit): HttpServer { + post(path, ContextRouteAction(action)) + return this + } + fun post(path: String, action: RouteAction): HttpServer { add(POST, path, action) return this @@ -69,6 +80,11 @@ class HttpServer(private val port: Int = 4545, initializeActions: HttpServer.() return this } + fun head(path: String, action: (Request, Response, Context) -> Unit): HttpServer { + head(path, ContextRouteAction(action)) + return this + } + fun head(path: String, action: RouteAction): HttpServer { add(HEAD, path, action) return this @@ -79,6 +95,11 @@ class HttpServer(private val port: Int = 4545, initializeActions: HttpServer.() return this } + fun any(path: String, action: (Request, Response, Context) -> Unit): HttpServer { + any(path, ContextRouteAction(action)) + return this + } + fun any(path: String, action: RouteAction): HttpServer { add(ANY, path, action) return this @@ -89,11 +110,21 @@ class HttpServer(private val port: Int = 4545, initializeActions: HttpServer.() return this } + fun before(path: String = "/*", action: (Request, Response, Context) -> Unit): HttpServer { + beforeActions.add(Route(ANY, joinPaths(path), ContextRouteAction(action))) + return this + } + fun after(path: String = "/*", action: (Request, Response) -> Unit): HttpServer { afterActions.add(Route(ANY, joinPaths(path), DummyRouteAction(action))) return this } + fun after(path: String = "/*", action: (Request, Response, Context) -> Unit): HttpServer { + afterActions.add(Route(ANY, joinPaths(path), ContextRouteAction(action))) + return this + } + fun assets(path: String): HttpServer { val servletHolder = ServletHolder(DefaultServlet()) handler.addServlet(servletHolder, path) @@ -108,6 +139,16 @@ class HttpServer(private val port: Int = 4545, initializeActions: HttpServer.() return this } + fun afterStart(function: (Context) -> Unit): HttpServer { + afterStartActions.add(function) + return this + } + + fun beforeStop(function: (Context) -> Unit): HttpServer { + beforeStopActions.add(function) + return this + } + private fun add(method: Method, path: String, action: RouteAction) { routes.add(Route(method, joinPaths(path), action)) } @@ -118,14 +159,5 @@ class HttpServer(private val port: Int = 4545, initializeActions: HttpServer.() Log.getProperties().setProperty("org.eclipse.jetty.util.log.announce", "false") Log.getProperties().setProperty("org.eclipse.jetty.LEVEL", "OFF") } - - fun afterStart(function: () -> Unit): HttpServer { - afterStartActions.add(function) - return this - } - - fun beforeStop(function: () -> Unit): HttpServer { - beforeStopActions.add(function) - return this - } } + diff --git a/src/main/kotlin/daikon/RouteAction.kt b/src/main/kotlin/daikon/RouteAction.kt index bbdaab5..d0e8fb8 100644 --- a/src/main/kotlin/daikon/RouteAction.kt +++ b/src/main/kotlin/daikon/RouteAction.kt @@ -1,5 +1,5 @@ package daikon interface RouteAction { - fun handle(request: Request, response: Response) + fun handle(request: Request, response: Response, context: Context) } diff --git a/src/main/kotlin/daikon/RoutingServlet.kt b/src/main/kotlin/daikon/RoutingServlet.kt index c15b184..92ba170 100644 --- a/src/main/kotlin/daikon/RoutingServlet.kt +++ b/src/main/kotlin/daikon/RoutingServlet.kt @@ -1,8 +1,6 @@ package daikon import daikon.Method.ANY -import org.eclipse.jetty.http.HttpStatus.NOT_FOUND_404 -import org.eclipse.jetty.http.HttpStatus.OK_200 import javax.servlet.GenericServlet import javax.servlet.ServletRequest import javax.servlet.ServletResponse @@ -12,7 +10,8 @@ import javax.servlet.http.HttpServletResponse class RoutingServlet( private val befores: Routing, private val routes: Routing, - private val afters: Routing + private val afters: Routing, + private val context: Context ) : GenericServlet() { override fun service(req: ServletRequest, res: ServletResponse) { @@ -37,6 +36,6 @@ class RoutingServlet( } private fun invoke(route: Route, req: HttpServletRequest, res: HttpResponse) { - route.action.handle(HttpRequest(req, PathParams(route.path)), res) + route.action.handle(HttpRequest(req, PathParams(route.path)), res, context) } } diff --git a/src/main/kotlin/daikon/ServerContext.kt b/src/main/kotlin/daikon/ServerContext.kt new file mode 100644 index 0000000..8b556c6 --- /dev/null +++ b/src/main/kotlin/daikon/ServerContext.kt @@ -0,0 +1,15 @@ +package daikon + +class ServerContext : Context { + + private val attributes = mutableMapOf() + + override fun addAttribute(key: String, value: Any) { + attributes[key] = value + } + + override fun getAttribute(key: String): T { + @Suppress("UNCHECKED_CAST") + return attributes[key] as T + } +} diff --git a/src/test/kotlin/daikon/DefaultRouteActionTest.kt b/src/test/kotlin/daikon/DefaultRouteActionTest.kt index cfccbee..a28f81f 100644 --- a/src/test/kotlin/daikon/DefaultRouteActionTest.kt +++ b/src/test/kotlin/daikon/DefaultRouteActionTest.kt @@ -13,10 +13,11 @@ class DefaultRouteActionTest { private val request: Request = mock() private val response: Response = mock() + private val context: Context = mock() @Test fun `page not found`() { - DefaultRouteAction().handle(request, response) + DefaultRouteAction().handle(request, response, context) verify(response).status(NOT_FOUND_404) } @@ -26,7 +27,7 @@ class DefaultRouteActionTest { whenever(request.method()).thenReturn(GET) whenever(request.path()).thenReturn("/") - DefaultRouteAction().handle(request, response) + DefaultRouteAction().handle(request, response, context) verify(response).status(OK_200) verify(response).type("text/html") diff --git a/src/test/kotlin/daikon/HttpContextTest.kt b/src/test/kotlin/daikon/HttpContextTest.kt new file mode 100644 index 0000000..0c91ec1 --- /dev/null +++ b/src/test/kotlin/daikon/HttpContextTest.kt @@ -0,0 +1,18 @@ +package daikon + +import daikon.Localhost.get +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class HttpContextTest { + + @Test + fun `context preserve attributes`() { + HttpServer() + .afterStart { ctx -> ctx.addAttribute("key", "value") } + .get("/") { _, res, ctx -> res.write(ctx.getAttribute("key")) } + .start().use { + assertThat(get("/").text).isEqualTo("value") + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/daikon/HttpRoutingTest.kt b/src/test/kotlin/daikon/HttpRoutingTest.kt index f72057e..46fcf78 100644 --- a/src/test/kotlin/daikon/HttpRoutingTest.kt +++ b/src/test/kotlin/daikon/HttpRoutingTest.kt @@ -64,7 +64,7 @@ class HttpRoutingTest { fun `route action`() { HttpServer() .get("/foo", object : RouteAction { - override fun handle(request: Request, response: Response) { + override fun handle(request: Request, response: Response, context: Context) { response.write("Hello foo") } }) diff --git a/src/test/kotlin/daikon/NopAction.kt b/src/test/kotlin/daikon/NopAction.kt index 7205643..566b072 100644 --- a/src/test/kotlin/daikon/NopAction.kt +++ b/src/test/kotlin/daikon/NopAction.kt @@ -1,6 +1,6 @@ package daikon class NopAction : RouteAction { - override fun handle(request: Request, response: Response) { + override fun handle(request: Request, response: Response, context: Context) { } } \ No newline at end of file