Skip to content

Commit 32fcb61

Browse files
committed
chore: add Smallrye Mutiny support
1 parent 82fd3ca commit 32fcb61

File tree

7 files changed

+310
-1
lines changed

7 files changed

+310
-1
lines changed

pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,14 @@
148148
<optional>true</optional>
149149
</dependency>
150150

151+
<!-- SmallRye Mutiny, see:https://smallrye.io/smallrye-mutiny/ -->
152+
<dependency>
153+
<groupId>io.smallrye.reactive</groupId>
154+
<artifactId>mutiny</artifactId>
155+
<version>${smallrye-mutiny}</version>
156+
<optional>true</optional>
157+
</dependency>
158+
151159
<!-- Querydsl -->
152160

153161
<dependency>
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/*
2+
* Copyright 2020-2021 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+
* https://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.repository.reactive;
17+
18+
import io.smallrye.mutiny.Multi;
19+
import io.smallrye.mutiny.Uni;
20+
import io.smallrye.mutiny.operators.uni.UniNever;
21+
import org.springframework.data.repository.NoRepositoryBean;
22+
import org.springframework.data.repository.Repository;
23+
24+
/**
25+
* Interface for generic CRUD operations on a repository for a specific type. This repository follows reactive paradigms
26+
* and uses SmallRye Mutiny types, see:https://smallrye.io/smallrye-mutiny/.
27+
*
28+
* @author Hantsy Bai
29+
* @since 2.6
30+
* @see io.smallrye.mutiny.Uni
31+
* @see io.smallrye.mutiny.Multi
32+
*/
33+
@NoRepositoryBean
34+
public interface MutinyCrudRepository<T, ID> extends Repository<T, ID> {
35+
36+
/**
37+
* Saves a given entity. Use the returned instance for further operations as the save operation might have changed the
38+
* entity instance completely.
39+
*
40+
* @param entity must not be {@literal null}.
41+
* @return {@link Uni} emitting the saved entity.
42+
* @throws IllegalArgumentException in case the given {@literal entity} is {@literal null}.
43+
*/
44+
<S extends T> Uni<S> save(S entity);
45+
46+
/**
47+
* Saves all given entities.
48+
*
49+
* @param entities must not be {@literal null}.
50+
* @return {@link Multi} emitting the saved entities.
51+
* @throws IllegalArgumentException in case the given {@link Iterable entities} or one of its entities is
52+
* {@literal null}.
53+
*/
54+
<S extends T> Multi<S> saveAll(Iterable<S> entities);
55+
56+
/**
57+
* Saves all given entities.
58+
*
59+
* @param entityStream must not be {@literal null}.
60+
* @return {@link Multi} emitting the saved entities.
61+
* @throws IllegalArgumentException in case the given {@link Multi entityStream} is {@literal null}.
62+
*/
63+
<S extends T> Multi<S> saveAll(Multi<S> entityStream);
64+
65+
/**
66+
* Retrieves an entity by its id.
67+
*
68+
* @param id must not be {@literal null}.
69+
* @return {@link Uni} emitting the entity with the given id or {@link UniNever} if none found.
70+
* @throws IllegalArgumentException in case the given {@literal id} is {@literal null}.
71+
*/
72+
Uni<T> findById(ID id);
73+
74+
/**
75+
* Retrieves an entity by its id supplied by a {@link Uni}.
76+
*
77+
* @param id must not be {@literal null}. Uses the first emitted element to perform the find-query.
78+
* @return {@link Uni} emitting the entity with the given id or {@link UniNever} if none found.
79+
* @throws IllegalArgumentException in case the given {@link Uni id} is {@literal null}.
80+
*/
81+
Uni<T> findById(Uni<ID> id);
82+
83+
/**
84+
* Returns whether an entity with the given {@literal id} exists.
85+
*
86+
* @param id must not be {@literal null}.
87+
* @return {@link Uni} emitting {@literal true} if an entity with the given id exists, {@literal false} otherwise.
88+
* @throws IllegalArgumentException in case the given {@literal id} is {@literal null}.
89+
*/
90+
Uni<Boolean> existsById(ID id);
91+
92+
/**
93+
* Returns whether an entity with the given id, supplied by a {@link Uni}, exists.
94+
*
95+
* @param id must not be {@literal null}.
96+
* @return {@link Uni} emitting {@literal true} if an entity with the given id exists, {@literal false} otherwise.
97+
* @throws IllegalArgumentException in case the given {@link Uni id} is {@literal null}.
98+
*/
99+
Uni<Boolean> existsById(Uni<ID> id);
100+
101+
/**
102+
* Returns all instances of the type.
103+
*
104+
* @return {@link Multi} emitting all entities.
105+
*/
106+
Multi<T> findAll();
107+
108+
/**
109+
* Returns all instances of the type {@code T} with the given IDs.
110+
* <p>
111+
* If some or all ids are not found, no entities are returned for these IDs.
112+
* <p>
113+
* Note that the order of elements in the result is not guaranteed.
114+
*
115+
* @param ids must not be {@literal null} nor contain any {@literal null} values.
116+
* @return {@link Multi} emitting the found entities. The size can be equal or less than the number of given
117+
* {@literal ids}.
118+
* @throws IllegalArgumentException in case the given {@link Iterable ids} or one of its items is {@literal null}.
119+
*/
120+
Multi<T> findAllById(Iterable<ID> ids);
121+
122+
/**
123+
* Returns all instances of the type {@code T} with the given IDs supplied by a {@link Multi}.
124+
* <p>
125+
* If some or all ids are not found, no entities are returned for these IDs.
126+
* <p>
127+
* Note that the order of elements in the result is not guaranteed.
128+
*
129+
* @param idStream must not be {@literal null}.
130+
* @return {@link Multi} emitting the found entities.
131+
* @throws IllegalArgumentException in case the given {@link Multi idStream} is {@literal null}.
132+
*/
133+
Multi<T> findAllById(Multi<ID> idStream);
134+
135+
/**
136+
* Returns the number of entities available.
137+
*
138+
* @return {@link Uni} emitting the number of entities.
139+
*/
140+
Uni<Long> count();
141+
142+
/**
143+
* Deletes the entity with the given id.
144+
*
145+
* @param id must not be {@literal null}.
146+
* @return {@link Uni} signaling when operation has completed.
147+
* @throws IllegalArgumentException in case the given {@literal id} is {@literal null}.
148+
*/
149+
Uni<Void> deleteById(ID id);
150+
151+
/**
152+
* Deletes a given entity.
153+
*
154+
* @param entity must not be {@literal null}.
155+
* @return {@link Uni} signaling when operation has completed.
156+
* @throws IllegalArgumentException in case the given entity is {@literal null}.
157+
*/
158+
Uni<Void> delete(T entity);
159+
160+
/**
161+
* Deletes all instances of the type {@code T} with the given IDs.
162+
*
163+
* @param ids must not be {@literal null}.
164+
* @return {@link Uni} signaling when operation has completed.
165+
* @throws IllegalArgumentException in case the given {@literal ids} or one of its elements is {@literal null}.
166+
* {@literal null}.
167+
* @since 2.5
168+
*/
169+
Uni<Void> deleteAllById(Iterable<? extends ID> ids);
170+
171+
/**
172+
* Deletes the given entities.
173+
*
174+
* @param entities must not be {@literal null}.
175+
* @return {@link Uni} signaling when operation has completed.
176+
* @throws IllegalArgumentException in case the given {@link Iterable entities} or one of its entities is
177+
* {@literal null}.
178+
*/
179+
Uni<Void> deleteAll(Iterable<? extends T> entities);
180+
181+
/**
182+
* Deletes the given entities supplied by a {@link Multi}.
183+
*
184+
* @param entityStream must not be {@literal null}.
185+
* @return {@link Uni} signaling when operation has completed.
186+
* @throws IllegalArgumentException in case the given {@link Multi entityStream} is {@literal null}.
187+
*/
188+
Uni<Void> deleteAll(Multi<? extends T> entityStream);
189+
190+
/**
191+
* Deletes all entities managed by the repository.
192+
*
193+
* @return {@link Uni} signaling when operation has completed.
194+
*/
195+
Uni<Void> deleteAll();
196+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2020-2021 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+
* https://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.repository.reactive;
17+
18+
import io.smallrye.mutiny.Multi;
19+
import org.springframework.data.domain.Sort;
20+
import org.springframework.data.repository.NoRepositoryBean;
21+
22+
/**
23+
* Extension of {@link MutinyCrudRepository} to provide additional methods to retrieve entities using the sorting
24+
* abstraction.
25+
*
26+
* @author Hantsy Bai
27+
* @since 2.6
28+
* @see Sort
29+
* @see Multi
30+
*/
31+
@NoRepositoryBean
32+
public interface MutinySortingRepository<T, ID> extends MutinyCrudRepository<T, ID> {
33+
34+
/**
35+
* Returns all entities sorted by the given options.
36+
*
37+
* @param sort must not be {@literal null}.
38+
* @return all entities sorted by the given options.
39+
*/
40+
Multi<T> findAll(Sort sort);
41+
}

src/main/java/org/springframework/data/repository/util/ReactiveWrapperConverters.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import io.reactivex.Flowable;
1919
import io.reactivex.Maybe;
20+
import io.smallrye.mutiny.Multi;
21+
import io.smallrye.mutiny.Uni;
2022
import kotlinx.coroutines.flow.Flow;
2123
import kotlinx.coroutines.flow.FlowKt;
2224
import kotlinx.coroutines.reactive.ReactiveFlowKt;
@@ -104,6 +106,11 @@ public abstract class ReactiveWrapperConverters {
104106
REACTIVE_WRAPPERS.add(FlowWrapper.INSTANCE);
105107
}
106108

109+
if (ReactiveWrappers.isAvailable(ReactiveLibrary.MUTINY)) {
110+
REACTIVE_WRAPPERS.add(UniWrapper.INSTANCE);
111+
REACTIVE_WRAPPERS.add(MultiWrapper.INSTANCE);
112+
}
113+
107114
registerConvertersIn(GENERIC_CONVERSION_SERVICE);
108115
}
109116

