From b7471e7fca4548e0234a45ecd68db772e3cb5cb6 Mon Sep 17 00:00:00 2001 From: Sebastien Deleuze Date: Wed, 11 Jan 2017 14:53:34 +0100 Subject: [PATCH] Add a Kotlin Web functional DSL This commit introduces a router DSL for RouterFunctions and RouterFunction in order to be able to write idiomatic Kotlin code as below: fun route(request: ServerRequest) = RouterFunctionDsl { accept(TEXT_HTML).apply { (GET("/user/") or GET("/users/")) { findAllView() } GET("/user/{login}") { findViewById() } } accept(APPLICATION_JSON).apply { (GET("/api/user/") or GET("/api/users/")) { findAll() } POST("/api/user/") { create() } POST("/api/user/{login}") { findOne() } } } (request) Issue: SPR-15065 --- .../function/server/RouterFunctionDsl.kt | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 spring-web-reactive/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt diff --git a/spring-web-reactive/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt b/spring-web-reactive/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt new file mode 100644 index 00000000000..484b962847f --- /dev/null +++ b/spring-web-reactive/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt @@ -0,0 +1,122 @@ +package org.springframework.web.reactive.function.server + +import org.springframework.core.io.Resource +import reactor.core.publisher.Mono + +/** + * Provide a router DSL for [RouterFunctions] and [RouterFunction] in order to be able to + * write idiomatic Kotlin code as below: + * + * * ```kotlin + * fun route(request: ServerRequest) = RouterFunctionDsl { + * accept(TEXT_HTML).apply { + * (GET("/user/") or GET("/users/")) { findAllView() } + * GET("/user/{login}") { findViewById() } + * } + * accept(APPLICATION_JSON).apply { + * (GET("/api/user/") or GET("/api/users/")) { findAll() } + * POST("/api/user/") { create() } + * POST("/api/user/{login}") { findOne() } + * } + * } (request) + * ``` + * + * @since 5.0 + * @author Sebastien Deleuze + * @author Yevhenii Melnyk + */ +class RouterFunctionDsl { + + val children = mutableListOf() + val routes = mutableListOf>() + + operator fun RequestPredicate.invoke(f: () -> HandlerFunction) { + routes += RouterFunctions.route(this, f()) + } + + infix fun RequestPredicate.and(other: RequestPredicate): RequestPredicate = this.and(other) + + infix fun RequestPredicate.or(other: RequestPredicate): RequestPredicate = this.or(other) + + operator fun RequestPredicate.not(): RequestPredicate = this.negate() + + fun GET(pattern: String): RequestPredicate { + return RequestPredicates.GET(pattern) + } + + fun GET(pattern: String, f: () -> HandlerFunction) { + routes += RouterFunctions.route(RequestPredicates.GET(pattern), f()) + } + + fun HEAD(pattern: String): RequestPredicate { + return RequestPredicates.HEAD(pattern) + } + + fun HEAD(pattern: String, f: () -> HandlerFunction) { + routes += RouterFunctions.route(RequestPredicates.HEAD(pattern), f()) + } + + fun POST(pattern: String): RequestPredicate { + return RequestPredicates.POST(pattern) + } + + fun POST(pattern: String, f: () -> HandlerFunction) { + routes += RouterFunctions.route(RequestPredicates.POST(pattern), f()) + } + + fun PUT(pattern: String): RequestPredicate { + return RequestPredicates.PUT(pattern) + } + + fun PUT(pattern: String, f: () -> HandlerFunction) { + routes += RouterFunctions.route(RequestPredicates.PUT(pattern), f()) + } + + fun PATCH(pattern: String): RequestPredicate { + return RequestPredicates.PATCH(pattern) + } + + fun PATCH(pattern: String, f: () -> HandlerFunction) { + routes += RouterFunctions.route(RequestPredicates.PATCH(pattern), f()) + } + + fun DELETE(pattern: String): RequestPredicate { + return RequestPredicates.DELETE(pattern) + } + + fun DELETE(pattern: String, f: () -> HandlerFunction) { + routes += RouterFunctions.route(RequestPredicates.DELETE(pattern), f()) + } + + fun OPTIONS(pattern: String): RequestPredicate { + return RequestPredicates.OPTIONS(pattern) + } + + fun OPTIONS(pattern: String, f: () -> HandlerFunction) { + routes += RouterFunctions.route(RequestPredicates.OPTIONS(pattern), f()) + } + + fun resources(path: String, location: Resource) { + routes += RouterFunctions.resources(path, location) + } + + @Suppress("UNCHECKED_CAST") + fun router(): RouterFunction { + return routes().reduce(RouterFunction<*>::and) as RouterFunction + } + + operator fun invoke(request: ServerRequest): Mono> { + return router().route(request) + } + + private fun routes(): List> { + val allRoutes = mutableListOf>() + allRoutes += routes + for (child in children) { + allRoutes += child.routes() + } + return allRoutes + } +} + +fun RouterFunctionDsl(configure: RouterFunctionDsl.()->Unit) = RouterFunctionDsl().apply(configure)