From 9ad5a8ead0f573d8442d570209414d5a1da1f8f9 Mon Sep 17 00:00:00 2001 From: Igor Manushin Date: Fri, 24 Apr 2020 22:28:22 +0100 Subject: [PATCH] Add KClass variant to Coroutines web API Closes gh-24967 --- .../client/ClientResponseExtensions.kt | 53 ++++++++++++++++++- .../server/ServerRequestExtensions.kt | 31 +++++++++++ .../client/ClientResponseExtensionsTests.kt | 47 +++++++++++++++- .../server/ServerRequestExtensionsTests.kt | 29 ++++++++-- 4 files changed, 155 insertions(+), 5 deletions(-) diff --git a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/client/ClientResponseExtensions.kt b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/client/ClientResponseExtensions.kt index 6b59b0b066..c34d0422ae 100644 --- a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/client/ClientResponseExtensions.kt +++ b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/client/ClientResponseExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-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. @@ -24,6 +24,7 @@ import org.springframework.core.ParameterizedTypeReference import org.springframework.http.ResponseEntity import reactor.core.publisher.Flux import reactor.core.publisher.Mono +import kotlin.reflect.KClass /** * Extension for [ClientResponse.bodyToMono] providing a `bodyToMono()` variant @@ -56,6 +57,16 @@ inline fun ClientResponse.bodyToFlux(): Flux = inline fun ClientResponse.bodyToFlow(): Flow = bodyToFlux().asFlow() +/** + * `KClass` coroutines [kotlinx.coroutines.flow.Flow] based variant of [ClientResponse.bodyToFlux]. + * Please consider `bodyToFlow` variant if possible. + * + * @author Igor Manushin + * @since 5.3 + */ +fun ClientResponse.bodyToFlow(clazz: KClass): Flow = + bodyToFlux(clazz.java).asFlow() + /** * Extension for [ClientResponse.toEntity] providing a `toEntity()` variant * leveraging Kotlin reified type parameters. This extension is not subject to type @@ -87,6 +98,16 @@ inline fun ClientResponse.toEntityList(): Mono ClientResponse.awaitBody(): T = bodyToMono().awaitSingle() +/** + * `KClass` non-nullable coroutines variant of [ClientResponse.bodyToMono]. + * Please consider `awaitBody` variant if possible. + * + * @author Igor Manushin + * @since 5.3 + */ +suspend fun ClientResponse.awaitBody(clazz: KClass): T = + bodyToMono(clazz.java).awaitSingle() + /** * Nullable coroutines variant of [ClientResponse.bodyToMono]. * @@ -96,6 +117,16 @@ suspend inline fun ClientResponse.awaitBody(): T = suspend inline fun ClientResponse.awaitBodyOrNull(): T? = bodyToMono().awaitFirstOrNull() +/** + * `KClass` nullable coroutines variant of [ClientResponse.bodyToMono]. + * Please consider `awaitBodyOrNull` variant if possible. + * + * @author Igor Manushin + * @since 5.3 + */ +suspend fun ClientResponse.awaitBodyOrNull(clazz: KClass): T? = + bodyToMono(clazz.java).awaitFirstOrNull() + /** * Coroutines variant of [ClientResponse.toEntity]. * @@ -105,6 +136,16 @@ suspend inline fun ClientResponse.awaitBodyOrNull(): T? = suspend inline fun ClientResponse.awaitEntity(): ResponseEntity = toEntity().awaitSingle() +/** + * `KClass` coroutines variant of [ClientResponse.toEntity]. + * Please consider `awaitEntity` variant if possible. + * + * @author Igor Manushin + * @since 5.3 + */ +suspend fun ClientResponse.awaitEntity(clazz: KClass): ResponseEntity = + toEntity(clazz.java).awaitSingle() + /** * Coroutines variant of [ClientResponse.toEntityList]. * @@ -113,3 +154,13 @@ suspend inline fun ClientResponse.awaitEntity(): ResponseEntit */ suspend inline fun ClientResponse.awaitEntityList(): ResponseEntity> = toEntityList().awaitSingle() + +/** + * `KClass` coroutines variant of [ClientResponse.toEntityList]. + * Please consider `awaitEntityList` variant if possible. + * + * @author Igor Manushin + * @since 5.3 + */ +suspend fun ClientResponse.awaitEntityList(clazz: KClass): ResponseEntity> = + toEntityList(clazz.java).awaitSingle() diff --git a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensions.kt b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensions.kt index 2c8df65950..42b877be30 100644 --- a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensions.kt +++ b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensions.kt @@ -30,6 +30,7 @@ import reactor.core.publisher.Flux import reactor.core.publisher.Mono import java.net.InetSocketAddress import java.security.Principal +import kotlin.reflect.KClass /** * Extension for [ServerRequest.bodyToMono] providing a `bodyToMono()` variant @@ -62,6 +63,16 @@ inline fun ServerRequest.bodyToFlux(): Flux = inline fun ServerRequest.bodyToFlow(): Flow = bodyToFlux().asFlow() +/** + * `KClass` coroutines [kotlinx.coroutines.flow.Flow] based variant of [ServerRequest.bodyToFlux]. + * Please consider `bodyToFlow` variant if possible. + * + * @author Igor Manushin + * @since 5.3 + */ +fun ServerRequest.bodyToFlow(clazz: KClass): Flow = + bodyToFlux(clazz.java).asFlow() + /** * Non-nullable Coroutines variant of [ServerRequest.bodyToMono]. * @@ -71,6 +82,16 @@ inline fun ServerRequest.bodyToFlow(): Flow = suspend inline fun ServerRequest.awaitBody(): T = bodyToMono().awaitSingle() +/** + * `KClass` non-nullable Coroutines variant of [ServerRequest.bodyToMono]. + * Please consider `awaitBody` variant if possible. + * + * @author Igor Manushin + * @since 5.3 + */ +suspend fun ServerRequest.awaitBody(clazz: KClass): T = + bodyToMono(clazz.java).awaitSingle() + /** * Nullable Coroutines variant of [ServerRequest.bodyToMono]. * @@ -80,6 +101,16 @@ suspend inline fun ServerRequest.awaitBody(): T = suspend inline fun ServerRequest.awaitBodyOrNull(): T? = bodyToMono().awaitFirstOrNull() +/** + * `KClass` nullable Coroutines variant of [ServerRequest.bodyToMono]. + * Please consider `awaitBodyOrNull` variant if possible. + * + * @author Igor Manushin + * @since 5.3 + */ +suspend fun ServerRequest.awaitBodyOrNull(clazz: KClass): T? = + bodyToMono(clazz.java).awaitFirstOrNull() + /** * Coroutines variant of [ServerRequest.formData]. * diff --git a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/client/ClientResponseExtensionsTests.kt b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/client/ClientResponseExtensionsTests.kt index 980d555cf9..6f2e8f2023 100644 --- a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/client/ClientResponseExtensionsTests.kt +++ b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/client/ClientResponseExtensionsTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-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. @@ -31,6 +31,7 @@ import reactor.core.publisher.Mono * Mock object based tests for [ClientResponse] Kotlin extensions. * * @author Sebastien Deleuze + * @author Igor Manushin */ class ClientResponseExtensionsTests { @@ -54,6 +55,12 @@ class ClientResponseExtensionsTests { verify { response.bodyToFlux(object : ParameterizedTypeReference>() {}) } } + @Test + fun `bodyToFlow with KClass parameter`() { + response.bodyToFlow(Foo::class) + verify { response.bodyToFlux(Foo::class.java) } + } + @Test fun `toEntity with reified type parameters`() { response.toEntity>() @@ -75,6 +82,15 @@ class ClientResponseExtensionsTests { } } + @Test + fun `awaitBody with KClass parameter`() { + val response = mockk() + every { response.bodyToMono(String::class.java) } returns Mono.just("foo") + runBlocking { + assertThat(response.awaitBody(String::class)).isEqualTo("foo") + } + } + @Test fun awaitBodyOrNull() { val response = mockk() @@ -84,6 +100,15 @@ class ClientResponseExtensionsTests { } } + @Test + fun `awaitBodyOrNullGeneric with KClass parameter`() { + val response = mockk() + every { response.bodyToMono(String::class.java) } returns Mono.empty() + runBlocking { + assertThat(response.awaitBodyOrNull(String::class)).isNull() + } + } + @Test fun awaitEntity() { val response = mockk() @@ -94,6 +119,16 @@ class ClientResponseExtensionsTests { } } + @Test + fun `awaitEntity with KClass parameter`() { + val response = mockk() + val entity = ResponseEntity("foo", HttpStatus.OK) + every { response.toEntity(String::class.java) } returns Mono.just(entity) + runBlocking { + assertThat(response.awaitEntity(String::class)).isEqualTo(entity) + } + } + @Test fun awaitEntityList() { val response = mockk() @@ -104,5 +139,15 @@ class ClientResponseExtensionsTests { } } + @Test + fun `awaitEntityList with KClass parameter`() { + val response = mockk() + val entity = ResponseEntity(listOf("foo"), HttpStatus.OK) + every { response.toEntityList(String::class.java) } returns Mono.just(entity) + runBlocking { + assertThat(response.awaitEntityList(String::class)).isEqualTo(entity) + } + } + class Foo } diff --git a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensionsTests.kt b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensionsTests.kt index 4922b04097..5406a6883c 100644 --- a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensionsTests.kt +++ b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensionsTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-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. @@ -37,6 +37,7 @@ import java.util.* * Mock object based tests for [ServerRequest] Kotlin extensions. * * @author Sebastien Deleuze + * @author Igor Manushin */ class ServerRequestExtensionsTests { @@ -63,7 +64,13 @@ class ServerRequestExtensionsTests { } @Test - fun awaitBody() { + fun `bodyToFlow with KClass parameters`() { + request.bodyToFlow(String::class) + verify { request.bodyToFlux(String::class.java) } + } + + @Test + fun `awaitBody with reified type parameters`() { every { request.bodyToMono() } returns Mono.just("foo") runBlocking { assertThat(request.awaitBody()).isEqualTo("foo") @@ -71,13 +78,29 @@ class ServerRequestExtensionsTests { } @Test - fun awaitBodyOrNull() { + fun `awaitBody with KClass parameters`() { + every { request.bodyToMono(String::class.java) } returns Mono.just("foo") + runBlocking { + assertThat(request.awaitBody(String::class)).isEqualTo("foo") + } + } + + @Test + fun `awaitBodyOrNull with reified type parameters`() { every { request.bodyToMono() } returns Mono.empty() runBlocking { assertThat(request.awaitBodyOrNull()).isNull() } } + @Test + fun `awaitBodyOrNull with KClass parameters`() { + every { request.bodyToMono(String::class.java) } returns Mono.empty() + runBlocking { + assertThat(request.awaitBodyOrNull(String::class)).isNull() + } + } + @Test fun awaitFormData() { val map = mockk>()