Skip to content

Commit bbabf8d

Browse files
christophejanpoutsma
authored andcommitted
Add minimal Kotlin DSL RouterFunction attributes support
Closes gh-28567
1 parent 5b1bda5 commit bbabf8d

File tree

6 files changed

+222
-6
lines changed

6 files changed

+222
-6
lines changed

spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/CoRouterFunctionDsl.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -586,6 +586,30 @@ class CoRouterFunctionDsl internal constructor (private val init: (CoRouterFunct
586586
}
587587
}
588588

589+
/**
590+
* Add an attribute with the given name and value to the last route built with this builder.
591+
* @param name the attribute name
592+
* @param value the attribute value
593+
- * @since 5.3
594+
*/
595+
fun withAttribute(name: String, value: Any) {
596+
builder.withAttribute(name, value)
597+
}
598+
599+
/**
600+
* Manipulate the attributes of the last route built with the given consumer.
601+
*
602+
* The map provided to the consumer is "live", so that the consumer can be used
603+
* to [overwrite][Map.put] existing attributes,
604+
* [remove][Map.remove] attributes, or use any of the other
605+
* [Map] methods.
606+
* @param attributesConsumer a function that consumes the attributes map
607+
* @since 5.3
608+
*/
609+
fun withAttributes(attributesConsumer: (MutableMap<String, Any>) -> Unit) {
610+
builder.withAttributes(attributesConsumer)
611+
}
612+
589613
/**
590614
* Return a composed routing function created from all the registered routes.
591615
*/

spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -702,6 +702,30 @@ class RouterFunctionDsl internal constructor (private val init: RouterFunctionDs
702702
builder.onError({it is E}, responseProvider)
703703
}
704704

