Skip to content

Commit

Permalink
Expose CursorsStrategy and Subrange as builder options
Browse files Browse the repository at this point in the history
This makes it possible to customize these settings individually,
optionally, and via QuerydslBuilderCustomizer.

See gh-597
  • Loading branch information
rstoyanchev committed Apr 12, 2023
1 parent 1f0c523 commit a0d7cc1
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 73 deletions.
10 changes: 2 additions & 8 deletions spring-graphql-docs/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -863,11 +863,8 @@ Then use it to create a `DataFetcher`:
QuerydslDataFetcher.builder(repository).many();
// For paginated queries
CursorStrategy<ScrollPosition> cursorStrategy = ... ;
ScrollSubrange defaultSubrange = ... ;
DataFetcher<Iterable<Account>> dataFetcher =
QuerydslDataFetcher.builder(repository).scrollable(cursorStrategy, defaultSubrange);
QuerydslDataFetcher.builder(repository).scrollable();
----

You can now register the above `DataFetcher` through a
Expand Down Expand Up @@ -1066,11 +1063,8 @@ Use `QueryByExampleDataFetcher` to turn the repository into a `DataFetcher`:
QueryByExampleDataFetcher.builder(repository).many();
// For paginated queries
CursorStrategy<ScrollPosition> cursorStrategy = ... ;
ScrollSubrange defaultSubrange = ... ;
DataFetcher<Iterable<Account>> dataFetcher =
QueryByExampleDataFetcher.builder(repository).scrollable(cursorStrategy, defaultSubrange);
QueryByExampleDataFetcher.builder(repository).scrollable();
----

You can now register the above `DataFetcher` through a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.springframework.data.util.TypeInformation;
import org.springframework.graphql.data.GraphQlArgumentBinder;
import org.springframework.graphql.data.GraphQlRepository;
import org.springframework.graphql.data.pagination.CursorEncoder;
import org.springframework.graphql.data.pagination.CursorStrategy;
import org.springframework.graphql.data.query.AutoRegistrationRuntimeWiringConfigurer.DataFetcherFactory;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
Expand Down Expand Up @@ -165,6 +166,7 @@ protected Collection<String> buildPropertyPaths(DataFetchingFieldSelectionSet se
}

protected ScrollSubrange buildScrollSubrange(DataFetchingEnvironment environment) {
Assert.state(this.cursorStrategy != null, "Expected CursorStrategy");
return RepositoryUtils.buildScrollSubrange(environment, this.cursorStrategy);
}

