Skip to content

Commit 823305f

Browse files
committed
#63 - Add DatabaseClient Coroutines extensions.
This commit introduces Coroutines support for `DatabaseClient` functional API via Kotlin extensions that provide suspendable functions prefixed by `await` for `Mono` based APIs. Extensions for `Flux` will be added when Kotlin/kotlinx.coroutines#254 will be fixed. It also provides `asType<Foo>()` extensions useful for Reactive API as well. Original pull request: #63.
1 parent 8acc3d0 commit 823305f

File tree

5 files changed

+318
-0
lines changed

5 files changed

+318
-0
lines changed

pom.xml

+34
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<r2dbc-releasetrain.version>Arabba-M7</r2dbc-releasetrain.version>
3636
<reactive-streams.version>1.0.1</reactive-streams.version>
3737
<testcontainers.version>1.10.1</testcontainers.version>
38+
<coroutines.version>1.1.1</coroutines.version>
3839

3940
</properties>
4041

@@ -126,6 +127,32 @@
126127
<artifactId>reactor-core</artifactId>
127128
</dependency>
128129

130+
<!-- Kotlin extension -->
131+
<dependency>
132+
<groupId>org.jetbrains.kotlin</groupId>
133+
<artifactId>kotlin-stdlib</artifactId>
134+
<version>${kotlin}</version>
135+
<optional>true</optional>
136+
</dependency>
137+
<dependency>
138+
<groupId>org.jetbrains.kotlin</groupId>
139+
<artifactId>kotlin-reflect</artifactId>
140+
<version>${kotlin}</version>
141+
<optional>true</optional>
142+
</dependency>
143+
<dependency>
144+
<groupId>org.jetbrains.kotlinx</groupId>
145+
<artifactId>kotlinx-coroutines-core</artifactId>
146+
<version>${coroutines.version}</version>
147+
<optional>true</optional>
148+
</dependency>
149+
<dependency>
150+
<groupId>org.jetbrains.kotlinx</groupId>
151+
<artifactId>kotlinx-coroutines-reactor</artifactId>
152+
<version>${coroutines.version}</version>
153+
<optional>true</optional>
154+
</dependency>
155+
129156
<dependency>
130157
<groupId>org.assertj</groupId>
131158
<artifactId>assertj-core</artifactId>
@@ -198,6 +225,13 @@
198225
<scope>test</scope>
199226
</dependency>
200227

228+
<dependency>
229+
<groupId>io.mockk</groupId>
230+
<artifactId>mockk</artifactId>
231+
<version>1.9.1</version>
232+
<scope>test</scope>
233+
</dependency>
234+
201235
</dependencies>
202236