705+
/**
706+
* Add an attribute with the given name and value to the last route built with this builder.
707+
* @param name the attribute name
708+
* @param value the attribute value
709+
* @since 5.3
710+
*/
711+
fun withAttribute(name: String, value: Any) {
712+
builder.withAttribute(name, value)
713+
}
714+
715+
/**
716+
* Manipulate the attributes of the last route built with the given consumer.
717+
*
718+
* The map provided to the consumer is "live", so that the consumer can be used
719+
* to [overwrite][Map.put] existing attributes,
720+
* [remove][Map.remove] attributes, or use any of the other
721+
* [Map] methods.
722+
* @param attributesConsumer a function that consumes the attributes map
723+
* @since 5.3
724+
*/
725+
fun withAttributes(attributesConsumer: (MutableMap<String, Any>) -> Unit) {
726+
builder.withAttributes(attributesConsumer)
727+
}
728+
705729
/**
706730
* Return a composed routing function created from all the registered routes.
707731
* @since 5.1

spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/server/CoRouterFunctionDslTests.kt

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.web.reactive.function.server
1818

19+
import org.assertj.core.api.Assertions.assertThat
1920
import org.assertj.core.api.Assertions.assertThatExceptionOfType
2021
import org.junit.jupiter.api.Test
2122
import org.springframework.core.io.ClassPathResource
@@ -25,6 +26,7 @@ import org.springframework.http.HttpStatus
2526
import org.springframework.http.MediaType.*
2627
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest.*
2728
import org.springframework.web.testfixture.server.MockServerWebExchange
29+
import org.springframework.web.reactive.function.server.AttributesTestVisitor
2830
import reactor.test.StepVerifier
2931

3032
/**
@@ -163,6 +165,20 @@ class CoRouterFunctionDslTests {
163165
.verifyComplete()
164166
}
165167

168+
@Test
169+
fun attributes() {
170+
val visitor = AttributesTestVisitor()
171+
attributesRouter.accept(visitor)
172+
assertThat(visitor.routerFunctionsAttributes()).containsExactly(
173+
listOf(mapOf("foo" to "bar", "baz" to "qux")),
174+
listOf(mapOf("foo" to "bar", "baz" to "qux")),
175+
listOf(mapOf("foo" to "bar"), mapOf("foo" to "n1")),
176+
listOf(mapOf("baz" to "qux"), mapOf("foo" to "n1")),
177+
listOf(mapOf("foo" to "n3"), mapOf("foo" to "n2"), mapOf("foo" to "n1"))
178+
);
179+
assertThat(visitor.visitCount()).isEqualTo(7);
180+
}
181+
166182
private fun sampleRouter() = coRouter {
167183
(GET("/foo/") or GET("/foos/")) { req -> handle(req) }
168184
"/api".nest {
@@ -231,6 +247,39 @@ class CoRouterFunctionDslTests {
231247
}
232248
}
233249

250+
private val attributesRouter = router {
251+
GET("/atts/1") {
252+
ok().build()
253+
}
254+
withAttribute("foo", "bar")
255+
withAttribute("baz", "qux")
256+
GET("/atts/2") {
257+
ok().build()
258+
}
259+
withAttributes { atts ->
260+
atts["foo"] = "bar"
261+
atts["baz"] = "qux"
262+
}
263+
"/atts".nest {
264+
GET("/3") {
265+
ok().build()
266+
}
267+
withAttribute("foo", "bar")
268+
GET("/4") {
269+
ok().build()
270+
}
271+
withAttribute("baz", "qux")
272+
"/5".nest {
273+
GET {
274+
ok().build()
275+
}
276+
withAttribute("foo", "n3")
277+
}
278+
withAttribute("foo", "n2")
279+
}
280+
withAttribute("foo", "n1")
281+
}
282+
234283
@Suppress("UNUSED_PARAMETER")
235284
private suspend fun handleFromClass(req: ServerRequest) = ServerResponse.ok().buildAndAwait()
236285
}

spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDslTests.kt

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.web.reactive.function.server
1818

19+
import org.assertj.core.api.Assertions.assertThat
1920
import org.assertj.core.api.Assertions.assertThatExceptionOfType
2021
import org.junit.jupiter.api.Test
2122
import org.springframework.core.io.ClassPathResource
@@ -25,6 +26,7 @@ import org.springframework.http.HttpStatus
2526
import org.springframework.http.MediaType.*
2627
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest.*
2728
import org.springframework.web.testfixture.server.MockServerWebExchange
29+
import org.springframework.web.reactive.function.server.AttributesTestVisitor
2830
import reactor.core.publisher.Mono
2931
import reactor.test.StepVerifier
3032

@@ -153,6 +155,19 @@ class RouterFunctionDslTests {
153155
}
154156
}
155157

158+
@Test
159+
fun attributes() {
160+
val visitor = AttributesTestVisitor()
161+
attributesRouter.accept(visitor)
162+
assertThat(visitor.routerFunctionsAttributes()).containsExactly(
163+
listOf(mapOf("foo" to "bar", "baz" to "qux")),
164+
listOf(mapOf("foo" to "bar", "baz" to "qux")),
165+
listOf(mapOf("foo" to "bar"), mapOf("foo" to "n1")),
166+
listOf(mapOf("baz" to "qux"), mapOf("foo" to "n1")),
167+
listOf(mapOf("foo" to "n3"), mapOf("foo" to "n2"), mapOf("foo" to "n1"))
168+
);
169+
assertThat(visitor.visitCount()).isEqualTo(7);
170+
}
156171

157172
private fun sampleRouter() = router {
158173
(GET("/foo/") or GET("/foos/")) { req -> handle(req) }
@@ -210,6 +225,39 @@ class RouterFunctionDslTests {
210225
}
211226
}
212227

228+
private val attributesRouter = router {
229+
GET("/atts/1") {
230+
ok().build()
231+
}
232+
withAttribute("foo", "bar")
233+
withAttribute("baz", "qux")
234+
GET("/atts/2") {
235+
ok().build()
236+
}
237+
withAttributes { atts ->
238+
atts["foo"] = "bar"
239+
atts["baz"] = "qux"
240+
}
241+
"/atts".nest {
242+
GET("/3") {
243+
ok().build()
244+
}
245+
withAttribute("foo", "bar")
246+
GET("/4") {
247+
ok().build()
248+
}
249+
withAttribute("baz", "qux")
250+
"/5".nest {
251+
GET {
252+
ok().build()
253+
}
254+
withAttribute("foo", "n3")
255+
}
256+
withAttribute("foo", "n2")
257+
}
258+
withAttribute("foo", "n1")
259+
}
260+
213261
@Suppress("UNUSED_PARAMETER")
214262
private fun handleFromClass(req: ServerRequest) = ServerResponse.ok().build()
215263
}

spring-webmvc/src/main/kotlin/org/springframework/web/servlet/function/RouterFunctionDsl.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -699,6 +699,30 @@ class RouterFunctionDsl internal constructor (private val init: (RouterFunctionD
699699
builder.onError({it is E}, responseProvider)
700700
}
701701

702+
/**
703+
* Add an attribute with the given name and value to the last route built with this builder.
704+
* @param name the attribute name
705+
* @param value the attribute value
706+
* @since 5.3
707+
*/
708+
fun withAttribute(name: String, value: Any) {
709+
builder.withAttribute(name, value)
710+
}
711+
712+
/**
713+
* Manipulate the attributes of the last route built with the given consumer.
714+
*
715+
* The map provided to the consumer is "live", so that the consumer can be used
716+
* to [overwrite][Map.put] existing attributes,
717+
* [remove][Map.remove] attributes, or use any of the other
718+
* [Map] methods.
719+
* @param attributesConsumer a function that consumes the attributes map
720+
* @since 5.3
721+
*/
722+
fun withAttributes(attributesConsumer: (MutableMap<String, Any>) -> Unit) {
723+
builder.withAttributes(attributesConsumer)
724+
}
725+
702726
/**
703727
* Return a composed routing function created from all the registered routes.
704728
*/