@@ -527,6 +534,43 @@ public io.reactivex.rxjava3.core.Flowable<?> map(Object wrapper, Function<Object
527534
}
528535
}
529536

537+
/**
538+
* Wrapper for SmallRye Mutiny's {@link io.smallrye.mutiny.Uni}.
539+
*/
540+
private enum UniWrapper implements ReactiveTypeWrapper<io.smallrye.mutiny.Uni<?>> {
541+
542+
INSTANCE;
543+
544+
@Override
545+
public Class<? super io.smallrye.mutiny.Uni<?>> getWrapperClass() {
546+
return io.smallrye.mutiny.Uni.class;
547+
}
548+
549+
@Override
550+
public io.smallrye.mutiny.Uni<?> map(Object wrapper, Function<Object, Object> function) {
551+
return ((io.smallrye.mutiny.Uni<?>) wrapper).map(function::apply);
552+
}
553+
}
554+
555+
/**
556+
* Wrapper for SmallRye Mutiny's {@link io.smallrye.mutiny.Multi}.
557+
*/
558+
private enum MultiWrapper implements ReactiveTypeWrapper<io.smallrye.mutiny.Multi<?>> {
559+
560+
INSTANCE;
561+
562+
@Override
563+
public Class<? super io.smallrye.mutiny.Multi<?>> getWrapperClass() {
564+
return io.smallrye.mutiny.Multi.class;
565+
}
566+
567+
@Override
568+
public io.smallrye.mutiny.Multi<?> map(Object wrapper, Function<Object, Object> function) {
569+
return ((io.smallrye.mutiny.Multi<?>) wrapper).map(function::apply);
570+
}
571+
}
572+
573+
530574
// -------------------------------------------------------------------------
531575
// ReactiveStreams converters
532576
// -------------------------------------------------------------------------

