Revise default ScrollSubrange for QueryDsl and QBE
The QueryDsl and QBE data fetcher builders now accept a default count and a function for a default position with awareness of the scroll direction. Closes gh-917
This commit is contained in:
@@ -35,6 +35,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.KeysetScrollPosition;
|
||||
import org.springframework.data.domain.OffsetScrollPosition;
|
||||
import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.data.domain.Sort;
|
||||
@@ -370,24 +371,29 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
private final CursorStrategy<ScrollPosition> cursorStrategy;
|
||||
|
||||
@Nullable
|
||||
private final ScrollSubrange defaultSubrange;
|
||||
private final Integer defaultScrollCount;
|
||||
|
||||
@Nullable
|
||||
private final Function<Boolean, ScrollPosition> defaultScrollPosition;
|
||||
|
||||
private final Sort sort;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Builder(QueryByExampleExecutor<T> executor, Class<R> domainType) {
|
||||
this(executor, TypeInformation.of((Class<T>) domainType), domainType, null, null, Sort.unsorted());
|
||||
this(executor, TypeInformation.of((Class<T>) domainType), domainType, null, null, null, Sort.unsorted());
|
||||
}
|
||||
|
||||
Builder(QueryByExampleExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType,
|
||||
@Nullable CursorStrategy<ScrollPosition> cursorStrategy, @Nullable ScrollSubrange defaultSubrange,
|
||||
@Nullable CursorStrategy<ScrollPosition> cursorStrategy,
|
||||
@Nullable Integer defaultScrollCount, @Nullable Function<Boolean, ScrollPosition> defaultScrollPosition,
|
||||
Sort sort) {
|
||||
|
||||
this.executor = executor;
|
||||
this.domainType = domainType;
|
||||
this.resultType = resultType;
|
||||
this.cursorStrategy = cursorStrategy;
|
||||
this.defaultSubrange = defaultSubrange;
|
||||
this.defaultScrollCount = defaultScrollCount;
|
||||
this.defaultScrollPosition = defaultScrollPosition;
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@@ -402,8 +408,8 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
*/
|
||||
public <P> Builder<T, P> projectAs(Class<P> projectionType) {
|
||||
Assert.notNull(projectionType, "Projection type must not be null");
|
||||
return new Builder<>(this.executor, this.domainType,
|
||||
projectionType, this.cursorStrategy, this.defaultSubrange, this.sort);
|
||||
return new Builder<>(this.executor, this.domainType, projectionType,
|
||||
this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition, this.sort);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,8 +422,26 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public Builder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosition> cursorStrategy) {
|
||||
return new Builder<>(this.executor, this.domainType, this.resultType,
|
||||
cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition, this.sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a default scroll count to use, and function to return a default
|
||||
* {@link ScrollPosition} for forward vs backward pagination.
|
||||
* <p>For offset scrolling, use {@link ScrollPosition#offset()} to scroll
|
||||
* from the beginning. Currently, it is not possible to go back from the end.
|
||||
* <p>For keyset scrolling, use {@link ScrollPosition#keyset()} to scroll
|
||||
* from the beginning, or {@link KeysetScrollPosition#reverse()} the same
|
||||
* to go back from the end.
|
||||
* <p>By default a count of 20 and {@link ScrollPosition#offset()} are used.
|
||||
* @since 1.2.5
|
||||
*/
|
||||
public Builder<T, R> defaultScrollSubrange(
|
||||
int defaultCount, Function<Boolean, ScrollPosition> defaultPosition) {
|
||||
|
||||
return new Builder<>(this.executor, this.domainType,
|
||||
this.resultType, cursorStrategy, this.defaultSubrange, this.sort);
|
||||
this.resultType, this.cursorStrategy, defaultCount, defaultPosition, this.sort);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,11 +451,16 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* count of 20.
|
||||
* @return a new {@link Builder} instance with all previously configured
|
||||
* options and {@code Sort} applied
|
||||
* @since 1.2.0
|
||||
* @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
|
||||
*/
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||
@Deprecated(since = "1.2.5", forRemoval = true)
|
||||
public Builder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubrange) {
|
||||
return new Builder<>(this.executor, this.domainType,
|
||||
this.resultType, this.cursorStrategy, defaultSubrange, this.sort);
|
||||
this.resultType, this.cursorStrategy,
|
||||
(defaultSubrange != null ? defaultSubrange.count().getAsInt() : null),
|
||||
(defaultSubrange != null ? forward -> defaultSubrange.position().get() : null),
|
||||
this.sort);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -442,8 +471,8 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
*/
|
||||
public Builder<T, R> sortBy(Sort sort) {
|
||||
Assert.notNull(sort, "Sort must not be null");
|
||||
return new Builder<>(this.executor, this.domainType,
|
||||
this.resultType, this.cursorStrategy, this.defaultSubrange, sort);
|
||||
return new Builder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition, sort);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -469,7 +498,8 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
return new ScrollableEntityFetcher<>(
|
||||
this.executor, this.domainType, this.resultType,
|
||||
(this.cursorStrategy != null ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy()),
|
||||
(this.defaultSubrange != null ? this.defaultSubrange : RepositoryUtils.defaultScrollSubrange()),
|
||||
(this.defaultScrollCount != null ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount()),
|
||||
(this.defaultScrollPosition != null ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition()),
|
||||
this.sort);
|
||||
}
|
||||
|
||||
@@ -516,25 +546,30 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
private final CursorStrategy<ScrollPosition> cursorStrategy;
|
||||
|
||||
@Nullable
|
||||
private final ScrollSubrange defaultSubrange;
|
||||
private final Integer defaultScrollCount;
|
||||
|
||||
@Nullable
|
||||
private final Function<Boolean, ScrollPosition> defaultScrollPosition;
|
||||
|
||||
private final Sort sort;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ReactiveBuilder(ReactiveQueryByExampleExecutor<T> executor, Class<R> domainType) {
|
||||
this(executor, TypeInformation.of((Class<T>) domainType), domainType, null, null, Sort.unsorted());
|
||||
this(executor, TypeInformation.of((Class<T>) domainType), domainType, null, null, null, Sort.unsorted());
|
||||
}
|
||||
|
||||
ReactiveBuilder(
|
||||
ReactiveQueryByExampleExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType,
|
||||
@Nullable CursorStrategy<ScrollPosition> cursorStrategy, @Nullable ScrollSubrange defaultSubrange,
|
||||
@Nullable CursorStrategy<ScrollPosition> cursorStrategy,
|
||||
@Nullable Integer defaultScrollCount, @Nullable Function<Boolean, ScrollPosition> defaultScrollPosition,
|
||||
Sort sort) {
|
||||
|
||||
this.executor = executor;
|
||||
this.domainType = domainType;
|
||||
this.resultType = resultType;
|
||||
this.cursorStrategy = cursorStrategy;
|
||||
this.defaultSubrange = defaultSubrange;
|
||||
this.defaultScrollCount = defaultScrollCount;
|
||||
this.defaultScrollPosition = defaultScrollPosition;
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@@ -550,7 +585,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
public <P> ReactiveBuilder<T, P> projectAs(Class<P> projectionType) {
|
||||
Assert.notNull(projectionType, "Projection type must not be null");
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType,
|
||||
projectionType, this.cursorStrategy, this.defaultSubrange, this.sort);
|
||||
projectionType, this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition, this.sort);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -563,8 +598,26 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public ReactiveBuilder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosition> cursorStrategy) {
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType, this.resultType,
|
||||
cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition, this.sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a default scroll count to use, and function to return a default
|
||||
* {@link ScrollPosition} for forward vs backward pagination.
|
||||
* <p>For offset scrolling, use {@link ScrollPosition#offset()} to scroll
|
||||
* from the beginning. Currently, it is not possible to go back from the end.
|
||||
* <p>For keyset scrolling, use {@link ScrollPosition#keyset()} to scroll
|
||||
* from the beginning, or {@link KeysetScrollPosition#reverse()} the same
|
||||
* to go back from the end.
|
||||
* <p>By default a count of 20 and {@link ScrollPosition#offset()} are used.
|
||||
* @since 1.2.5
|
||||
*/
|
||||
public ReactiveBuilder<T, R> defaultScrollSubrange(
|
||||
int defaultCount, Function<Boolean, ScrollPosition> defaultPosition) {
|
||||
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType,
|
||||
this.resultType, cursorStrategy, this.defaultSubrange, this.sort);
|
||||
this.resultType, this.cursorStrategy, defaultCount, defaultPosition, this.sort);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -574,11 +627,16 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
* count of 20.
|
||||
* @return a new {@link Builder} instance with all previously configured
|
||||
* options and {@code Sort} applied
|
||||
* @since 1.2.0
|
||||
* @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
|
||||
*/
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||
@Deprecated(since = "1.2.5", forRemoval = true)
|
||||
public ReactiveBuilder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubrange) {
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType,
|
||||
this.resultType, this.cursorStrategy, defaultSubrange, this.sort);
|
||||
this.resultType, this.cursorStrategy,
|
||||
(defaultSubrange != null ? defaultSubrange.count().getAsInt() : null),
|
||||
(defaultSubrange != null ? forward -> defaultSubrange.position().get() : null),
|
||||
this.sort);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -589,8 +647,8 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
*/
|
||||
public ReactiveBuilder<T, R> sortBy(Sort sort) {
|
||||
Assert.notNull(sort, "Sort must not be null");
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType,
|
||||
this.resultType, this.cursorStrategy, this.defaultSubrange, sort);
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition, sort);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -616,7 +674,8 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
return new ReactiveScrollableEntityFetcher<>(
|
||||
this.executor, this.domainType, this.resultType,
|
||||
(this.cursorStrategy != null ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy()),
|
||||
(this.defaultSubrange != null ? this.defaultSubrange : RepositoryUtils.defaultScrollSubrange()),
|
||||
(this.defaultScrollCount != null ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount()),
|
||||
(this.defaultScrollPosition != null ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition()),
|
||||
this.sort);
|
||||
}
|
||||
|
||||
@@ -747,23 +806,27 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
|
||||
private final CursorStrategy<ScrollPosition> cursorStrategy;
|
||||
|
||||
private final ScrollSubrange defaultSubrange;
|
||||
private final int defaultCount;
|
||||
|
||||
private final Function<Boolean, ScrollPosition> defaultPosition;
|
||||
|
||||
private final ResolvableType scrollableResultType;
|
||||
|
||||
ScrollableEntityFetcher(
|
||||
QueryByExampleExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType,
|
||||
CursorStrategy<ScrollPosition> cursorStrategy, ScrollSubrange defaultSubrange, Sort sort) {
|
||||
CursorStrategy<ScrollPosition> cursorStrategy,
|
||||
int defaultCount,
|
||||
Function<Boolean, ScrollPosition> defaultPosition,
|
||||
Sort sort) {
|
||||
|
||||
super(executor, domainType, resultType, sort);
|
||||
|
||||
Assert.notNull(cursorStrategy, "CursorStrategy is required");
|
||||
Assert.notNull(defaultSubrange, "Default ScrollSubrange is required");
|
||||
Assert.isTrue(defaultSubrange.position().isPresent(), "Default ScrollPosition is required");
|
||||
Assert.isTrue(defaultSubrange.count().isPresent(), "Default scroll limit is required");
|
||||
Assert.notNull(defaultPosition, "'defaultPosition' is required");
|
||||
|
||||
this.cursorStrategy = cursorStrategy;
|
||||
this.defaultSubrange = defaultSubrange;
|
||||
this.defaultCount = defaultCount;
|
||||
this.defaultPosition = defaultPosition;
|
||||
this.scrollableResultType = ResolvableType.forClassWithGenerics(Window.class, resultType);
|
||||
}
|
||||
|
||||
@@ -772,12 +835,12 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
return ResolvableType.forClassWithGenerics(Iterable.class, this.scrollableResultType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||
@Override
|
||||
protected Iterable<R> getResult(FluentQuery.FetchableFluentQuery<R> queryToUse, DataFetchingEnvironment env) {
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, this.cursorStrategy, this.defaultSubrange);
|
||||
int count = range.count().getAsInt();
|
||||
ScrollPosition position = range.position().get();
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, this.cursorStrategy);
|
||||
int count = range.count().orElse(this.defaultCount);
|
||||
ScrollPosition position = (range.position().isPresent() ?
|
||||
range.position().get() : this.defaultPosition.apply(range.forward()));
|
||||
return queryToUse.limit(count).scroll(position);
|
||||
}
|
||||
|
||||
@@ -891,26 +954,30 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
|
||||
private final CursorStrategy<ScrollPosition> cursorStrategy;
|
||||
|
||||
private final ScrollSubrange defaultSubrange;
|
||||
private final int defaultCount;
|
||||
|
||||
private final Function<Boolean, ScrollPosition> defaultPosition;
|
||||
|
||||
private final Sort sort;
|
||||
|
||||
ReactiveScrollableEntityFetcher(
|
||||
ReactiveQueryByExampleExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType,
|
||||
CursorStrategy<ScrollPosition> cursorStrategy, ScrollSubrange defaultSubrange, Sort sort) {
|
||||
CursorStrategy<ScrollPosition> cursorStrategy,
|
||||
int defaultCount,
|
||||
Function<Boolean, ScrollPosition> defaultPosition,
|
||||
Sort sort) {
|
||||
|
||||
super(domainType);
|
||||
|
||||
Assert.notNull(cursorStrategy, "CursorStrategy is required");
|
||||
Assert.notNull(defaultSubrange, "Default ScrollSubrange is required");
|
||||
Assert.isTrue(defaultSubrange.position().isPresent(), "Default ScrollPosition is required");
|
||||
Assert.isTrue(defaultSubrange.count().isPresent(), "Default scroll limit is required");
|
||||
Assert.notNull(defaultPosition, "'defaultPosition' is required");
|
||||
|
||||
this.executor = executor;
|
||||
this.resultType = resultType;
|
||||
this.scrollableResultType = ResolvableType.forClassWithGenerics(Iterable.class, resultType);
|
||||
this.cursorStrategy = cursorStrategy;
|
||||
this.defaultSubrange = defaultSubrange;
|
||||
this.defaultCount = defaultCount;
|
||||
this.defaultPosition = defaultPosition;
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@@ -920,7 +987,7 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "OptionalGetWithoutIsPresent"})
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Iterable<R>> get(DataFetchingEnvironment env) throws BindException {
|
||||
return this.executor.findBy(buildExample(env), query -> {
|
||||
FluentQuery.ReactiveFluentQuery<R> queryToUse = (FluentQuery.ReactiveFluentQuery<R>) query;
|
||||
@@ -936,9 +1003,10 @@ public abstract class QueryByExampleDataFetcher<T> {
|
||||
queryToUse = queryToUse.project(buildPropertyPaths(env.getSelectionSet(), this.resultType));
|
||||
}
|
||||
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, this.cursorStrategy, this.defaultSubrange);
|
||||
int count = range.count().getAsInt();
|
||||
ScrollPosition position = range.position().get();
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, this.cursorStrategy);
|
||||
int count = range.count().orElse(this.defaultCount);
|
||||
ScrollPosition position = (range.position().isPresent() ?
|
||||
range.position().get() : this.defaultPosition.apply(range.forward()));
|
||||
return queryToUse.limit(count).scroll(position).map(Function.identity());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.domain.KeysetScrollPosition;
|
||||
import org.springframework.data.domain.OffsetScrollPosition;
|
||||
import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.data.domain.Sort;
|
||||
@@ -412,7 +413,10 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
private final CursorStrategy<ScrollPosition> cursorStrategy;
|
||||
|
||||
@Nullable
|
||||
private final ScrollSubrange defaultSubrange;
|
||||
private final Integer defaultScrollCount;
|
||||
|
||||
@Nullable
|
||||
private final Function<Boolean, ScrollPosition> defaultScrollPosition;
|
||||
|
||||
private final Sort sort;
|
||||
|
||||
@@ -421,18 +425,20 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
@SuppressWarnings("unchecked")
|
||||
Builder(QuerydslPredicateExecutor<T> executor, Class<R> domainType) {
|
||||
this(executor, TypeInformation.of((Class<T>) domainType),
|
||||
domainType, null, null, Sort.unsorted(), NO_OP_BINDER_CUSTOMIZER);
|
||||
domainType, null, null, null, Sort.unsorted(), NO_OP_BINDER_CUSTOMIZER);
|
||||
}
|
||||
|
||||
Builder(QuerydslPredicateExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType,
|
||||
@Nullable CursorStrategy<ScrollPosition> cursorStrategy, @Nullable ScrollSubrange defaultSubrange,
|
||||
@Nullable CursorStrategy<ScrollPosition> cursorStrategy,
|
||||
@Nullable Integer defaultScrollCount, @Nullable Function<Boolean, ScrollPosition> defaultScrollPosition,
|
||||
Sort sort, QuerydslBinderCustomizer<? extends EntityPath<T>> customizer) {
|
||||
|
||||
this.executor = executor;
|
||||
this.domainType = domainType;
|
||||
this.resultType = resultType;
|
||||
this.cursorStrategy = cursorStrategy;
|
||||
this.defaultSubrange = defaultSubrange;
|
||||
this.defaultScrollCount = defaultScrollCount;
|
||||
this.defaultScrollPosition = defaultScrollPosition;
|
||||
this.sort = sort;
|
||||
this.customizer = customizer;
|
||||
}
|
||||
@@ -449,7 +455,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
public <P> Builder<T, P> projectAs(Class<P> projectionType) {
|
||||
Assert.notNull(projectionType, "Projection type must not be null");
|
||||
return new Builder<>(this.executor, this.domainType, projectionType,
|
||||
this.cursorStrategy, this.defaultSubrange, this.sort, this.customizer);
|
||||
this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition,
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -463,21 +470,43 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
*/
|
||||
public Builder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosition> cursorStrategy) {
|
||||
return new Builder<>(this.executor, this.domainType, this.resultType,
|
||||
cursorStrategy, this.defaultSubrange, this.sort, this.customizer);
|
||||
cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition,
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a default scroll count to use, and function to return a default
|
||||
* {@link ScrollPosition} for forward vs backward pagination.
|
||||
* <p>For offset scrolling, use {@link ScrollPosition#offset()} to scroll
|
||||
* from the beginning. Currently, it is not possible to go back from the end.
|
||||
* <p>For keyset scrolling, use {@link ScrollPosition#keyset()} to scroll
|
||||
* from the beginning, or {@link KeysetScrollPosition#reverse()} the same
|
||||
* to go back from the end.
|
||||
* <p>By default a count of 20 and {@link ScrollPosition#offset()} are used.
|
||||
* @since 1.2.5
|
||||
*/
|
||||
public Builder<T, R> defaultScrollSubrange(
|
||||
int defaultCount, Function<Boolean, ScrollPosition> defaultPosition) {
|
||||
|
||||
return new Builder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy, defaultCount, defaultPosition, this.sort, this.customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a {@link ScrollSubrange} to use when a paginated request does
|
||||
* not specify a cursor and/or a count of items.
|
||||
* <p>By default, this is {@link OffsetScrollPosition#offset()} with a
|
||||
* count of 20.
|
||||
* @return a new {@link Builder} instance with all previously configured
|
||||
* options and {@code Sort} applied
|
||||
* <p>By default, this is {@link OffsetScrollPosition#offset()} with a count of 20.
|
||||
* @return a new {@link Builder} instance
|
||||
* @since 1.2.0
|
||||
* @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
|
||||
*/
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||
@Deprecated(since = "1.2.5", forRemoval = true)
|
||||
public Builder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubrange) {
|
||||
return new Builder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy, defaultSubrange, this.sort, this.customizer);
|
||||
return new Builder<>(this.executor, this.domainType, this.resultType, this.cursorStrategy,
|
||||
(defaultSubrange != null ? defaultSubrange.count().getAsInt() : null),
|
||||
(defaultSubrange != null ? forward -> defaultSubrange.position().get() : null),
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -489,7 +518,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
public Builder<T, R> sortBy(Sort sort) {
|
||||
Assert.notNull(sort, "Sort must not be null");
|
||||
return new Builder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy, this.defaultSubrange, sort, customizer);
|
||||
this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition,
|
||||
sort, customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -508,7 +538,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
public Builder<T, R> customizer(QuerydslBinderCustomizer<? extends EntityPath<T>> customizer) {
|
||||
Assert.notNull(customizer, "QuerydslBinderCustomizer must not be null");
|
||||
return new Builder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy, this.defaultSubrange, this.sort, customizer);
|
||||
this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition,
|
||||
this.sort, customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -536,7 +567,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
return new ScrollableEntityFetcher<>(
|
||||
this.executor, this.domainType, this.resultType,
|
||||
(this.cursorStrategy != null ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy()),
|
||||
(this.defaultSubrange != null ? this.defaultSubrange : RepositoryUtils.defaultScrollSubrange()),
|
||||
(this.defaultScrollCount != null ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount()),
|
||||
(this.defaultScrollPosition != null ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition()),
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
@@ -583,7 +615,10 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
private final CursorStrategy<ScrollPosition> cursorStrategy;
|
||||
|
||||
@Nullable
|
||||
private final ScrollSubrange defaultSubrange;
|
||||
private final Integer defaultScrollCount;
|
||||
|
||||
@Nullable
|
||||
private final Function<Boolean, ScrollPosition> defaultScrollPosition;
|
||||
|
||||
private final Sort sort;
|
||||
|
||||
@@ -592,19 +627,21 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
@SuppressWarnings("unchecked")
|
||||
ReactiveBuilder(ReactiveQuerydslPredicateExecutor<T> executor, Class<R> domainType) {
|
||||
this(executor, TypeInformation.of((Class<T>) domainType),
|
||||
domainType, null, null, Sort.unsorted(), NO_OP_BINDER_CUSTOMIZER);
|
||||
domainType, null, null, null, Sort.unsorted(), NO_OP_BINDER_CUSTOMIZER);
|
||||
}
|
||||
|
||||
ReactiveBuilder(
|
||||
ReactiveQuerydslPredicateExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType,
|
||||
@Nullable CursorStrategy<ScrollPosition> cursorStrategy, @Nullable ScrollSubrange defaultSubrange,
|
||||
@Nullable CursorStrategy<ScrollPosition> cursorStrategy,
|
||||
@Nullable Integer defaultScrollCount, @Nullable Function<Boolean, ScrollPosition> defaultScrollPosition,
|
||||
Sort sort, QuerydslBinderCustomizer<? extends EntityPath<T>> customizer) {
|
||||
|
||||
this.executor = executor;
|
||||
this.domainType = domainType;
|
||||
this.resultType = resultType;
|
||||
this.cursorStrategy = cursorStrategy;
|
||||
this.defaultSubrange = defaultSubrange;
|
||||
this.defaultScrollCount = defaultScrollCount;
|
||||
this.defaultScrollPosition = defaultScrollPosition;
|
||||
this.sort = sort;
|
||||
this.customizer = customizer;
|
||||
}
|
||||
@@ -621,7 +658,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
public <P> ReactiveBuilder<T, P> projectAs(Class<P> projectionType) {
|
||||
Assert.notNull(projectionType, "Projection type must not be null");
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType, projectionType,
|
||||
this.cursorStrategy, this.defaultSubrange, this.sort, this.customizer);
|
||||
this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition,
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -635,21 +673,44 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
*/
|
||||
public ReactiveBuilder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosition> cursorStrategy) {
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType, this.resultType,
|
||||
cursorStrategy, this.defaultSubrange, this.sort, this.customizer);
|
||||
cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition,
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a default scroll count to use, and function to return a default
|
||||
* {@link ScrollPosition} for forward vs backward pagination.
|
||||
* <p>For offset scrolling, use {@link ScrollPosition#offset()} to scroll
|
||||
* from the beginning. Currently, it is not possible to go back from the end.
|
||||
* <p>For keyset scrolling, use {@link ScrollPosition#keyset()} to scroll
|
||||
* from the beginning, or {@link KeysetScrollPosition#reverse()} the same
|
||||
* to go back from the end.
|
||||
* <p>By default a count of 20 and {@link ScrollPosition#offset()} are used.
|
||||
* @since 1.2.5
|
||||
*/
|
||||
public ReactiveBuilder<T, R> defaultScrollSubrange(
|
||||
int defaultCount, Function<Boolean, ScrollPosition> defaultPosition) {
|
||||
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy, defaultCount, defaultPosition, this.sort, this.customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a {@link ScrollSubrange} to use when a paginated request does
|
||||
* not specify a cursor and/or a count of items.
|
||||
* <p>By default, this is {@link OffsetScrollPosition#offset()} with a
|
||||
* count of 20.
|
||||
* @return a new {@link Builder} instance with all previously configured
|
||||
* options and {@code Sort} applied
|
||||
* <p>By default, this is {@link OffsetScrollPosition#offset()} with a count of 20.
|
||||
* @return a new {@link Builder} instance
|
||||
* @since 1.2.0
|
||||
* @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
|
||||
*/
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||
@Deprecated(since = "1.2.5", forRemoval = true)
|
||||
public ReactiveBuilder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubrange) {
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy, defaultSubrange, this.sort, this.customizer);
|
||||
this.cursorStrategy,
|
||||
(defaultSubrange != null ? defaultSubrange.count().getAsInt() : null),
|
||||
(defaultSubrange != null ? forward -> defaultSubrange.position().get() : null),
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -661,7 +722,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
public ReactiveBuilder<T, R> sortBy(Sort sort) {
|
||||
Assert.notNull(sort, "Sort must not be null");
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy, this.defaultSubrange, sort, this.customizer);
|
||||
this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition,
|
||||
sort, this.customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -680,7 +742,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
public ReactiveBuilder<T, R> customizer(QuerydslBinderCustomizer<? extends EntityPath<T>> customizer) {
|
||||
Assert.notNull(customizer, "QuerydslBinderCustomizer must not be null");
|
||||
return new ReactiveBuilder<>(this.executor, this.domainType, this.resultType,
|
||||
this.cursorStrategy, this.defaultSubrange, this.sort, customizer);
|
||||
this.cursorStrategy, this.defaultScrollCount, this.defaultScrollPosition,
|
||||
this.sort, customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -708,7 +771,8 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
return new ReactiveScrollableEntityFetcher<>(
|
||||
this.executor, this.domainType, this.resultType,
|
||||
(this.cursorStrategy != null ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy()),
|
||||
(this.defaultSubrange != null ? this.defaultSubrange : RepositoryUtils.defaultScrollSubrange()),
|
||||
(this.defaultScrollCount != null ? this.defaultScrollCount : RepositoryUtils.defaultScrollCount()),
|
||||
(this.defaultScrollPosition != null ? this.defaultScrollPosition : RepositoryUtils.defaultScrollPosition()),
|
||||
this.sort, this.customizer);
|
||||
}
|
||||
|
||||
@@ -843,33 +907,35 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
|
||||
private final CursorStrategy<ScrollPosition> cursorStrategy;
|
||||
|
||||
private final ScrollSubrange defaultSubrange;
|
||||
private final int defaultCount;
|
||||
|
||||
private final Function<Boolean, ScrollPosition> defaultPosition;
|
||||
|
||||
ScrollableEntityFetcher(QuerydslPredicateExecutor<T> executor,
|
||||
TypeInformation<T> domainType,
|
||||
Class<R> resultType,
|
||||
CursorStrategy<ScrollPosition> cursorStrategy,
|
||||
ScrollSubrange defaultSubrange,
|
||||
int defaultCount,
|
||||
Function<Boolean, ScrollPosition> defaultPosition,
|
||||
Sort sort,
|
||||
QuerydslBinderCustomizer<? extends EntityPath<T>> customizer) {
|
||||
|
||||
super(executor, domainType, resultType, sort, customizer);
|
||||
|
||||
Assert.notNull(cursorStrategy, "CursorStrategy is required");
|
||||
Assert.notNull(defaultSubrange, "Default ScrollSubrange is required");
|
||||
Assert.isTrue(defaultSubrange.position().isPresent(), "Default ScrollPosition is required");
|
||||
Assert.isTrue(defaultSubrange.count().isPresent(), "Default scroll limit is required");
|
||||
Assert.notNull(defaultPosition, "'defaultPosition' is required");
|
||||
|
||||
this.cursorStrategy = cursorStrategy;
|
||||
this.defaultSubrange = defaultSubrange;
|
||||
this.defaultCount = defaultCount;
|
||||
this.defaultPosition = defaultPosition;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||
@Override
|
||||
protected Iterable<R> getResult(FetchableFluentQuery<R> queryToUse, DataFetchingEnvironment env) {
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, this.cursorStrategy, this.defaultSubrange);
|
||||
int count = range.count().getAsInt();
|
||||
ScrollPosition position = range.position().get();
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, this.cursorStrategy);
|
||||
int count = range.count().orElse(this.defaultCount);
|
||||
ScrollPosition position = (range.position().isPresent() ?
|
||||
range.position().get() : this.defaultPosition.apply(range.forward()));
|
||||
return queryToUse.limit(count).scroll(position);
|
||||
}
|
||||
|
||||
@@ -987,7 +1053,9 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
|
||||
private final CursorStrategy<ScrollPosition> cursorStrategy;
|
||||
|
||||
private final ScrollSubrange defaultSubrange;
|
||||
private final int defaultCount;
|
||||
|
||||
private final Function<Boolean, ScrollPosition> defaultPosition;
|
||||
|
||||
private final Sort sort;
|
||||
|
||||
@@ -995,22 +1063,23 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
ReactiveScrollableEntityFetcher(ReactiveQuerydslPredicateExecutor<T> executor,
|
||||
TypeInformation<T> domainType,
|
||||
Class<R> resultType,
|
||||
CursorStrategy<ScrollPosition> cursorStrategy, ScrollSubrange defaultSubrange,
|
||||
CursorStrategy<ScrollPosition> cursorStrategy,
|
||||
int defaultCount,
|
||||
Function<Boolean, ScrollPosition> defaultPosition,
|
||||
Sort sort,
|
||||
QuerydslBinderCustomizer<? extends EntityPath<T>> customizer) {
|
||||
|
||||
super(domainType, (QuerydslBinderCustomizer) customizer);
|
||||
|
||||
Assert.notNull(cursorStrategy, "CursorStrategy is required");
|
||||
Assert.notNull(defaultSubrange, "Default ScrollSubrange is required");
|
||||
Assert.isTrue(defaultSubrange.position().isPresent(), "Default ScrollPosition is required");
|
||||
Assert.isTrue(defaultSubrange.count().isPresent(), "Default scroll limit is required");
|
||||
Assert.notNull(defaultPosition, "'defaultPosition' is required");
|
||||
|
||||
this.executor = executor;
|
||||
this.resultType = resultType;
|
||||
this.scrollableResultType = ResolvableType.forClassWithGenerics(Iterable.class, resultType);
|
||||
this.cursorStrategy = cursorStrategy;
|
||||
this.defaultSubrange = defaultSubrange;
|
||||
this.defaultCount = defaultCount;
|
||||
this.defaultPosition = defaultPosition;
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@@ -1020,7 +1089,7 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "OptionalGetWithoutIsPresent"})
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Iterable<R>> get(DataFetchingEnvironment env) {
|
||||
return this.executor.findBy(buildPredicate(env), query -> {
|
||||
FluentQuery.ReactiveFluentQuery<R> queryToUse = (FluentQuery.ReactiveFluentQuery<R>) query;
|
||||
@@ -1036,9 +1105,10 @@ public abstract class QuerydslDataFetcher<T> {
|
||||
queryToUse = queryToUse.project(buildPropertyPaths(env.getSelectionSet(), this.resultType));
|
||||
}
|
||||
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, this.cursorStrategy, this.defaultSubrange);
|
||||
int count = range.count().getAsInt();
|
||||
ScrollPosition position = range.position().get();
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, this.cursorStrategy);
|
||||
int count = range.count().orElse(this.defaultCount);
|
||||
ScrollPosition position = (range.position().isPresent() ?
|
||||
range.position().get() : this.defaultPosition.apply(range.forward()));
|
||||
return queryToUse.limit(count).scroll(position).map(Function.identity());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.graphql.data.query;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.function.Function;
|
||||
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
|
||||
@@ -84,22 +85,25 @@ class RepositoryUtils {
|
||||
return CursorStrategy.withEncoder(new ScrollPositionCursorStrategy(), CursorEncoder.base64());
|
||||
}
|
||||
|
||||
public static int defaultScrollCount() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
public static Function<Boolean, ScrollPosition> defaultScrollPosition() {
|
||||
return forward -> ScrollPosition.offset();
|
||||
}
|
||||
|
||||
public static ScrollSubrange defaultScrollSubrange() {
|
||||
return ScrollSubrange.create(ScrollPosition.offset(), 20, true);
|
||||
}
|
||||
|
||||
public static ScrollSubrange getScrollSubrange(
|
||||
DataFetchingEnvironment env, CursorStrategy<ScrollPosition> strategy,
|
||||
ScrollSubrange defaultSubrange) {
|
||||
DataFetchingEnvironment env, CursorStrategy<ScrollPosition> strategy) {
|
||||
|
||||
boolean forward = !env.getArguments().containsKey("last");
|
||||
|
||||
Integer count = env.getArgument(forward ? "first" : "last");
|
||||
count = (count != null ? count : defaultSubrange.count().getAsInt());
|
||||
|
||||
String cursor = env.getArgument(forward ? "after" : "before");
|
||||
ScrollPosition position = (cursor != null ? strategy.fromCursor(cursor) : defaultSubrange.position().get());
|
||||
|
||||
ScrollPosition position = (cursor != null ? strategy.fromCursor(cursor) : null);
|
||||
return ScrollSubrange.create(position, count, forward);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,6 @@ public class RepositoryUtilsTests {
|
||||
|
||||
private final CursorStrategy<ScrollPosition> cursorStrategy = RepositoryUtils.defaultCursorStrategy();
|
||||
|
||||
private final ScrollSubrange defaultSubrange = ScrollSubrange.create(ScrollPosition.offset(50), 20, true);
|
||||
|
||||
|
||||
@Test
|
||||
void buildScrollSubrangeForward() {
|
||||
@@ -48,7 +46,7 @@ public class RepositoryUtilsTests {
|
||||
DataFetchingEnvironment env = environment(
|
||||
Map.of("first", count, "after", cursorStrategy.toCursor(offset)));
|
||||
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, cursorStrategy, defaultSubrange);
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, cursorStrategy);
|
||||
|
||||
assertThat(range.position().get()).isEqualTo(ScrollPosition.offset(11));
|
||||
assertThat(range.count().getAsInt()).isEqualTo(count);
|
||||
@@ -63,7 +61,7 @@ public class RepositoryUtilsTests {
|
||||
DataFetchingEnvironment env = environment(
|
||||
Map.of("last", count, "before", cursorStrategy.toCursor(offset)));
|
||||
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, cursorStrategy, defaultSubrange);
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, cursorStrategy);
|
||||
|
||||
assertThat(range.position().get()).isEqualTo(ScrollPosition.offset(5));
|
||||
assertThat(range.count().getAsInt()).isEqualTo(count);
|
||||
@@ -71,34 +69,41 @@ public class RepositoryUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildScrollSubrangeForwardWithDefaultScrollSubrange() {
|
||||
void noInput() {
|
||||
DataFetchingEnvironment env = environment(Collections.emptyMap());
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, cursorStrategy, defaultSubrange);
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, cursorStrategy);
|
||||
|
||||
assertThat(range.position().get()).isEqualTo(getDefaultPosition().advanceBy(1));
|
||||
assertThat(range.count().getAsInt()).isEqualTo(this.defaultSubrange.count().getAsInt());
|
||||
assertThat(range.position()).isNotPresent();
|
||||
assertThat(range.count()).isNotPresent();
|
||||
assertThat(range.forward()).isTrue();
|
||||
}
|
||||
|
||||
@Test // gh-900
|
||||
void buildScrollSubrangeBackwardFromDefaultPosition() {
|
||||
@Test
|
||||
void buildScrollSubrangeForwardWithoutPosition() {
|
||||
int count = 5;
|
||||
DataFetchingEnvironment env = environment(Map.of("last", count));
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, cursorStrategy, defaultSubrange);
|
||||
DataFetchingEnvironment env = environment(Map.of("first", count));
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, cursorStrategy);
|
||||
|
||||
assertThat(range.position().get()).isEqualTo(getDefaultPosition().advanceBy(-count));
|
||||
assertThat(range.position()).isNotPresent();
|
||||
assertThat(range.count().getAsInt()).isEqualTo(count);
|
||||
assertThat(range.forward()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildScrollSubrangeBackwardWithoutPosition() {
|
||||
int count = 5;
|
||||
DataFetchingEnvironment env = environment(Map.of("last", count));
|
||||
ScrollSubrange range = RepositoryUtils.getScrollSubrange(env, cursorStrategy);
|
||||
|
||||
assertThat(range.position()).isNotPresent();
|
||||
assertThat(range.count().getAsInt()).isEqualTo(count);
|
||||
assertThat(range.forward()).isFalse();
|
||||
}
|
||||
|
||||
private static DataFetchingEnvironment environment(Map<String, Object> arguments) {
|
||||
return DataFetchingEnvironmentImpl.newDataFetchingEnvironment()
|
||||
.arguments(arguments)
|
||||
.build();
|
||||
}
|
||||
|
||||
private OffsetScrollPosition getDefaultPosition() {
|
||||
return (OffsetScrollPosition) defaultSubrange.position().get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user