From b46082b1b042051577c754d3dd4335f284e7b272 Mon Sep 17 00:00:00 2001 From: Greg Turnquist Date: Fri, 3 Apr 2020 15:35:18 -0500 Subject: [PATCH] #1213 - Add support for Kotlin co-routines. The ReactiveRepresentationModelAssembler and SimpleRepresentationModelAssembler both accept Flux inputs for entities. This change adds support for Kotlin co-routines to the same inputs. Related pull request: #1258. --- pom.xml | 29 ++++++++- ...iveRepresentationModelAssemblerBuildDsl.kt | 35 ++++++++++ ...iveRepresentationModelAssemblerBuildDsl.kt | 35 ++++++++++ ...ntationModelAssemblerBuilderDslUnitTest.kt | 65 +++++++++++++++++++ ...ntationModelAssemblerBuilderDslUnitTest.kt | 59 +++++++++++++++++ 5 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuildDsl.kt create mode 100644 src/main/kotlin/org/springframework/hateoas/server/reactive/SimpleReactiveRepresentationModelAssemblerBuildDsl.kt create mode 100644 src/test/kotlin/org/springframework/hateoas/server/reactive/ReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt create mode 100644 src/test/kotlin/org/springframework/hateoas/server/reactive/SimpleReactiveRepresentationModelAssemblerBuilderDslUnitTest.kt 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) + +}