Add KClass variant to Coroutines web API

Closes gh-24967
This commit is contained in:
Igor Manushin 2020-04-24 22:28:22 +01:00 committed by Sébastien Deleuze
parent 00651a3e38
commit 9ad5a8ead0
4 changed files with 155 additions and 5 deletions

View File

@ -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<Foo>()` variant
@ -56,6 +57,16 @@ inline fun <reified T : Any> ClientResponse.bodyToFlux(): Flux<T> =
inline fun <reified T : Any> ClientResponse.bodyToFlow(): Flow<T> =
bodyToFlux<T>().asFlow()
/**
* `KClass` coroutines [kotlinx.coroutines.flow.Flow] based variant of [ClientResponse.bodyToFlux].
* Please consider `bodyToFlow<Foo>` variant if possible.
*
* @author Igor Manushin
* @since 5.3
*/
fun <T : Any> ClientResponse.bodyToFlow(clazz: KClass<T>): Flow<T> =
bodyToFlux(clazz.java).asFlow()
/**
* Extension for [ClientResponse.toEntity] providing a `toEntity<Foo>()` variant
* leveraging Kotlin reified type parameters. This extension is not subject to type
@ -87,6 +98,16 @@ inline fun <reified T : Any> ClientResponse.toEntityList(): Mono<ResponseEntity<
suspend inline fun <reified T : Any> ClientResponse.awaitBody(): T =
bodyToMono<T>().awaitSingle()
/**
* `KClass` non-nullable coroutines variant of [ClientResponse.bodyToMono].
* Please consider `awaitBody<Foo>` variant if possible.
*
* @author Igor Manushin
* @since 5.3
*/
suspend fun <T : Any> ClientResponse.awaitBody(clazz: KClass<T>): T =
bodyToMono(clazz.java).awaitSingle()
/**
* Nullable coroutines variant of [ClientResponse.bodyToMono].
*
@ -96,6 +117,16 @@ suspend inline fun <reified T : Any> ClientResponse.awaitBody(): T =
suspend inline fun <reified T : Any> ClientResponse.awaitBodyOrNull(): T? =
bodyToMono<T>().awaitFirstOrNull()
/**
* `KClass` nullable coroutines variant of [ClientResponse.bodyToMono].
* Please consider `awaitBodyOrNull<Foo>` variant if possible.
*
* @author Igor Manushin
* @since 5.3
*/
suspend fun <T : Any> ClientResponse.awaitBodyOrNull(clazz: KClass<T>): T? =
bodyToMono(clazz.java).awaitFirstOrNull()
/**
* Coroutines variant of [ClientResponse.toEntity].
*
@ -105,6 +136,16 @@ suspend inline fun <reified T : Any> ClientResponse.awaitBodyOrNull(): T? =
suspend inline fun <reified T : Any> ClientResponse.awaitEntity(): ResponseEntity<T> =
toEntity<T>().awaitSingle()
/**
* `KClass` coroutines variant of [ClientResponse.toEntity].
* Please consider `awaitEntity<Foo>` variant if possible.
*
* @author Igor Manushin
* @since 5.3
*/
suspend fun <T : Any> ClientResponse.awaitEntity(clazz: KClass<T>): ResponseEntity<T> =
toEntity(clazz.java).awaitSingle()
/**
* Coroutines variant of [ClientResponse.toEntityList].
*
@ -113,3 +154,13 @@ suspend inline fun <reified T : Any> ClientResponse.awaitEntity(): ResponseEntit
*/
suspend inline fun <reified T : Any> ClientResponse.awaitEntityList(): ResponseEntity<List<T>> =
toEntityList<T>().awaitSingle()
/**
* `KClass` coroutines variant of [ClientResponse.toEntityList].
* Please consider `awaitEntityList<Foo>` variant if possible.
*
* @author Igor Manushin
* @since 5.3
*/
suspend fun <T : Any> ClientResponse.awaitEntityList(clazz: KClass<T>): ResponseEntity<List<T>> =
toEntityList(clazz.java).awaitSingle()

View File

@ -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<Foo>()` variant
@ -62,6 +63,16 @@ inline fun <reified T : Any> ServerRequest.bodyToFlux(): Flux<T> =
inline fun <reified T : Any> ServerRequest.bodyToFlow(): Flow<T> =
bodyToFlux<T>().asFlow()
/**
* `KClass` coroutines [kotlinx.coroutines.flow.Flow] based variant of [ServerRequest.bodyToFlux].
* Please consider `bodyToFlow<Foo>` variant if possible.
*
* @author Igor Manushin
* @since 5.3
*/
fun <T : Any> ServerRequest.bodyToFlow(clazz: KClass<T>): Flow<T> =
bodyToFlux(clazz.java).asFlow()
/**
* Non-nullable Coroutines variant of [ServerRequest.bodyToMono].
*
@ -71,6 +82,16 @@ inline fun <reified T : Any> ServerRequest.bodyToFlow(): Flow<T> =
suspend inline fun <reified T : Any> ServerRequest.awaitBody(): T =
bodyToMono<T>().awaitSingle()
/**
* `KClass` non-nullable Coroutines variant of [ServerRequest.bodyToMono].
* Please consider `awaitBody<Foo>` variant if possible.
*
* @author Igor Manushin
* @since 5.3
*/
suspend fun <T : Any> ServerRequest.awaitBody(clazz: KClass<T>): T =
bodyToMono(clazz.java).awaitSingle()
/**
* Nullable Coroutines variant of [ServerRequest.bodyToMono].
*
@ -80,6 +101,16 @@ suspend inline fun <reified T : Any> ServerRequest.awaitBody(): T =
suspend inline fun <reified T : Any> ServerRequest.awaitBodyOrNull(): T? =
bodyToMono<T>().awaitFirstOrNull()
/**
* `KClass` nullable Coroutines variant of [ServerRequest.bodyToMono].
* Please consider `awaitBodyOrNull<Foo>` variant if possible.
*
* @author Igor Manushin
* @since 5.3
*/
suspend fun <T : Any> ServerRequest.awaitBodyOrNull(clazz: KClass<T>): T? =
bodyToMono(clazz.java).awaitFirstOrNull()
/**
* Coroutines variant of [ServerRequest.formData].
*

View File

@ -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<List<Foo>>() {}) }
}
@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<List<Foo>>()
@ -75,6 +82,15 @@ class ClientResponseExtensionsTests {
}
}
@Test
fun `awaitBody with KClass parameter`() {
val response = mockk<ClientResponse>()
every { response.bodyToMono(String::class.java) } returns Mono.just("foo")
runBlocking {
assertThat(response.awaitBody(String::class)).isEqualTo("foo")
}
}
@Test
fun awaitBodyOrNull() {
val response = mockk<ClientResponse>()
@ -84,6 +100,15 @@ class ClientResponseExtensionsTests {
}
}
@Test
fun `awaitBodyOrNullGeneric with KClass parameter`() {
val response = mockk<ClientResponse>()
every { response.bodyToMono(String::class.java) } returns Mono.empty()
runBlocking {
assertThat(response.awaitBodyOrNull(String::class)).isNull()
}
}
@Test
fun awaitEntity() {
val response = mockk<ClientResponse>()
@ -94,6 +119,16 @@ class ClientResponseExtensionsTests {
}
}
@Test
fun `awaitEntity with KClass parameter`() {
val response = mockk<ClientResponse>()
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<ClientResponse>()
@ -104,5 +139,15 @@ class ClientResponseExtensionsTests {
}
}
@Test
fun `awaitEntityList with KClass parameter`() {
val response = mockk<ClientResponse>()
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
}

View File

@ -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<String>() } returns Mono.just("foo")
runBlocking {
assertThat(request.awaitBody<String>()).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<String>() } returns Mono.empty()
runBlocking {
assertThat(request.awaitBodyOrNull<String>()).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<MultiValueMap<String, String>>()