spring-webmvc/src/test/kotlin/org/springframework/web/servlet/function/RouterFunctionDslTests.kt

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -134,6 +134,20 @@ class RouterFunctionDslTests {
134134
assertThat(sampleRouter().route(request).get().handle(request).headers().getFirst("foo")).isEqualTo("bar")
135135
}
136136

137+
@Test
138+
fun attributes() {
139+
val visitor = AttributesTestVisitor()
140+
attributesRouter.accept(visitor)
141+
assertThat(visitor.routerFunctionsAttributes()).containsExactly(
142+
listOf(mapOf("foo" to "bar", "baz" to "qux")),
143+
listOf(mapOf("foo" to "bar", "baz" to "qux")),
144+
listOf(mapOf("foo" to "bar"), mapOf("foo" to "n1")),
145+
listOf(mapOf("baz" to "qux"), mapOf("foo" to "n1")),
146+
listOf(mapOf("foo" to "n3"), mapOf("foo" to "n2"), mapOf("foo" to "n1"))
147+
);
148+
assertThat(visitor.visitCount()).isEqualTo(7);
149+
}
150+
137151
private fun sampleRouter() = router {
138152
(GET("/foo/") or GET("/foos/")) { req -> handle(req) }
139153
"/api".nest {
@@ -202,6 +216,39 @@ class RouterFunctionDslTests {
202216
}
203217
}
204218

219+
private val attributesRouter = router {
220+
GET("/atts/1") {
221+
ok().build()
222+
}
223+
withAttribute("foo", "bar")
224+
withAttribute("baz", "qux")
225+
GET("/atts/2") {
226+
ok().build()
227+
}
228+
withAttributes { atts ->
229+
atts["foo"] = "bar"
230+
atts["baz"] = "qux"
231+
}
232+
"/atts".nest {
233+
GET("/3") {
234+
ok().build()
235+
}
236+
withAttribute("foo", "bar")
237+
GET("/4") {
238+
ok().build()
239+
}
240+
withAttribute("baz", "qux")
241+
"/5".nest {
242+
GET {
243+
ok().build()
244+
}
245+
withAttribute("foo", "n3")
246+
}
247+
withAttribute("foo", "n2")
248+
}
249+
withAttribute("foo", "n1")
250+
}
251+
205252
@Suppress("UNUSED_PARAMETER")
206253
private fun handleFromClass(req: ServerRequest) = ServerResponse.ok().build()
207254
}

0 commit comments

Comments
 (0)