src/main/java/org/springframework/data/repository/util/ReactiveWrappers.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ public abstract class ReactiveWrappers {
8383
private static final boolean KOTLIN_COROUTINES_PRESENT = ClassUtils.isPresent("kotlinx.coroutines.reactor.MonoKt",
8484
ReactiveWrappers.class.getClassLoader());
8585

86+
private static final boolean MUTINY_PRESENT = ClassUtils.isPresent("io.smallrye.mutiny.Multi",
87+
ReactiveWrappers.class.getClassLoader());
88+
8689
private ReactiveWrappers() {}
8790

8891
/**
@@ -104,7 +107,7 @@ public enum ReactiveLibrary {
104107
* @deprecated since 2.6, use RxJava 3 instead. To be removed with Spring Data 3.0.
105108
*/
106109
@Deprecated
107-
RXJAVA2, RXJAVA3, KOTLIN_COROUTINES;
110+
RXJAVA2, RXJAVA3, KOTLIN_COROUTINES, MUTINY;
108111
}
109112

110113
/**
@@ -138,6 +141,8 @@ public static boolean isAvailable(ReactiveLibrary reactiveLibrary) {
138141
return RXJAVA3_PRESENT;
139142
case KOTLIN_COROUTINES:
140143
return PROJECT_REACTOR_PRESENT && KOTLIN_COROUTINES_PRESENT;
144+
case MUTINY:
145+
return MUTINY_PRESENT;
141146
default:
142147
throw new IllegalArgumentException(String.format("Reactive library %s not supported", reactiveLibrary));
143148
}

src/test/java/org/springframework/data/repository/util/ReactiveWrapperConvertersUnitTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import static org.assertj.core.api.AssertionsForClassTypes.*;
1919

2020
import io.reactivex.Maybe;
21+
import io.smallrye.mutiny.Multi;
22+
import io.smallrye.mutiny.Uni;
2123
import kotlinx.coroutines.flow.Flow;
2224
import kotlinx.coroutines.flow.FlowKt;
2325
import kotlinx.coroutines.reactive.ReactiveFlowKt;
@@ -82,6 +84,13 @@ void shouldSupportKotlinFlow() {
8284
assertThat(ReactiveWrapperConverters.supports(io.reactivex.rxjava3.core.Completable.class)).isTrue();
8385
}
8486

87+
@Test
88+
void shouldSupportMutinyTypes() {
89+
90+
assertThat(ReactiveWrapperConverters.supports(Uni.class)).isTrue();
91+
assertThat(ReactiveWrapperConverters.supports(Multi.class)).isTrue();
92+
}
93+
8594
@Test // DATACMNS-836
8695
void toWrapperShouldCastMonoToMono() {
8796

src/test/java/org/springframework/data/repository/util/ReactiveWrappersUnitTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ void isSingleLikeShouldReportCorrectNoTypes() {
5555
assertThat(ReactiveWrappers.isNoValueType(io.reactivex.rxjava3.core.Maybe.class)).isFalse();
5656
assertThat(ReactiveWrappers.isNoValueType(io.reactivex.rxjava3.core.Flowable.class)).isFalse();
5757
assertThat(ReactiveWrappers.isNoValueType(io.reactivex.rxjava3.core.Observable.class)).isFalse();
58+
assertThat(ReactiveWrappers.isNoValueType(io.smallrye.mutiny.Uni.class)).isFalse();
59+
assertThat(ReactiveWrappers.isNoValueType(io.smallrye.mutiny.Multi.class)).isFalse();
5860
}
5961

6062
@Test // DATACMNS-836, DATACMNS-1653, DATACMNS-1753
@@ -77,6 +79,8 @@ void isSingleLikeShouldReportCorrectSingleTypes() {
7779
assertThat(ReactiveWrappers.isSingleValueType(io.reactivex.rxjava3.core.Maybe.class)).isTrue();
7880
assertThat(ReactiveWrappers.isSingleValueType(io.reactivex.rxjava3.core.Flowable.class)).isFalse();
7981
assertThat(ReactiveWrappers.isSingleValueType(io.reactivex.rxjava3.core.Observable.class)).isFalse();
82+
assertThat(ReactiveWrappers.isSingleValueType(io.smallrye.mutiny.Uni.class)).isTrue();
83+
assertThat(ReactiveWrappers.isSingleValueType(io.smallrye.mutiny.Multi.class)).isFalse();
8084
}
8185

8286
@Test // DATACMNS-836, DATACMNS-1653, DATACMNS-1753
@@ -97,5 +101,7 @@ void isCollectionLikeShouldReportCorrectCollectionTypes() {
97101
assertThat(ReactiveWrappers.isSingleValueType(io.reactivex.rxjava3.core.Completable.class)).isFalse();
98102
assertThat(ReactiveWrappers.isMultiValueType(io.reactivex.rxjava3.core.Flowable.class)).isTrue();
99103
assertThat(ReactiveWrappers.isMultiValueType(io.reactivex.rxjava3.core.Observable.class)).isTrue();
104+
assertThat(ReactiveWrappers.isMultiValueType(io.smallrye.mutiny.Uni.class)).isFalse();
105+
assertThat(ReactiveWrappers.isMultiValueType(io.smallrye.mutiny.Multi.class)).isTrue();
100106
}
101107
}

0 commit comments

Comments
 (0)