Skip to content

Commit be599d4

Browse files
committed
Fix ClassCastException when projections are used with ReactorFacet.
`ReactorFetcherDelegate` did not implement certain interfaces (eg. `ReactorFetcher.DistinctLimitOffset`). Related: #1456
1 parent c98038e commit be599d4

6 files changed

Lines changed: 94 additions & 8 deletions

File tree

criteria/reactor/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@
7373
<type>test-jar</type>
7474
<scope>test</scope>
7575
</dependency>
76+
<dependency>
77+
<groupId>org.immutables</groupId>
78+
<artifactId>criteria-inmemory</artifactId>
79+
<version>${project.version}</version>
80+
<scope>test</scope>
81+
</dependency>
7682
<dependency>
7783
<groupId>io.projectreactor</groupId>
7884
<artifactId>reactor-test</artifactId>

criteria/reactor/src/org/immutables/criteria/reactor/ReactorFetcherDelegate.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,43 @@
1717
package org.immutables.criteria.reactor;
1818

1919
import org.immutables.criteria.backend.Backend;
20+
import org.immutables.criteria.expression.ImmutableQuery;
2021
import org.immutables.criteria.expression.Query;
2122
import org.immutables.criteria.repository.reactive.ReactiveFetcher;
2223
import reactor.core.publisher.Flux;
2324
import reactor.core.publisher.Mono;
2425

2526
import java.util.Objects;
27+
import java.util.function.UnaryOperator;
2628

