diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index c85d971ad..285161bc0 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -136,6 +136,13 @@ true + + io.reactivex.rxjava3 + rxjava + ${rxjava3} + true + + diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java index 85ffd3f8e..cdbe54c6c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java @@ -35,6 +35,7 @@ import org.springframework.util.Assert; * @since 2.0 * @see org.springframework.data.repository.reactive.ReactiveSortingRepository * @see org.springframework.data.repository.reactive.RxJava2SortingRepository + * @see org.springframework.data.repository.reactive.RxJava3SortingRepository */ public class ReactiveMongoRepositoryFactoryBean, S, ID extends Serializable> extends RepositoryFactoryBeanSupport { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ConvertingReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ConvertingReactiveMongoRepositoryTests.java index 63d4b8854..cdce6e20e 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ConvertingReactiveMongoRepositoryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ConvertingReactiveMongoRepositoryTests.java @@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.*; import io.reactivex.Flowable; import io.reactivex.Maybe; import io.reactivex.observers.TestObserver; +import io.reactivex.rxjava3.subscribers.TestSubscriber; import lombok.Data; import lombok.NoArgsConstructor; import reactor.core.publisher.Flux; @@ -38,6 +39,7 @@ import org.reactivestreams.Publisher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.ImportResource; import org.springframework.data.annotation.Id; import org.springframework.data.domain.Sort; @@ -45,7 +47,6 @@ import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; import org.springframework.data.repository.reactive.ReactiveSortingRepository; import org.springframework.data.repository.reactive.RxJava2SortingRepository; -import org.springframework.stereotype.Repository; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @@ -58,7 +59,12 @@ import org.springframework.test.context.junit4.SpringRunner; @ContextConfiguration(classes = ConvertingReactiveMongoRepositoryTests.Config.class) public class ConvertingReactiveMongoRepositoryTests { - @EnableReactiveMongoRepositories(includeFilters = @Filter(value = Repository.class), + @EnableReactiveMongoRepositories( + includeFilters = { @Filter(value = ReactivePersonRepostitory.class, type = FilterType.ASSIGNABLE_TYPE), + @Filter(value = RxJava1PersonRepostitory.class, type = FilterType.ASSIGNABLE_TYPE), + @Filter(value = RxJava2PersonRepostitory.class, type = FilterType.ASSIGNABLE_TYPE), + @Filter(value = RxJava3PersonRepostitory.class, type = FilterType.ASSIGNABLE_TYPE), + @Filter(value = MixedReactivePersonRepostitory.class, type = FilterType.ASSIGNABLE_TYPE) }, considerNestedRepositories = true) @ImportResource("classpath:reactive-infrastructure.xml") static class Config {} @@ -67,6 +73,7 @@ public class ConvertingReactiveMongoRepositoryTests { @Autowired ReactivePersonRepostitory reactivePersonRepostitory; @Autowired RxJava1PersonRepostitory rxJava1PersonRepostitory; @Autowired RxJava2PersonRepostitory rxJava2PersonRepostitory; + @Autowired RxJava3PersonRepostitory rxJava3PersonRepostitory; ReactivePerson dave, oliver, carter, boyd, stefan, leroi, alicia; @@ -237,6 +244,85 @@ public class ConvertingReactiveMongoRepositoryTests { testObserver.assertValue(boyd); } + @Test // DATAMONGO-1610 + public void simpleRxJava3MethodsShouldWork() { + + TestObserver testObserver = rxJava3PersonRepostitory.existsById(dave.getId()).test(); + + testObserver.awaitTerminalEvent(); + testObserver.assertComplete(); + testObserver.assertNoErrors(); + testObserver.assertValue(true); + } + + @Test // DATAMONGO-1610 + public void existsWithSingleRxJava3IdMethodsShouldWork() { + + TestObserver testObserver = rxJava3PersonRepostitory.existsById(io.reactivex.Single.just(dave.getId())) + .test(); + + testObserver.awaitTerminalEvent(); + testObserver.assertComplete(); + testObserver.assertNoErrors(); + testObserver.assertValue(true); + } + + @Test // DATAMONGO-1610 + public void flowableRxJava3QueryMethodShouldWork() throws InterruptedException { + + TestSubscriber testSubscriber = rxJava3PersonRepostitory + .findByFirstnameAndLastname(dave.getFirstname(), dave.getLastname()).test(); + + testSubscriber.await(); + testSubscriber.assertComplete(); + testSubscriber.assertNoErrors(); + testSubscriber.assertValue(dave); + } + + @Test // DATAMONGO-1610 + public void singleProjectedRxJava3QueryMethodShouldWork() throws InterruptedException { + + io.reactivex.rxjava3.observers.TestObserver testObserver = rxJava3PersonRepostitory + .findProjectedByLastname(io.reactivex.rxjava3.core.Maybe.just(carter.getLastname())).test(); + + testObserver.await(); + testObserver.assertComplete(); + testObserver.assertNoErrors(); + + testObserver.assertValue(actual -> { + assertThat(actual.getFirstname()).isEqualTo(carter.getFirstname()); + return true; + }); + } + + @Test // DATAMONGO-1610 + public void observableProjectedRxJava3QueryMethodShouldWork() throws InterruptedException { + + io.reactivex.rxjava3.observers.TestObserver testObserver = rxJava3PersonRepostitory + .findProjectedByLastname(io.reactivex.rxjava3.core.Single.just(carter.getLastname())).test(); + + testObserver.await(); + testObserver.assertComplete(); + testObserver.assertNoErrors(); + + testObserver.assertValue(actual -> { + assertThat(actual.getFirstname()).isEqualTo(carter.getFirstname()); + return true; + }); + } + + @Test // DATAMONGO-1610 + public void maybeRxJava3QueryMethodShouldWork() throws InterruptedException { + + io.reactivex.rxjava3.observers.TestObserver testObserver = rxJava3PersonRepostitory + .findByLastname(boyd.getLastname()).test(); + + testObserver.await(); + testObserver.assertComplete(); + testObserver.assertNoErrors(); + testObserver.assertValue(boyd); + } + @Test // DATAMONGO-1444 public void mixedRepositoryShouldWork() { @@ -280,13 +366,11 @@ public class ConvertingReactiveMongoRepositoryTests { assertThat(people).contains(carter, dave); } - @Repository interface ReactivePersonRepostitory extends ReactiveSortingRepository { Publisher findByLastname(String lastname); } - @Repository interface RxJava1PersonRepostitory extends org.springframework.data.repository.Repository { Observable findByFirstnameAndLastname(String firstname, String lastname); @@ -300,7 +384,6 @@ public class ConvertingReactiveMongoRepositoryTests { Single existsById(Single id); } - @Repository interface RxJava2PersonRepostitory extends RxJava2SortingRepository { Flowable findByFirstnameAndLastname(String firstname, String lastname); @@ -312,7 +395,19 @@ public class ConvertingReactiveMongoRepositoryTests { io.reactivex.Observable findProjectedByLastname(Single lastname); } - @Repository + interface RxJava3PersonRepostitory extends RxJava2SortingRepository { + + io.reactivex.rxjava3.core.Flowable findByFirstnameAndLastname(String firstname, String lastname); + + io.reactivex.rxjava3.core.Maybe findByLastname(String lastname); + + io.reactivex.rxjava3.core.Single findProjectedByLastname( + io.reactivex.rxjava3.core.Maybe lastname); + + io.reactivex.rxjava3.core.Observable findProjectedByLastname( + io.reactivex.rxjava3.core.Single lastname); + } + interface MixedReactivePersonRepostitory extends ReactiveMongoRepository { Single findByLastname(String lastname); diff --git a/src/main/asciidoc/reference/reactive-mongo-repositories.adoc b/src/main/asciidoc/reference/reactive-mongo-repositories.adoc index c9c54cc80..e0aea3b00 100644 --- a/src/main/asciidoc/reference/reactive-mongo-repositories.adoc +++ b/src/main/asciidoc/reference/reactive-mongo-repositories.adoc @@ -16,6 +16,8 @@ Spring Data's Repository abstraction is a dynamic API, mostly defined by you and * `ReactiveSortingRepository` * `RxJava2CrudRepository` * `RxJava2SortingRepository` +* `RxJava3CrudRepository` +* `RxJava3SortingRepository` Spring Data converts reactive wrapper types behind the scenes so that you can stick to your favorite composition library.