From ca1f0c8f5a5004cab464cf82f3387308147c436f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Str=C3=B8msodd?= Date: Thu, 6 May 2021 14:09:30 +0200 Subject: [PATCH] Add Kotlin DSL for functional routes --- springdoc-openapi-kotlin/pom.xml | 18 +- .../kotlin/SpringdocRouterFunctionDsl.kt | 821 ++++++++++++++++++ 2 files changed, 838 insertions(+), 1 deletion(-) create mode 100644 springdoc-openapi-kotlin/src/main/kotlin/org/springdoc/kotlin/SpringdocRouterFunctionDsl.kt diff --git a/springdoc-openapi-kotlin/pom.xml b/springdoc-openapi-kotlin/pom.xml index f82052de4..55565e969 100644 --- a/springdoc-openapi-kotlin/pom.xml +++ b/springdoc-openapi-kotlin/pom.xml @@ -5,6 +5,9 @@ springdoc-openapi 1.5.9-SNAPSHOT + + 5.3.6 + springdoc-openapi-kotlin @@ -31,7 +34,19 @@ org.springdoc springdoc-openapi-webflux-core ${project.version} - test + provided + + + org.springdoc + springdoc-openapi-webflux-ui + ${project.version} + provided + + + org.springframework + spring-webflux + ${spring.version} + provided @@ -61,6 +76,7 @@ ${project.basedir}/src/main/java + ${project.basedir}/src/main/kotlin diff --git a/springdoc-openapi-kotlin/src/main/kotlin/org/springdoc/kotlin/SpringdocRouterFunctionDsl.kt b/springdoc-openapi-kotlin/src/main/kotlin/org/springdoc/kotlin/SpringdocRouterFunctionDsl.kt new file mode 100644 index 000000000..5995cfb0c --- /dev/null +++ b/springdoc-openapi-kotlin/src/main/kotlin/org/springdoc/kotlin/SpringdocRouterFunctionDsl.kt @@ -0,0 +1,821 @@ +package org.springdoc.kotlin + +import org.springframework.core.io.Resource +import org.springframework.http.HttpMethod +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import reactor.core.publisher.Mono +import java.net.URI +import java.util.function.Supplier +import org.springdoc.core.fn.builders.operation.Builder +import org.springdoc.webflux.core.fn.SpringdocRouteBuilder +import org.springframework.web.reactive.function.server.HandlerFunction +import org.springframework.web.reactive.function.server.RequestPredicate +import org.springframework.web.reactive.function.server.RequestPredicates +import org.springframework.web.reactive.function.server.RouterFunction +import org.springframework.web.reactive.function.server.RouterFunctions +import org.springframework.web.reactive.function.server.ServerRequest +import org.springframework.web.reactive.function.server.ServerResponse + +/** + * Allow to create easily a WebFlux.fn [SpringdocRouteBuilder] with a [Reactive router Kotlin DSL][SpringdocRouterFunctionDsl]. + * + * Example: + * + * ``` + * @Configuration + * class RouterConfiguration { + * + * @Bean + * fun mainRouter(userHandler: UserHandler) = docRouter { + * accept(TEXT_HTML).nest { + * (GET("/user/") or GET("/users/")).invoke(userHandler::findAllView) + * GET("/users/{login}", userHandler::findViewById) + * } + * accept(APPLICATION_JSON).nest { + * (GET("/api/user/") or GET("/api/users/")).invoke(userHandler::findAll) + * POST("/api/users/", userHandler::create) + * } + * } + * + * } + * ``` + * @author Sebastien Deleuze + * @see coRouter + * @since 5.0 + */ +fun docRouter(routes: SpringdocRouterFunctionDsl.() -> Unit) = SpringdocRouterFunctionDsl(routes).build() + +/** + * Provide a WebFlux.fn [SpringdocRouteBuilder] Reactive Kotlin DSL created by [`docRouter { }`][docRouter] in order to be able to write idiomatic Kotlin code. + * + * @author Sebastien Deleuze + * @author Yevhenii Melnyk + * @author Arjen Poutsma + * @since 5.0 + */ +class SpringdocRouterFunctionDsl internal constructor (private val init: SpringdocRouterFunctionDsl.() -> Unit) { + + @PublishedApi + internal val builder = SpringdocRouteBuilder.route() + + /** + * Return a composed request predicate that tests against both this predicate AND + * the [other] predicate (String processed as a path predicate). When evaluating the + * composed predicate, if this predicate is `false`, then the [other] predicate is not + * evaluated. + * @see RequestPredicate.and + * @see RequestPredicates.path) + */ + infix fun RequestPredicate.and(other: String): RequestPredicate = this.and(path(other)) + + /** + * Return a composed request predicate that tests against both this predicate OR + * the [other] predicate (String processed as a path predicate). When evaluating the + * composed predicate, if this predicate is `true`, then the [other] predicate is not + * evaluated. + * @see RequestPredicate.or + * @see RequestPredicates.path + */ + infix fun RequestPredicate.or(other: String): RequestPredicate = this.or(path(other)) + + /** + * Return a composed request predicate that tests against both this predicate (String + * processed as a path predicate) AND the [other] predicate. When evaluating the + * composed predicate, if this predicate is `false`, then the [other] predicate is not + * evaluated. + * @see RequestPredicate.and + * @see RequestPredicates.path + */ + infix fun String.and(other: RequestPredicate): RequestPredicate = path(this).and(other) + + /** + * Return a composed request predicate that tests against both this predicate (String + * processed as a path predicate) OR the [other] predicate. When evaluating the + * composed predicate, if this predicate is `true`, then the [other] predicate is not + * evaluated. + * @see RequestPredicate.or + * @see RequestPredicates.path + */ + infix fun String.or(other: RequestPredicate): RequestPredicate = path(this).or(other) + + /** + * Return a composed request predicate that tests against both this predicate AND + * the [other] predicate. When evaluating the composed predicate, if this + * predicate is `false`, then the [other] predicate is not evaluated. + * @see RequestPredicate.and + */ + infix fun RequestPredicate.and(other: RequestPredicate): RequestPredicate = this.and(other) + + /** + * Return a composed request predicate that tests against both this predicate OR + * the [other] predicate. When evaluating the composed predicate, if this + * predicate is `true`, then the [other] predicate is not evaluated. + * @see RequestPredicate.or + */ + infix fun RequestPredicate.or(other: RequestPredicate): RequestPredicate = this.or(other) + + /** + * Return a predicate that represents the logical negation of this predicate. + */ + operator fun RequestPredicate.not(): RequestPredicate = this.negate() + + /** + * Route to the given router function if the given request predicate applies. This + * method can be used to create *nested routes*, where a group of routes share a + * common path (prefix), header, or other request predicate. + * @see RouterFunctions.nest + */ + fun RequestPredicate.nest(init: SpringdocRouterFunctionDsl.() -> Unit, oc: (Builder) -> Unit = {}) { + builder.nest(this, Supplier { SpringdocRouterFunctionDsl(init).build() }, oc) + } + + /** + * Route to the given router function if the given request predicate (String + * processed as a path predicate) applies. This method can be used to create + * *nested routes*, where a group of routes share a common path + * (prefix), header, or other request predicate. + * @see RouterFunctions.nest + * @see RequestPredicates.path + */ + fun String.nest(init: SpringdocRouterFunctionDsl.() -> Unit, oc: (Builder) -> Unit = {}) { + builder.path(this, Supplier { SpringdocRouterFunctionDsl(init).build() }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `GET` requests. + * @since 5.3 + */ + fun GET(f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.GET({ f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `GET` requests + * that match the given pattern. + * @param pattern the pattern to match to + */ + fun GET(pattern: String, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.GET(pattern, { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `GET` requests + * that match the given predicate. + * @param predicate predicate to match + * @since 5.3 + */ + fun GET(predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.GET(predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `GET` requests + * that match the given pattern and predicate. + * @param pattern the pattern to match to + * @param predicate additional predicate to match + * @since 5.2 + */ + fun GET(pattern: String, predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.GET(pattern, predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Return a [RequestPredicate] that matches if request's HTTP method is `GET` + * and the given `pattern` matches against the request path. + * @see RequestPredicates.GET + */ + fun GET(pattern: String): RequestPredicate = RequestPredicates.GET(pattern) + + /** + * Adds a route to the given handler function that handles all HTTP `HEAD` requests. + * @since 5.3 + */ + fun HEAD(f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.HEAD({ f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `HEAD` requests + * that match the given pattern. + * @param pattern the pattern to match to + */ + fun HEAD(pattern: String, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.HEAD(pattern, { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `HEAD` requests + * that match the given predicate. + * @param predicate predicate to match + * @since 5.3 + */ + fun HEAD(predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.HEAD(predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `HEAD` requests + * that match the given pattern and predicate. + * @param pattern the pattern to match to + * @param predicate additional predicate to match + * @since 5.2 + */ + fun HEAD(pattern: String, predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.HEAD(pattern, predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Return a [RequestPredicate] that matches if request's HTTP method is `HEAD` + * and the given `pattern` matches against the request path. + * @see RequestPredicates.HEAD + */ + fun HEAD(pattern: String): RequestPredicate = RequestPredicates.HEAD(pattern) + + /** + * Adds a route to the given handler function that handles all HTTP `POST` requests. + * @since 5.3 + */ + fun POST(f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.POST({ f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `POST` requests + * that match the given pattern. + * @param pattern the pattern to match to + */ + fun POST(pattern: String, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.POST(pattern, { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `POST` requests + * that match the given predicate. + * @param predicate predicate to match + * @since 5.3 + */ + fun POST(predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.POST(predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `POST` requests + * that match the given pattern and predicate. + * @param pattern the pattern to match to + * @param predicate additional predicate to match + * @since 5.2 + */ + fun POST(pattern: String, predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.POST(pattern, predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Return a [RequestPredicate] that matches if request's HTTP method is `POST` + * and the given `pattern` matches against the request path. + * @see RequestPredicates.POST + */ + fun POST(pattern: String): RequestPredicate = RequestPredicates.POST(pattern) + + /** + * Adds a route to the given handler function that handles all HTTP `PUT` requests. + * @since 5.3 + */ + fun PUT(f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.PUT({ f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `PUT` requests + * that match the given pattern. + * @param pattern the pattern to match to + */ + fun PUT(pattern: String, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.PUT(pattern, { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `PUT` requests + * that match the given predicate. + * @param predicate predicate to match + * @since 5.3 + */ + fun PUT(predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.PUT(predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `PUT` requests + * that match the given pattern and predicate. + * @param pattern the pattern to match to + * @param predicate additional predicate to match + * @since 5.2 + */ + fun PUT(pattern: String, predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.PUT(pattern, predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Return a [RequestPredicate] that matches if request's HTTP method is `PUT` + * and the given `pattern` matches against the request path. + * @see RequestPredicates.PUT + */ + fun PUT(pattern: String): RequestPredicate = RequestPredicates.PUT(pattern) + + /** + * Adds a route to the given handler function that handles all HTTP `PATCH` requests. + * @since 5.3 + */ + fun PATCH(f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.PATCH({ f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `PATCH` requests + * that match the given pattern. + * @param pattern the pattern to match to + */ + fun PATCH(pattern: String, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.PATCH(pattern, { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `PATCH` requests + * that match the given predicate. + * @param predicate predicate to match + * @since 5.3 + */ + fun PATCH(predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.PATCH(predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `PATCH` requests + * that match the given pattern and predicate. + * @param pattern the pattern to match to + * @param predicate additional predicate to match + * @since 5.2 + */ + fun PATCH(pattern: String, predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.PATCH(pattern, predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Return a [RequestPredicate] that matches if request's HTTP method is `PATCH` + * and the given `pattern` matches against the request path. + * @param pattern the path pattern to match against + * @return a predicate that matches if the request method is `PATCH` and if the given pattern + * matches against the request path + */ + fun PATCH(pattern: String): RequestPredicate = RequestPredicates.PATCH(pattern) + + /** + * Adds a route to the given handler function that handles all HTTP `DELETE` requests. + * @since 5.3 + */ + fun DELETE(f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.DELETE({ f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `DELETE` requests + * that match the given pattern. + * @param pattern the pattern to match to + */ + fun DELETE(pattern: String, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.DELETE(pattern, { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `DELETE` requests + * that match the given predicate. + * @param predicate predicate to match + * @since 5.3 + */ + fun DELETE(predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.DELETE(predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `DELETE` requests + * that match the given pattern and predicate. + * @param pattern the pattern to match to + * @param predicate additional predicate to match + * @since 5.2 + */ + fun DELETE(pattern: String, predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.DELETE(pattern, predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Return a [RequestPredicate] that matches if request's HTTP method is `DELETE` + * and the given `pattern` matches against the request path. + * @param pattern the path pattern to match against + * @return a predicate that matches if the request method is `DELETE` and if the given pattern + * matches against the request path + */ + fun DELETE(pattern: String): RequestPredicate = RequestPredicates.DELETE(pattern) + + /** + * Adds a route to the given handler function that handles all HTTP `OPTIONS` requests. + * @since 5.3 + */ + fun OPTIONS(f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.OPTIONS({ f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `OPTIONS` requests + * that match the given pattern. + * @param pattern the pattern to match to + */ + fun OPTIONS(pattern: String, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.OPTIONS(pattern, { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `OPTIONS` requests + * that match the given predicate. + * @param predicate predicate to match + * @since 5.3 + */ + fun OPTIONS(predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.OPTIONS(predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Adds a route to the given handler function that handles all HTTP `OPTIONS` requests + * that match the given pattern and predicate. + * @param pattern the pattern to match to + * @param predicate additional predicate to match + * @since 5.2 + */ + fun OPTIONS(pattern: String, predicate: RequestPredicate, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.OPTIONS(pattern, predicate, HandlerFunction { f(it).cast(ServerResponse::class.java) }, oc) + } + + /** + * Return a [RequestPredicate] that matches if request's HTTP method is `OPTIONS` + * and the given `pattern` matches against the request path. + * @param pattern the path pattern to match against + * @return a predicate that matches if the request method is `OPTIONS` and if the given pattern + * matches against the request path + */ + fun OPTIONS(pattern: String): RequestPredicate = RequestPredicates.OPTIONS(pattern) + + /** + * Route to the given handler function if the given accept predicate applies. + * @see RouterFunctions.route + */ + fun accept(mediaType: MediaType, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.add(RouterFunctions.route(RequestPredicates.accept(mediaType), HandlerFunction { f(it).cast(ServerResponse::class.java) }), oc) + } + + /** + * Return a [RequestPredicate] that tests if the request's + * [accept][ServerRequest.Headers.accept] header is + * [compatible][MediaType.isCompatibleWith] with any of the given media types. + * @param mediaTypes the media types to match the request's accept header against + * @return a predicate that tests the request's accept header against the given media types + */ + fun accept(vararg mediaTypes: MediaType): RequestPredicate = RequestPredicates.accept(*mediaTypes) + + /** + * Route to the given handler function if the given contentType predicate applies. + * @see RouterFunctions.route + */ + fun contentType(mediaTypes: MediaType, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.add(RouterFunctions.route(RequestPredicates.contentType(mediaTypes), HandlerFunction { f(it).cast(ServerResponse::class.java) }), oc) + } + + /** + * Return a [RequestPredicate] that tests if the request's + * [content type][ServerRequest.Headers.contentType] is + * [included][MediaType.includes] by any of the given media types. + * @param mediaTypes the media types to match the request's content type against + * @return a predicate that tests the request's content type against the given media types + */ + fun contentType(vararg mediaTypes: MediaType): RequestPredicate = RequestPredicates.contentType(*mediaTypes) + + /** + * Route to the given handler function if the given headers predicate applies. + * @see RouterFunctions.route + */ + fun headers(headersPredicate: (ServerRequest.Headers) -> Boolean, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.add(RouterFunctions.route(RequestPredicates.headers(headersPredicate), HandlerFunction { f(it).cast(ServerResponse::class.java) }), oc) + } + + /** + * Return a [RequestPredicate] that tests the request's headers against the given headers predicate. + * @param headersPredicate a predicate that tests against the request headers + * @return a predicate that tests against the given header predicate + */ + fun headers(headersPredicate: (ServerRequest.Headers) -> Boolean): RequestPredicate = + RequestPredicates.headers(headersPredicate) + + /** + * Route to the given handler function if the given method predicate applies. + * @see RouterFunctions.route + */ + fun method(httpMethod: HttpMethod, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.add(RouterFunctions.route(RequestPredicates.method(httpMethod), HandlerFunction { f(it).cast(ServerResponse::class.java) }), oc) + } + + /** + * Return a [RequestPredicate] that tests against the given HTTP method. + * @param httpMethod the HTTP method to match to + * @return a predicate that tests against the given HTTP method + */ + fun method(httpMethod: HttpMethod): RequestPredicate = RequestPredicates.method(httpMethod) + + /** + * Route to the given handler function if the given path predicate applies. + * @see RouterFunctions.route + */ + fun path(pattern: String, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.add(RouterFunctions.route(RequestPredicates.path(pattern), HandlerFunction { f(it).cast(ServerResponse::class.java) }), oc) + } + + /** + * Return a [RequestPredicate] that tests the request path against the given path pattern. + * @see RequestPredicates.path + */ + fun path(pattern: String): RequestPredicate = RequestPredicates.path(pattern) + + /** + * Route to the given handler function if the given pathExtension predicate applies. + * @see RouterFunctions.route + */ + fun pathExtension(extension: String, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.add(RouterFunctions.route(RequestPredicates.pathExtension(extension), HandlerFunction { f(it).cast(ServerResponse::class.java) }), oc) + } + + /** + * Return a [RequestPredicate] that matches if the request's path has the given extension. + * @param extension the path extension to match against, ignoring case + * @return a predicate that matches if the request's path has the given file extension + */ + fun pathExtension(extension: String): RequestPredicate = RequestPredicates.pathExtension(extension) + + /** + * Route to the given handler function if the given pathExtension predicate applies. + * @see RouterFunctions.route + */ + fun pathExtension(predicate: (String) -> Boolean, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.add(RouterFunctions.route(RequestPredicates.pathExtension(predicate), HandlerFunction { f(it).cast(ServerResponse::class.java) }), oc) + } + + /** + * Return a [RequestPredicate] that matches if the request's path matches the given + * predicate. + * @see RequestPredicates.pathExtension + */ + fun pathExtension(predicate: (String) -> Boolean): RequestPredicate = + RequestPredicates.pathExtension(predicate) + + /** + * Route to the given handler function if the given queryParam predicate applies. + * @see RouterFunctions.route + */ + fun queryParam(name: String, predicate: (String) -> Boolean, f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.add(RouterFunctions.route(RequestPredicates.queryParam(name, predicate), HandlerFunction { f(it).cast(ServerResponse::class.java) }), oc) + } + + /** + * Return a [RequestPredicate] that tests the request's query parameter of the given name + * against the given predicate. + * @param name the name of the query parameter to test against + * @param predicate the predicate to test against the query parameter value + * @return a predicate that matches the given predicate against the query parameter of the given name + * @see ServerRequest#queryParam(String) + */ + fun queryParam(name: String, predicate: (String) -> Boolean): RequestPredicate = + RequestPredicates.queryParam(name, predicate) + + /** + * Route to the given handler function if the given request predicate applies. + * @see RouterFunctions.route + */ + operator fun RequestPredicate.invoke(f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.add(RouterFunctions.route(this, HandlerFunction { f(it).cast(ServerResponse::class.java) }), oc) + } + + /** + * Route to the given handler function if the given predicate (String + * processed as a path predicate) applies. + * @see RouterFunctions.route + */ + operator fun String.invoke(f: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.add(RouterFunctions.route(RequestPredicates.path(this), HandlerFunction { f(it).cast(ServerResponse::class.java) }), oc) + } + + /** + * Route requests that match the given pattern to resources relative to the given root location. + * @see RouterFunctions.resources + */ + fun resources(path: String, location: Resource, oc: (Builder) -> Unit = {}) { + builder.resources(path, location, oc) + } + + /** + * Route to resources using the provided lookup function. If the lookup function provides a + * [Resource] for the given request, it will be it will be exposed using a + * [HandlerFunction] that handles GET, HEAD, and OPTIONS requests. + */ + fun resources(lookupFunction: (ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.resources(lookupFunction, oc) + } + + /** + * Merge externally defined router functions into this one. + * @param routerFunction the router function to be added + * @since 5.2 + */ + fun add(routerFunction: RouterFunction, oc: (Builder) -> Unit = {}) { + builder.add(routerFunction, oc) + } + + /** + * Filters all routes created by this router with the given filter function. Filter + * functions are typically used to address cross-cutting concerns, such as logging, + * security, etc. + * @param filterFunction the function to filter all routes built by this router + * @since 5.2 + */ + fun filter(filterFunction: (ServerRequest, (ServerRequest) -> Mono) -> Mono, oc: (Builder) -> Unit = {}) { + builder.filter({ request, next -> + filterFunction(request) { + next.handle(request) + } + }, oc) + } + + /** + * Filter the request object for all routes created by this builder with the given request + * processing function. Filters are typically used to address cross-cutting concerns, such + * as logging, security, etc. + * @param requestProcessor a function that transforms the request + * @since 5.2 + */ + fun before(requestProcessor: (ServerRequest) -> ServerRequest, oc: (Builder) -> Unit = {}) { + builder.before(requestProcessor, oc) + } + + /** + * Filter the response object for all routes created by this builder with the given response + * processing function. Filters are typically used to address cross-cutting concerns, such + * as logging, security, etc. + * @param responseProcessor a function that transforms the response + * @since 5.2 + */ + fun after(responseProcessor: (ServerRequest, ServerResponse) -> ServerResponse, oc: (Builder) -> Unit = {}) { + builder.after(responseProcessor, oc) + } + + /** + * Filters all exceptions that match the predicate by applying the given response provider + * function. + * @param predicate the type of exception to filter + * @param responseProvider a function that creates a response + * @since 5.2 + */ + fun onError(predicate: (Throwable) -> Boolean, responseProvider: (Throwable, ServerRequest) -> Mono, oc: (Builder) -> Unit = {}) { + builder.onError(predicate, responseProvider, oc) + } + + /** + * Filters all exceptions that match the predicate by applying the given response provider + * function. + * @param E the type of exception to filter + * @param responseProvider a function that creates a response + * @since 5.2 + */ + inline fun onError(noinline responseProvider: (Throwable, ServerRequest) -> Mono, noinline oc: (Builder) -> Unit = {}) { + builder.onError({it is E}, responseProvider, oc) + } + + /** + * Return a composed routing function created from all the registered routes. + * @since 5.1 + */ + internal fun build(): RouterFunction { + init() + return builder.build() + } + + /** + * Create a builder with the status code and headers of the given response. + * @param other the response to copy the status and headers from + * @return the created builder + * @since 5.1 + */ + fun from(other: ServerResponse): ServerResponse.BodyBuilder = + ServerResponse.from(other) + + /** + * Create a builder with the given HTTP status. + * @param status the response status + * @return the created builder + * @since 5.1 + */ + fun status(status: HttpStatus): ServerResponse.BodyBuilder = + ServerResponse.status(status) + + /** + * Create a builder with the given HTTP status. + * @param status the response status + * @return the created builder + * @since 5.1 + */ + fun status(status: Int): ServerResponse.BodyBuilder = + ServerResponse.status(status) + + /** + * Create a builder with the status set to [200 OK][HttpStatus.OK]. + * @return the created builder + * @since 5.1 + */ + fun ok(): ServerResponse.BodyBuilder = + ServerResponse.ok() + + /** + * Create a new builder with a [201 Created][HttpStatus.CREATED] status + * and a location header set to the given URI. + * @param location the location URI + * @return the created builder + * @since 5.1 + */ + fun created(location: URI): ServerResponse.BodyBuilder = + ServerResponse.created(location) + + /** + * Create a builder with an [202 Accepted][HttpStatus.ACCEPTED] status. + * @return the created builder + * @since 5.1 + */ + fun accepted(): ServerResponse.BodyBuilder = + ServerResponse.accepted() + + /** + * Create a builder with a [204 No Content][HttpStatus.NO_CONTENT] status. + * @return the created builder + * @since 5.1 + */ + fun noContent(): ServerResponse.HeadersBuilder<*> = + ServerResponse.noContent() + + /** + * Create a builder with a [303 See Other][HttpStatus.SEE_OTHER] + * status and a location header set to the given URI. + * @param location the location URI + * @return the created builder + * @since 5.1 + */ + fun seeOther(location: URI): ServerResponse.BodyBuilder = + ServerResponse.seeOther(location) + + /** + * Create a builder with a [307 Temporary Redirect][HttpStatus.TEMPORARY_REDIRECT] + * status and a location header set to the given URI. + * @param location the location URI + * @return the created builder + * @since 5.1 + */ + fun temporaryRedirect(location: URI): ServerResponse.BodyBuilder = + ServerResponse.temporaryRedirect(location) + + /** + * Create a builder with a [308 Permanent Redirect][HttpStatus.PERMANENT_REDIRECT] + * status and a location header set to the given URI. + * @param location the location URI + * @return the created builder + * @since 5.1 + */ + fun permanentRedirect(location: URI): ServerResponse.BodyBuilder = + ServerResponse.permanentRedirect(location) + + /** + * Create a builder with a [400 Bad Request][HttpStatus.BAD_REQUEST] status. + * @return the created builder + * @since 5.1 + */ + fun badRequest(): ServerResponse.BodyBuilder = + ServerResponse.badRequest() + + /** + * Create a builder with a [404 Not Found][HttpStatus.NOT_FOUND] status. + * @return the created builder + * @since 5.1 + */ + fun notFound(): ServerResponse.HeadersBuilder<*> = + ServerResponse.notFound() + + /** + * Create a builder with an + * [422 Unprocessable Entity][HttpStatus.UNPROCESSABLE_ENTITY] status. + * @return the created builder + * @since 5.1 + */ + fun unprocessableEntity(): ServerResponse.BodyBuilder = + ServerResponse.unprocessableEntity() +}