27-
class ReactorFetcherDelegate<T> implements ReactorFetcher<T> {
29+
class ReactorFetcherDelegate<T> implements ReactorFetcher<T>, ReactorFetcher.DistinctLimitOffset<T> {
2830

2931
private final ReactiveFetcher<T> delegate;
3032

3133
private ReactorFetcherDelegate(ReactiveFetcher<T> delegate) {
3234
this.delegate = Objects.requireNonNull(delegate, "delegate");
3335
}
3436

37+
private ReactorFetcherDelegate<T> changeQuery(UnaryOperator<Query> fn) {
38+
return new ReactorFetcherDelegate<>(delegate.changeQuery(fn));
39+
}
40+
41+
42+
@Override
43+
public LimitOffset<T> distinct() {
44+
return changeQuery(query -> ImmutableQuery.copyOf(query).withDistinct(true));
45+
}
46+
47+
@Override
48+
public Offset<T> limit(long limit) {
49+
return changeQuery(query -> ImmutableQuery.copyOf(query).withLimit(limit));
50+
}
51+
52+
@Override
53+
public ReactorFetcherDelegate<T> offset(long offset) {
54+
return changeQuery(query -> ImmutableQuery.copyOf(query).withOffset(offset));
55+
}
56+
3557
@Override
3658
public Flux<T> fetch() {
3759
return Flux.from(delegate.fetch());

criteria/reactor/test/org/immutables/criteria/reactor/ReactorTest.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.immutables.criteria.reactor;
1818

1919
import org.immutables.criteria.Criteria;
20+
import org.immutables.criteria.inmemory.InMemoryBackend;
2021
import org.immutables.criteria.repository.FakeBackend;
2122
import org.immutables.value.Value;
2223
import org.junit.jupiter.api.Test;
@@ -33,10 +34,22 @@ void empty() {
3334

3435
@Test
3536
void single() {
36-
ReactorModelRepository repo = new ReactorModelRepository(new FakeBackend(Flux.just(ImmutableReactorModel.builder().build())));
37+
ReactorModelRepository repo = new ReactorModelRepository(new FakeBackend(Flux.just(ImmutableReactorModel.builder().id("id1").build())));
3738
StepVerifier.create(repo.findAll().fetch()).thenRequest(1).expectNextCount(1).expectComplete().verify();
3839
}
3940

41+
/**
42+
* Validate the projections work with different types of facets (see {@link org.immutables.criteria.repository.Facet}).
43+
*/
44+
@Test
45+
void projection() {
46+
// need in-memory backend because of projections. FakeBackend does not support projections.
47+
InMemoryBackend backend = new InMemoryBackend();
48+
ReactorModelRepository repo = new ReactorModelRepository(backend);
49+
repo.insert(ImmutableReactorModel.builder().id("id1").build()).block();
50+
StepVerifier.create(repo.findAll().select(ReactorModelCriteria.reactorModel.id).limit(1).offset(0).fetch()).thenRequest(1).expectNext("id1").expectComplete().verify();
51+
}
52+
4053
@Test
4154
void error() {
4255
ReactorModelRepository repo = new ReactorModelRepository(new FakeBackend(Flux.error(new RuntimeException("boom"))));
@@ -46,5 +59,8 @@ void error() {
4659
@Value.Immutable
4760
@Criteria
4861
@Criteria.Repository(facets = {ReactorReadable.class, ReactorWritable.class, ReactorWatchable.class})
49-
interface ReactorModel {}
50-
}
62+
interface ReactorModel {
63+
@Criteria.Id
64+
String id();
65+
}
66+
}

criteria/rxjava2/src/org/immutables/criteria/repository/rxjava2/RxJavaFetcherDelegate.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020
import io.reactivex.Maybe;
2121
import io.reactivex.Single;
2222
import org.immutables.criteria.backend.Backend;
23+
import org.immutables.criteria.expression.ImmutableQuery;
2324
import org.immutables.criteria.expression.Query;
2425
import org.immutables.criteria.repository.reactive.ReactiveFetcher;
2526

2627
import java.util.Objects;
28+
import java.util.function.UnaryOperator;
2729

28-
class RxJavaFetcherDelegate<T> implements RxJavaFetcher<T> {
30+
class RxJavaFetcherDelegate<T> implements RxJavaFetcher<T>, RxJavaFetcher.DistinctLimitOffset<T> {
2931

3032
private final ReactiveFetcher<T> delegate;
3133

@@ -65,4 +67,24 @@ static <T> RxJavaFetcherDelegate<T> fromReactive(ReactiveFetcher<T> delegate) {
6567
static <T> RxJavaFetcherDelegate<T> of(Query query, Backend.Session session) {
6668
return fromReactive(ReactiveFetcher.of(query, session));
6769
}
70+
71+
private RxJavaFetcherDelegate<T> changeQuery(UnaryOperator<Query> fn) {
72+
return new RxJavaFetcherDelegate<>(delegate.changeQuery(fn));
73+
}
74+
75+
76+
@Override
77+
public LimitOffset<T> distinct() {
78+
return changeQuery(query -> ImmutableQuery.copyOf(query).withDistinct(true));
79+
}
80+
81+
@Override
82+
public Offset<T> limit(long limit) {
83+
return changeQuery(query -> ImmutableQuery.copyOf(query).withLimit(limit));
84+
}
85+
86+
@Override
87+
public RxJavaFetcher<T> offset(long offset) {
88+
return changeQuery(query -> ImmutableQuery.copyOf(query).withOffset(offset));
89+
}
6890
}

criteria/rxjava2/src/org/immutables/criteria/repository/rxjava2/RxJavaReader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ protected RxJavaReader<T> newReader(Query query) {
5555
return new RxJavaReader<>(query, session);
5656
}
5757

58-
public <T1> RxJavaMapper1<T1> select(Projection<T1> proj1) {
58+
public <T1> RxJavaMapper1.DistinctLimitOffset<T1> select(Projection<T1> proj1) {
5959
Query newQuery = this.query.addProjections(Matchers.toExpression(proj1));
6060
return new RxJavaMappers.Mapper1<T1>(newQuery, session);
6161
}

criteria/rxjava2/test/org/immutables/criteria/repository/rxjava2/RxJavaTest.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,27 @@ void empty() {
3434

3535
@Test
3636
void single() {
37-
RxJavaModelRepository repo = new RxJavaModelRepository(new FakeBackend(Flowable.just(ImmutableRxJavaModel.builder().build())));
37+
RxJavaModelRepository repo = new RxJavaModelRepository(new FakeBackend(Flowable.just(ImmutableRxJavaModel.builder().id("id1").build())));
3838
repo.findAll().fetch().test().awaitDone(1, TimeUnit.SECONDS).assertValueCount(1);
3939
}
4040

41+
/**
42+
* Validate the projections work with different types of facets (see {@link org.immutables.criteria.repository.Facet}).
43+
*/
44+
@Test
45+
void projection() {
46+
// TODO: can't use InMemoryBackend because of circular dependency.
47+
RxJavaModelRepository repo = new RxJavaModelRepository(new FakeBackend(Flowable.just(ImmutableRxJavaModel.builder().id("id1").build())));
48+
repo.findAll()
49+
//.select(RxJavaModelCriteria.rxJavaModel.id) TODO: FakeBackend does not support projections
50+
.limit(1)
51+
.offset(0)
52+
.fetch()
53+
.test()
54+
.awaitDone(1, TimeUnit.SECONDS)
55+
.assertValueCount(1);
56+
}
57+
4158
@Test
4259
void error() {
4360
RxJavaModelRepository repo = new RxJavaModelRepository(new FakeBackend(Flowable.error(new RuntimeException("boom"))));
@@ -47,5 +64,8 @@ void error() {
4764
@Value.Immutable
4865
@Criteria
4966
@Criteria.Repository(facets = {RxJavaReadable.class, RxJavaWritable.class, RxJavaWatchable.class})
50-
interface RxJavaModel {}
67+
interface RxJavaModel {
68+
@Criteria.Id
69+
String id();
70+
}
5171
}

0 commit comments

Comments
 (0)