diff --git a/pom.xml b/pom.xml
index 37a7151c..15d92a86 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,6 +85,8 @@
5.2.5.RELEASE
2.0.0.RELEASE
1.3.71
+ 1.3.5
+ 1.9.3
@@ -704,6 +706,20 @@
pom
import
+
+ org.jetbrains.kotlin
+ kotlin-bom
+ ${kotlin.version}
+ pom
+ import
+
+
+ org.jetbrains.kotlinx
+ kotlinx-coroutines-bom
+ ${kotlinx-coroutines.version}
+ pom
+ import
+
@@ -713,13 +729,16 @@
org.jetbrains.kotlin
kotlin-stdlib
- ${kotlin.version}
true
org.jetbrains.kotlin
kotlin-reflect
- ${kotlin.version}
+ true
+
+
+ org.jetbrains.kotlinx
+ kotlinx-coroutines-reactor
true
@@ -728,6 +747,12 @@
${kotlin.version}
test
+
+ io.mockk
+ mockk
+ ${mockk.version}
+ test
+
org.springframework
diff --git a/src/main/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuildDsl.kt b/src/main/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuildDsl.kt
new file mode 100644
index 00000000..051ea829
--- /dev/null
+++ b/src/main/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuildDsl.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.hateoas.server.reactive
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.reactive.awaitFirst
+import kotlinx.coroutines.reactor.asFlux
+import org.springframework.hateoas.CollectionModel
+import org.springframework.hateoas.RepresentationModel
+import org.springframework.web.server.ServerWebExchange
+
+/**
+ * Add support for Kotlin co-routines.
+ *
+ * @author Juergen Zimmerman
+ * @author Greg Turnquist
+ * @since 1.1
+ */
+suspend fun > ReactiveRepresentationModelAssembler.toCollectionModel(
+ entities: Flow,
+ exchange: ServerWebExchange): CollectionModel = toCollectionModel(entities.asFlux(), exchange).awaitFirst()
diff --git a/src/main/kotlin/org/springframework/hateoas/server/reactive/SimpleReactiveRepresentationModelAssemblerBuildDsl.kt b/src/main/kotlin/org/springframework/hateoas/server/reactive/SimpleReactiveRepresentationModelAssemblerBuildDsl.kt
new file mode 100644
index 00000000..d35721e0
--- /dev/null
+++ b/src/main/kotlin/org/springframework/hateoas/server/reactive/SimpleReactiveRepresentationModelAssemblerBuildDsl.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.hateoas.server.reactive
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.reactive.awaitFirst
+import kotlinx.coroutines.reactor.asFlux
+import org.springframework.hateoas.CollectionModel
+import org.springframework.hateoas.EntityModel
+import org.springframework.web.server.ServerWebExchange
+
+/**
+ * Add support for Kotlin co-routines.
+ *
+ * @author Juergen Zimmerman
+ * @author Greg Turnquist
+ * @since 1.1
+ */
+suspend fun SimpleReactiveRepresentationModelAssembler.toCollectionModel(
+ entities: Flow,
+ exchange: ServerWebExchange): CollectionModel> = toCollectionModel(entities.asFlux(), exchange).awaitFirst()
diff --git a/src/test/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt b/src/test/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt
new file mode 100644
index 00000000..36409c8a
--- /dev/null
+++ b/src/test/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2019-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.hateoas.server.reactive
+
+import io.mockk.mockk
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.runBlocking
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import org.springframework.hateoas.EntityModel
+import org.springframework.hateoas.Link
+import org.springframework.hateoas.LinkRelation
+import org.springframework.hateoas.TestUtils
+import org.springframework.web.server.ServerWebExchange
+import reactor.core.publisher.Mono
+
+/**
+ * @author Greg Turnquist
+ * @since 1.1
+ */
+class ReactiveRepresentationModelAssemblerBuilderDslUnitTest : TestUtils() {
+
+ private val EMPLOYEES_RELATION = LinkRelation.of("employees")
+
+ private val employees = flow {
+ emit(Employee("Frodo"))
+ emit(Employee("Bilbo"))
+ }
+
+ @Test // #1213
+ fun `Kotlin Flows should render as CollectionModels`() {
+
+ val testResourceAssembler = TestResourceAssembler()
+ val exchange = mockk()
+
+ runBlocking {
+ val collectionModel = testResourceAssembler.toCollectionModel(employees, exchange)
+
+ assertThat(collectionModel.content.flatMap { entityModel -> entityModel.links })
+ .containsExactlyInAnyOrder(Link.of("/employees", EMPLOYEES_RELATION), Link.of("/employees", EMPLOYEES_RELATION))
+ }
+ }
+
+ class TestResourceAssembler : ReactiveRepresentationModelAssembler> {
+ override fun toModel(entity: Employee, exchange: ServerWebExchange): Mono> {
+ return Mono.just(EntityModel.of(entity, Link.of("/employees", LinkRelation.of("employees"))))
+ }
+ }
+
+ data class Employee(val name: String)
+
+}
diff --git a/src/test/kotlin/org/springframework/hateoas/server/reactive/SimpleReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt b/src/test/kotlin/org/springframework/hateoas/server/reactive/SimpleReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt
new file mode 100644
index 00000000..aa647dbf
--- /dev/null
+++ b/src/test/kotlin/org/springframework/hateoas/server/reactive/SimpleReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.hateoas.server.reactive
+
+import io.mockk.mockk
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.runBlocking
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import org.springframework.hateoas.*
+import org.springframework.web.server.ServerWebExchange
+
+/**
+ * @author Greg Turnquist
+ * @since 1.1
+ */
+class SimpleReactiveRexpresentationModelAssemblerBuilderDslUnitTest : TestUtils() {
+
+ private val employees = flow {
+ emit(Employee("Frodo"))
+ emit(Employee("Bilbo"))
+ }
+
+ @Test // #1213
+ fun `Kotlin Flows should render as CollectionModels`() {
+
+ val testResourceAssembler = SimpleTestResourceAssembler()
+ val exchange = mockk()
+
+ runBlocking {
+ val collectionModel = testResourceAssembler.toCollectionModel(employees, exchange)
+
+ assertThat(collectionModel.links).containsExactly(Link.of("/employees"))
+ }
+ }
+
+ class SimpleTestResourceAssembler : SimpleReactiveRepresentationModelAssembler {
+ override fun addLinks(resources: CollectionModel>, exchange: ServerWebExchange): CollectionModel> {
+ resources.add(Link.of("/employees"))
+ return resources
+ }
+ }
+
+ data class Employee(val name: String)
+
+}