Expand Down Expand Up @@ -234,7 +236,10 @@ public static RuntimeWiringConfigurer autoRegistrationConfigurer(
for (QueryByExampleExecutor<?> executor : executors) {
String typeName = RepositoryUtils.getGraphQlTypeName(executor);
if (typeName != null) {
Builder<?, ?> builder = customize(executor, builder(executor));
Builder<?, ?> builder = customize(executor, builder(executor)
.cursorStrategy(cursorStrategy)
.defaultScrollSubrange(defaultScrollSubrange));

factories.put(typeName, new DataFetcherFactory() {
@Override
public DataFetcher<?> single() {
Expand All @@ -248,7 +253,7 @@ public DataFetcher<?> many() {

@Override
public DataFetcher<?> scrollable() {
return builder.scrollable(cursorStrategy, defaultScrollSubrange);
return builder.scrollable();
}
});
}
Expand All @@ -257,7 +262,10 @@ public DataFetcher<?> scrollable() {
for (ReactiveQueryByExampleExecutor<?> executor : reactiveExecutors) {
String typeName = RepositoryUtils.getGraphQlTypeName(executor);
if (typeName != null) {
ReactiveBuilder<?, ?> builder = customize(executor, builder(executor));
ReactiveBuilder<?, ?> builder = customize(executor, builder(executor)
.cursorStrategy(cursorStrategy)
.defaultScrollSubrange(defaultScrollSubrange));

factories.put(typeName, new DataFetcherFactory() {
@Override
public DataFetcher<?> single() {
Expand All @@ -271,7 +279,7 @@ public DataFetcher<?> many() {

@Override
public DataFetcher<?> scrollable() {
return builder.scrollable(cursorStrategy, defaultScrollSubrange);
return builder.scrollable();
}
});
}
Expand Down Expand Up @@ -356,17 +364,28 @@ public static class Builder<T, R> {

private final Class<R> resultType;

@Nullable
private final CursorStrategy<ScrollPosition> cursorStrategy;

@Nullable
private final ScrollSubrange defaultSubrange;

private final Sort sort;

@SuppressWarnings("unchecked")
Builder(QueryByExampleExecutor<T> executor, Class<R> domainType) {
this(executor, TypeInformation.of((Class<T>) domainType), domainType, Sort.unsorted());
this(executor, TypeInformation.of((Class<T>) domainType), domainType, null, null, Sort.unsorted());
}

Builder(QueryByExampleExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType, Sort sort) {
Builder(QueryByExampleExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType,
@Nullable CursorStrategy<ScrollPosition> cursorStrategy, @Nullable ScrollSubrange defaultSubrange,
Sort sort) {

this.executor = executor;
this.domainType = domainType;
this.resultType = resultType;
this.cursorStrategy = cursorStrategy;
this.defaultSubrange = defaultSubrange;
this.sort = sort;
}

Expand All @@ -381,7 +400,36 @@ public static class Builder<T, R> {
*/
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.sort);
return new Builder<>(this.executor, this.domainType,
projectionType, this.cursorStrategy, this.defaultSubrange, this.sort);
}

/**
* Configure strategy for decoding a cursor from a paginated request.
* <p>By default, this is {@link ScrollPositionCursorStrategy} with
* {@link CursorEncoder#base64()} encoding.
* @param cursorStrategy the strategy to use
* @return a new {@link Builder} instance with all previously configured
* options and {@code Sort} applied
* @since 1.2
*/
public Builder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosition> cursorStrategy) {
return new Builder<>(this.executor, this.domainType,
this.resultType, cursorStrategy, this.defaultSubrange, this.sort);
}

/**
* 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#initial()} with a
* count of 20.
* @return a new {@link Builder} instance with all previously configured
* options and {@code Sort} applied
* @since 1.2
*/
public Builder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubrange) {
return new Builder<>(this.executor, this.domainType,
this.resultType, this.cursorStrategy, defaultSubrange, this.sort);
}

/**
Expand All @@ -392,7 +440,8 @@ public <P> Builder<T, P> projectAs(Class<P> projectionType) {
*/
public Builder<T, R> sortBy(Sort sort) {
Assert.notNull(sort, "Sort must not be null");
return new Builder<>(this.executor, this.domainType, this.resultType, sort);
return new Builder<>(this.executor, this.domainType,
this.resultType, this.cursorStrategy, this.defaultSubrange, sort);
}

/**
Expand All @@ -414,11 +463,12 @@ public DataFetcher<Iterable<R>> many() {
* {@link org.springframework.data.domain.Window}.
* @since 1.2
*/
public DataFetcher<Iterable<R>> scrollable(
CursorStrategy<ScrollPosition> cursorStrategy, ScrollSubrange defaultScrollSubrange) {

public DataFetcher<Iterable<R>> scrollable() {
return new ScrollableEntityFetcher<>(
this.executor, this.domainType, this.resultType, cursorStrategy, defaultScrollSubrange, this.sort);
this.executor, this.domainType, this.resultType,
(this.cursorStrategy != null ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy()),
(this.defaultSubrange != null ? this.defaultSubrange : RepositoryUtils.defaultScrollSubrange()),
this.sort);
}

}
Expand Down Expand Up @@ -460,20 +510,29 @@ public static class ReactiveBuilder<T, R> {

private final Class<R> resultType;

@Nullable
private final CursorStrategy<ScrollPosition> cursorStrategy;

@Nullable
private final ScrollSubrange defaultSubrange;

private final Sort sort;

@SuppressWarnings("unchecked")
ReactiveBuilder(ReactiveQueryByExampleExecutor<T> executor, Class<R> domainType) {
this(executor, TypeInformation.of((Class<T>) domainType), domainType, Sort.unsorted());
this(executor, TypeInformation.of((Class<T>) domainType), domainType, null, null, Sort.unsorted());
}

ReactiveBuilder(
ReactiveQueryByExampleExecutor<T> executor, TypeInformation<T> domainType,
Class<R> resultType, Sort sort) {
ReactiveQueryByExampleExecutor<T> executor, TypeInformation<T> domainType, Class<R> resultType,
@Nullable CursorStrategy<ScrollPosition> cursorStrategy, @Nullable ScrollSubrange defaultSubrange,
Sort sort) {

this.executor = executor;
this.domainType = domainType;
this.resultType = resultType;
this.cursorStrategy = cursorStrategy;
this.defaultSubrange = defaultSubrange;
this.sort = sort;
}

Expand All @@ -488,7 +547,36 @@ public static class ReactiveBuilder<T, R> {
*/
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.sort);
return new ReactiveBuilder<>(this.executor, this.domainType,
projectionType, this.cursorStrategy, this.defaultSubrange, this.sort);
}

/**
* Configure strategy for decoding a cursor from a paginated request.
* <p>By default, this is {@link ScrollPositionCursorStrategy} with
* {@link CursorEncoder#base64()} encoding.
* @param cursorStrategy the strategy to use
* @return a new {@link Builder} instance with all previously configured
* options and {@code Sort} applied
* @since 1.2
*/
public ReactiveBuilder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosition> cursorStrategy) {
return new ReactiveBuilder<>(this.executor, this.domainType,
this.resultType, cursorStrategy, this.defaultSubrange, this.sort);
}

/**
* 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#initial()} with a
* count of 20.
* @return a new {@link Builder} instance with all previously configured
* options and {@code Sort} applied
* @since 1.2
*/
public ReactiveBuilder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubrange) {
return new ReactiveBuilder<>(this.executor, this.domainType,
this.resultType, this.cursorStrategy, defaultSubrange, this.sort);
}

/**
Expand All @@ -499,7 +587,8 @@ public <P> ReactiveBuilder<T, P> projectAs(Class<P> projectionType) {
*/
public ReactiveBuilder<T, R> sortBy(Sort sort) {
Assert.notNull(sort, "Sort must not be null");
return new ReactiveBuilder<>(this.executor, this.domainType, this.resultType, sort);
return new ReactiveBuilder<>(this.executor, this.domainType,
this.resultType, this.cursorStrategy, this.defaultSubrange, sort);
}

/**
Expand All @@ -521,11 +610,12 @@ public DataFetcher<Flux<R>> many() {
* {@link org.springframework.data.domain.Window}.
* @since 1.2
*/
public DataFetcher<Mono<Iterable<R>>> scrollable(
CursorStrategy<ScrollPosition> cursorStrategy, ScrollSubrange defaultScrollSubrange) {

public DataFetcher<Mono<Iterable<R>>> scrollable() {
return new ReactiveScrollableEntityFetcher<>(
this.executor, this.domainType, this.resultType, cursorStrategy, defaultScrollSubrange, this.sort);
this.executor, this.domainType, this.resultType,
(this.cursorStrategy != null ? this.cursorStrategy : RepositoryUtils.defaultCursorStrategy()),
(this.defaultSubrange != null ? this.defaultSubrange : RepositoryUtils.defaultScrollSubrange()),
this.sort);
}

}
Expand Down
Loading

0 comments on commit a0d7cc1

Please sign in to comment.