Skip to content

Commit dab10e7

Browse files
committed
Apply default ScrollSubrange correctly
The defaults are now applied before the call to ScrollSubrange#create. After is too late given that for offset positions, the direction may switch from backward to forward. Closes gh-900
1 parent 5248a32 commit dab10e7

File tree

4 files changed

+184
-97
lines changed

4 files changed

+184
-97
lines changed

spring-graphql/src/main/java/org/springframework/graphql/data/query/QueryByExampleDataFetcher.java

+28-36
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -107,15 +107,9 @@ public abstract class QueryByExampleDataFetcher<T> {
107107

108108
private final GraphQlArgumentBinder argumentBinder;
109109

110-
@Nullable
111-
private final CursorStrategy<ScrollPosition> cursorStrategy;
112-
113-
114-
QueryByExampleDataFetcher(
115-
TypeInformation<T> domainType, @Nullable CursorStrategy<ScrollPosition> cursorStrategy) {
116110

111+
QueryByExampleDataFetcher(TypeInformation<T> domainType) {
117112
this.domainType = domainType;
118-
this.cursorStrategy = cursorStrategy;
119113
this.argumentBinder = new GraphQlArgumentBinder();
120114
}
121115

@@ -174,11 +168,6 @@ protected Collection<String> buildPropertyPaths(DataFetchingFieldSelectionSet se
174168
return Collections.emptyList();
175169
}
176170

177-
protected ScrollSubrange buildScrollSubrange(DataFetchingEnvironment environment) {
178-
Assert.state(this.cursorStrategy != null, "Expected CursorStrategy");
179-
return RepositoryUtils.buildScrollSubrange(environment, this.cursorStrategy);
180-
}
181-
182171
@Override
183172
public String toString() {
184173
return getDescription();
@@ -434,7 +423,7 @@ public Builder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosition> cur
434423
/**
435424
* Configure a {@link ScrollSubrange} to use when a paginated request does
436425
* not specify a cursor and/or a count of items.
437-
* <p>By default, this is {@link OffsetScrollPosition#initial()} with a
426+
* <p>By default, this is {@link OffsetScrollPosition#offset()} with a
438427
* count of 20.
439428
* @return a new {@link Builder} instance with all previously configured
440429
* options and {@code Sort} applied
@@ -468,7 +457,7 @@ public DataFetcher<R> single() {
468457
* Build a {@link DataFetcher} to fetch many object instances.
469458
*/
470459
public DataFetcher<Iterable<R>> many() {
471-
return new ManyEntityFetcher<>(this.executor, this.domainType, this.resultType, null, this.sort);
460+
return new ManyEntityFetcher<>(this.executor, this.domainType, this.resultType, this.sort);
472461
}
473462

474463
/**
@@ -581,7 +570,7 @@ public ReactiveBuilder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosit
581570
/**
582571
* Configure a {@link ScrollSubrange} to use when a paginated request does
583572
* not specify a cursor and/or a count of items.
584-
* <p>By default, this is {@link OffsetScrollPosition#initial()} with a
573+
* <p>By default, this is {@link OffsetScrollPosition#offset()} with a
585574
* count of 20.
586575
* @return a new {@link Builder} instance with all previously configured
587576
* options and {@code Sort} applied
@@ -666,7 +655,7 @@ private static class SingleEntityFetcher<T, R>
666655
SingleEntityFetcher(
667656
QueryByExampleExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType, Sort sort) {
668657

669-
super(domainType, null);
658+
super(domainType);
670659
this.executor = executor;
671660
this.resultType = resultType;
672661
this.sort = sort;
@@ -712,10 +701,10 @@ private static class ManyEntityFetcher<T, R>
712701
private final Sort sort;
713702

714703
ManyEntityFetcher(
715-
QueryByExampleExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType,
716-
@Nullable CursorStrategy<ScrollPosition> cursorStrategy, Sort sort) {
704+
QueryByExampleExecutor<T> executor, TypeInformation<T> domainType,
705+
Class<R> resultType, Sort sort) {
717706

718-
super(domainType, cursorStrategy);
707+
super(domainType);
719708
this.executor = executor;
720709
this.resultType = resultType;
721710
this.sort = sort;
@@ -756,23 +745,24 @@ protected Iterable<R> getResult(FluentQuery.FetchableFluentQuery<R> queryToUse,
756745

757746
private static class ScrollableEntityFetcher<T, R> extends ManyEntityFetcher<T, R> {
758747

748+
private final CursorStrategy<ScrollPosition> cursorStrategy;
749+
759750
private final ScrollSubrange defaultSubrange;
760751

761752
private final ResolvableType scrollableResultType;
762753

763754
ScrollableEntityFetcher(
764755
QueryByExampleExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType,
765-
CursorStrategy<ScrollPosition> cursorStrategy,
766-
ScrollSubrange defaultSubrange,
767-
Sort sort) {
756+
CursorStrategy<ScrollPosition> cursorStrategy, ScrollSubrange defaultSubrange, Sort sort) {
768757

769-
super(executor, domainType, resultType, cursorStrategy, sort);
758+
super(executor, domainType, resultType, sort);
770759

771760
Assert.notNull(cursorStrategy, "CursorStrategy is required");
772761
Assert.notNull(defaultSubrange, "Default ScrollSubrange is required");
773762
Assert.isTrue(defaultSubrange.position().isPresent(), "Default ScrollPosition is required");
774763
Assert.isTrue(defaultSubrange.count().isPresent(), "Default scroll limit is required");
775764

765+
this.cursorStrategy = cursorStrategy;
776766
this.defaultSubrange = defaultSubrange;
777767
this.scrollableResultType = ResolvableType.forClassWithGenerics(Window.class, resultType);
778768
}
@@ -785,10 +775,10 @@ public ResolvableType getReturnType() {
785775
@SuppressWarnings("OptionalGetWithoutIsPresent")
786776
@Override
787777
protected Iterable<R> getResult(FluentQuery.FetchableFluentQuery<R> queryToUse, DataFetchingEnvironment env) {
788-
ScrollSubrange subrange = buildScrollSubrange(env);
789-
int limit = subrange.count().orElse(this.defaultSubrange.count().getAsInt());
790-
ScrollPosition position = subrange.position().orElse(this.defaultSubrange.position().get());
791-
return queryToUse.limit(limit).scroll(position);
778+
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, this.cursorStrategy, this.defaultSubrange);
779+
int count = range.count().getAsInt();
780+
ScrollPosition position = range.position().get();
781+
return queryToUse.limit(count).scroll(position);
792782
}
793783

794784
}
@@ -807,7 +797,7 @@ private static class ReactiveSingleEntityFetcher<T, R>
807797
ReactiveQueryByExampleExecutor<T> executor, TypeInformation<T> domainType,
808798
Class<R> resultType, Sort sort) {
809799

810-
super(domainType, null);
800+
super(domainType);
811801
this.executor = executor;
812802
this.resultType = resultType;
813803
this.sort = sort;
@@ -855,7 +845,7 @@ private static class ReactiveManyEntityFetcher<T, R>
855845
ReactiveQueryByExampleExecutor<T> executor, TypeInformation<T> domainType,
856846
Class<R> resultType, Sort sort) {
857847

858-
super(domainType, null);
848+
super(domainType);
859849
this.executor = executor;
860850
this.resultType = resultType;
861851
this.sort = sort;
@@ -899,6 +889,8 @@ private static class ReactiveScrollableEntityFetcher<T, R>
899889

900890
private final ResolvableType scrollableResultType;
901891

892+
private final CursorStrategy<ScrollPosition> cursorStrategy;
893+
902894
private final ScrollSubrange defaultSubrange;
903895

904896
private final Sort sort;
@@ -907,7 +899,7 @@ private static class ReactiveScrollableEntityFetcher<T, R>
907899
ReactiveQueryByExampleExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType,
908900
CursorStrategy<ScrollPosition> cursorStrategy, ScrollSubrange defaultSubrange, Sort sort) {
909901

910-
super(domainType, cursorStrategy);
902+
super(domainType);
911903

912904
Assert.notNull(cursorStrategy, "CursorStrategy is required");
913905
Assert.notNull(defaultSubrange, "Default ScrollSubrange is required");
@@ -917,6 +909,7 @@ private static class ReactiveScrollableEntityFetcher<T, R>
917909
this.executor = executor;
918910
this.resultType = resultType;
919911
this.scrollableResultType = ResolvableType.forClassWithGenerics(Iterable.class, resultType);
912+
this.cursorStrategy = cursorStrategy;
920913
this.defaultSubrange = defaultSubrange;
921914
this.sort = sort;
922915
}
@@ -943,11 +936,10 @@ public Mono<Iterable<R>> get(DataFetchingEnvironment env) throws BindException {
943936
queryToUse = queryToUse.project(buildPropertyPaths(env.getSelectionSet(), this.resultType));
944937
}
945938

946-
ScrollSubrange subrange = buildScrollSubrange(env);
947-
int limit = subrange.count().orElse(this.defaultSubrange.count().getAsInt());
948-
ScrollPosition position = subrange.position().orElse(this.defaultSubrange.position().get());
949-
950-
return queryToUse.limit(limit).scroll(position).map(Function.identity());
939+
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, this.cursorStrategy, this.defaultSubrange);
940+
int count = range.count().getAsInt();
941+
ScrollPosition position = range.position().get();
942+
return queryToUse.limit(count).scroll(position).map(Function.identity());
951943
});
952944
}
953945

0 commit comments

Comments
 (0)