203237
<build>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2018-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.function
17+
18+
import kotlinx.coroutines.reactive.awaitFirstOrNull
19+
20+
/**
21+
* Coroutines variant of [DatabaseClient.GenericExecuteSpec.then].
22+
*
23+
* @author Sebastien Deleuze
24+
*/
25+
suspend fun DatabaseClient.GenericExecuteSpec.await() {
26+
then().awaitFirstOrNull()
27+
}
28+
29+
/**
30+
* Extension for [DatabaseClient.GenericExecuteSpec.as] providing a
31+
* `asType<Foo>()` variant.
32+
*
33+
* @author Sebastien Deleuze
34+
*/
35+
inline fun <reified T : Any> DatabaseClient.GenericExecuteSpec.asType(): DatabaseClient.TypedExecuteSpec<T>
36+
= `as`(T::class.java)
37+
38+
/**
39+
* Extension for [DatabaseClient.GenericSelectSpec.as] providing a
40+
* `asType<Foo>()` variant.
41+
*
42+
* @author Sebastien Deleuze
43+
*/
44+
inline fun <reified T : Any> DatabaseClient.GenericSelectSpec.asType(): DatabaseClient.TypedSelectSpec<T>
45+
= `as`(T::class.java)
46+
47+
/**
48+
* Coroutines variant of [DatabaseClient.TypedExecuteSpec.then].
49+
*
50+
* @author Sebastien Deleuze
51+
*/
52+
suspend fun <T> DatabaseClient.TypedExecuteSpec<T>.await() {
53+
then().awaitFirstOrNull()
54+
}
55+
56+
/**
57+
* Extension for [DatabaseClient.TypedExecuteSpec.as] providing a
58+
* `asType<Foo>()` variant.
59+
*
60+
* @author Sebastien Deleuze
61+
*/
62+
inline fun <reified T : Any> DatabaseClient.TypedExecuteSpec<T>.asType(): DatabaseClient.TypedExecuteSpec<T>
63+
= `as`(T::class.java)
64+
65+
/**
66+
* Coroutines variant of [DatabaseClient.InsertSpec.then].
67+
*
68+
* @author Sebastien Deleuze
69+
*/
70+
suspend fun <T> DatabaseClient.InsertSpec<T>.await() {
71+
then().awaitFirstOrNull()
72+
}
73+
74+
/**
75+
* Extension for [DatabaseClient.InsertIntoSpec.into] providing a
76+
* `into<Foo>()` variant.
77+
*
78+
* @author Sebastien Deleuze
79+
*/
80+
inline fun <reified T : Any> DatabaseClient.InsertIntoSpec.into(): DatabaseClient.TypedInsertSpec<T>
81+
= into(T::class.java)
82+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2018-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.function
17+
18+
import kotlinx.coroutines.reactive.awaitFirstOrNull
19+
20+
/**
21+
* Coroutines variant of [RowsFetchSpec.one].
22+
*
23+
* @author Sebastien Deleuze
24+
*/
25+
suspend fun <T> RowsFetchSpec<T>.awaitOne(): T?
26+
= one().awaitFirstOrNull()
27+
28+
/**
29+
* Coroutines variant of [RowsFetchSpec.first].
30+
*
31+
* @author Sebastien Deleuze
32+
*/
33+
suspend fun <T> RowsFetchSpec<T>.awaitFirst(): T?
34+
= first().awaitFirstOrNull()
35+
36+
// TODO Coroutines variant of [RowsFetchSpec.all], depends on [kotlinx.coroutines#254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
37+
// suspend fun <T> RowsFetchSpec<T>.awaitAll() = all()...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright 2018-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.function
17+
18+
import io.mockk.every
19+
import io.mockk.mockk
20+
import io.mockk.verify
21+
import kotlinx.coroutines.runBlocking
22+
import org.junit.Assert.assertEquals
23+
import org.junit.Test
24+
import reactor.core.publisher.Mono
25+
26+
class DatabaseClientExtensionsTests {
27+
28+
@Test
29+
fun genericExecuteSpecAwait() {
30+
val spec = mockk<DatabaseClient.GenericExecuteSpec>()
31+
every { spec.then() } returns Mono.empty()
32+
runBlocking {
33+
spec.await()
34+
}
35+
verify {
36+
spec.then()
37+
}
38+
}
39+
40+
@Test
41+
fun genericExecuteSpecAsType() {
42+
val genericSpec = mockk<DatabaseClient.GenericExecuteSpec>()
43+
val typedSpec: DatabaseClient.TypedExecuteSpec<String> = mockk()
44+
every { genericSpec.`as`(String::class.java) } returns typedSpec
45+
runBlocking {
46+
assertEquals(typedSpec, genericSpec.asType<String>())
47+
}
48+
verify {
49+
genericSpec.`as`(String::class.java)
50+
}
51+
}
52+
53+
@Test
54+
fun genericSelectSpecAsType() {
55+
val genericSpec = mockk<DatabaseClient.GenericSelectSpec>()
56+
val typedSpec: DatabaseClient.TypedSelectSpec<String> = mockk()
57+
every { genericSpec.`as`(String::class.java) } returns typedSpec
58+
runBlocking {
59+
assertEquals(typedSpec, genericSpec.asType<String>())
60+
}
61+
verify {
62+
genericSpec.`as`(String::class.java)
63+
}
64+
}
65+
66+
@Test
67+
fun typedExecuteSpecAwait() {
68+
val spec = mockk<DatabaseClient.TypedExecuteSpec<String>>()
69+
every { spec.then() } returns Mono.empty()
70+
runBlocking {
71+
spec.await()
72+
}
73+
verify {
74+
spec.then()
75+
}
76+
}
77+
78+
@Test
79+
fun typedExecuteSpecAsType() {
80+
val spec: DatabaseClient.TypedExecuteSpec<String> = mockk()
81+
every { spec.`as`(String::class.java) } returns spec
82+
runBlocking {
83+
assertEquals(spec, spec.asType())
84+
}
85+
verify {
86+
spec.`as`(String::class.java)
87+
}
88+
}
89+
90+
@Test
91+
fun insertSpecAwait() {
92+
val spec = mockk<DatabaseClient.InsertSpec<String>>()
93+
every { spec.then() } returns Mono.empty()
94+
runBlocking {
95+
spec.await()
96+
}
97+
verify {
98+
spec.then()
99+
}
100+
}
101+
102+
@Test
103+
fun insertIntoSpecInto() {
104+
val spec = mockk<DatabaseClient.InsertIntoSpec>()
105+
val typedSpec: DatabaseClient.TypedInsertSpec<String> = mockk()
106+
every { spec.into(String::class.java) } returns typedSpec
107+
runBlocking {
108+
assertEquals(typedSpec, spec.into<String>())
109+
}
110+
verify {
111+
spec.into(String::class.java)
112+
}
113+
}
114+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2018-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.function
17+
18+
import io.mockk.every
19+
import io.mockk.mockk
20+
import io.mockk.verify
21+
import kotlinx.coroutines.runBlocking
22+
import org.junit.Assert.assertEquals
23+
import org.junit.Test
24+
import reactor.core.publisher.Mono
25+
26+
class RowsFetchSpecExtensionsTests {
27+
28+
@Test
29+
fun awaitOne() {
30+
val spec = mockk<RowsFetchSpec<String>>()
31+
every { spec.one() } returns Mono.just("foo")
32+
runBlocking {
33+
assertEquals("foo", spec.awaitOne())
34+
}
35+
verify {
36+
spec.one()
37+
}
38+
}
39+
40+
@Test
41+
fun awaitFirst() {
42+
val spec = mockk<RowsFetchSpec<String>>()
43+
every { spec.first() } returns Mono.just("foo")
44+
runBlocking {
45+
assertEquals("foo", spec.awaitFirst())
46+
}
47+
verify {
48+
spec.first()
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)