Polishing contribution

Closes gh-201
This commit is contained in:
Rossen Stoyanchev
2021-11-25 18:12:09 +00:00
parent 8704177741
commit 46d4d3d648
2 changed files with 59 additions and 46 deletions

View File

@@ -26,7 +26,19 @@ import java.util.function.Function;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.Predicate;
import graphql.schema.*;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.DataFetchingFieldSelectionSet;
import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLFieldsContainer;
import graphql.schema.GraphQLList;
import graphql.schema.GraphQLNamedOutputType;
import graphql.schema.GraphQLSchemaElement;
import graphql.schema.GraphQLType;
import graphql.schema.GraphQLTypeVisitor;
import graphql.schema.GraphQLTypeVisitorStub;
import graphql.schema.PropertyDataFetcher;
import graphql.util.TraversalControl;
import graphql.util.TraverserContext;
import reactor.core.publisher.Flux;
@@ -49,7 +61,6 @@ import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.graphql.data.GraphQlRepository;
@@ -102,6 +113,9 @@ public abstract class QuerydslDataFetcher<T> {
private static final QuerydslPredicateBuilder BUILDER = new QuerydslPredicateBuilder(
DefaultConversionService.getSharedInstance(), SimpleEntityPathResolver.INSTANCE);
@SuppressWarnings("rawtypes")
private static final QuerydslBinderCustomizer NO_OP_BINDER_CUSTOMIZER = (bindings, root) -> {};
private final TypeInformation<T> domainType;
@@ -242,7 +256,7 @@ public abstract class QuerydslDataFetcher<T> {
ClassTypeInformation.from((Class<T>) domainType),
domainType,
Sort.unsorted(),
(bindings, root) -> {});
NO_OP_BINDER_CUSTOMIZER);
}
Builder(QuerydslPredicateExecutor<T> executor, ClassTypeInformation<T> domainType,
@@ -256,7 +270,7 @@ public abstract class QuerydslDataFetcher<T> {
}
/**
* Project results returned from the {@link QueryByExampleExecutor}
* Project results returned from the {@link QuerydslPredicateExecutor}
* into the target {@code projectionType}. Projection types can be
* either interfaces with property getters to expose or regular classes
* outside the entity type hierarchy for DTO projections.
@@ -335,7 +349,7 @@ public abstract class QuerydslDataFetcher<T> {
ClassTypeInformation.from((Class<T>) domainType),
domainType,
Sort.unsorted(),
(bindings, root) -> {});
NO_OP_BINDER_CUSTOMIZER);
}
ReactiveBuilder(ReactiveQuerydslPredicateExecutor<T> executor,
@@ -352,7 +366,7 @@ public abstract class QuerydslDataFetcher<T> {
}
/**
* Project results returned from the {@link ReactiveQueryByExampleExecutor}
* Project results returned from the {@link ReactiveQuerydslPredicateExecutor}
* into the target {@code projectionType}. Projection types can be
* either interfaces with property getters to expose or regular classes
* outside the entity type hierarchy for DTO projections.
@@ -610,20 +624,18 @@ public abstract class QuerydslDataFetcher<T> {
for (QuerydslPredicateExecutor<?> executor : executors) {
String typeName = getTypeName(executor);
if (typeName != null) {
QuerydslBinderCustomizer<? extends EntityPath<?>> customizer = detectCustomizer(executor);
map.put(typeName, (single) -> single ?
builder(executor, customizer).single() :
builder(executor, customizer).many());
builder(executor).single() :
builder(executor).many());
}
}
for (ReactiveQuerydslPredicateExecutor<?> reactiveExecutor : reactiveExecutors) {
String typeName = getTypeName(reactiveExecutor);
if (typeName != null) {
QuerydslBinderCustomizer<? extends EntityPath<?>> customizer = detectCustomizer(reactiveExecutor);
map.put(typeName, (single) -> single ?
reactiveBuilder(reactiveExecutor, customizer).single() :
reactiveBuilder(reactiveExecutor, customizer).many());
reactiveBuilder(reactiveExecutor).single() :
reactiveBuilder(reactiveExecutor).many());
}
}
@@ -647,15 +659,22 @@ public abstract class QuerydslDataFetcher<T> {
}
@SuppressWarnings({"unchecked", "rawtypes"})
private Builder<?,?> builder(QuerydslPredicateExecutor<?> executor,
QuerydslBinderCustomizer<? extends EntityPath<?>> customizer) {
return QuerydslDataFetcher.builder(executor).customizer((QuerydslBinderCustomizer)customizer);
private Builder<?,?> builder(QuerydslPredicateExecutor<?> executor) {
return QuerydslDataFetcher.builder(executor)
.customizer((QuerydslBinderCustomizer) detectCustomizer(executor));
}
@SuppressWarnings({"unchecked", "rawtypes"})
private ReactiveBuilder<?, ?> reactiveBuilder(ReactiveQuerydslPredicateExecutor<?> reactiveExecutor,
QuerydslBinderCustomizer<? extends EntityPath<?>> customizer) {
return QuerydslDataFetcher.builder(reactiveExecutor).customizer((QuerydslBinderCustomizer)customizer);
private ReactiveBuilder<?, ?> reactiveBuilder(ReactiveQuerydslPredicateExecutor<?> reactiveExecutor) {
return QuerydslDataFetcher.builder(reactiveExecutor)
.customizer((QuerydslBinderCustomizer) detectCustomizer(reactiveExecutor));
}
@SuppressWarnings("unchecked")
private QuerydslBinderCustomizer<? extends EntityPath<?>> detectCustomizer(Object executor) {
return (executor instanceof QuerydslBinderCustomizer<?> ?
(QuerydslBinderCustomizer<? extends EntityPath<?>>) executor :
NO_OP_BINDER_CUSTOMIZER);
}
@Override
@@ -706,12 +725,6 @@ public abstract class QuerydslDataFetcher<T> {
return (fetcher != null && !(fetcher instanceof PropertyDataFetcher));
}
private QuerydslBinderCustomizer<? extends EntityPath<?>> detectCustomizer(Object executor) {
if(executor instanceof QuerydslBinderCustomizer<?>) {
return (QuerydslBinderCustomizer<? extends EntityPath<?>>) executor;
}
return ((bindings, root) -> {});
}
}
}

View File

@@ -134,6 +134,26 @@ class QuerydslDataFetcherTests {
tester.accept(graphQlSetup(mockRepository));
}
@Test
void shouldApplyCustomizerViaBuilder() {
MockRepository mockRepository = mock(MockRepository.class);
DataFetcher<Iterable<Book>> fetcher = QuerydslDataFetcher.builder(mockRepository)
.customizer((QuerydslBinderCustomizer<QBook>) (bindings, book) ->
bindings.bind(book.name).firstOptional((path, value) -> value.map(path::startsWith)))
.many();
graphQlSetup("books", fetcher).toWebGraphQlHandler()
.handleRequest(input("{ books(name: \"H\", author: \"Doug\") {name}}"))
.block();
ArgumentCaptor<Predicate> predicateCaptor = ArgumentCaptor.forClass(Predicate.class);
verify(mockRepository).findBy(predicateCaptor.capture(), any());
Predicate predicate = predicateCaptor.getValue();
assertThat(predicate).isEqualTo(QBook.book.name.startsWith("H").and(QBook.book.author.eq("Doug")));
}
@Test
void shouldFavorExplicitWiring() {
MockRepository mockRepository = mock(MockRepository.class);
@@ -186,26 +206,6 @@ class QuerydslDataFetcherTests {
assertThat(actualBook.getName()).isEqualTo("The book is: Hitchhiker's Guide to the Galaxy");
}
@Test
void shouldConstructPredicateProperly() {
MockRepository mockRepository = mock(MockRepository.class);
DataFetcher<Iterable<Book>> fetcher = QuerydslDataFetcher.builder(mockRepository)
.customizer((QuerydslBinderCustomizer<QBook>) (bindings, book) ->
bindings.bind(book.name).firstOptional((path, value) -> value.map(path::startsWith)))
.many();
WebGraphQlHandler handler = graphQlSetup("books", fetcher).toWebGraphQlHandler();
handler.handleRequest(input("{ books(name: \"H\", author: \"Doug\") {name}}")).block();
ArgumentCaptor<Predicate> predicateCaptor = ArgumentCaptor.forClass(Predicate.class);
verify(mockRepository).findBy(predicateCaptor.capture(), any());
Predicate predicate = predicateCaptor.getValue();
assertThat(predicate).isEqualTo(QBook.book.name.startsWith("H").and(QBook.book.author.eq("Doug")));
}
@Test
void shouldReactivelyFetchSingleItems() {
ReactiveMockRepository mockRepository = mock(ReactiveMockRepository.class);
@@ -279,9 +279,9 @@ class QuerydslDataFetcherTests {
@GraphQlRepository
interface MockRepository extends CrudRepository<Book, Long>, QuerydslPredicateExecutor<Book> {
}
@GraphQlRepository
interface MockWithCustomizerRepository extends CrudRepository<Book, Long>, QuerydslPredicateExecutor<Book>,
QuerydslBinderCustomizer<QBook> {
@@ -290,12 +290,12 @@ class QuerydslDataFetcherTests {
default void customize(QuerydslBindings bindings, QBook book){
bindings.bind(book.name).firstOptional((path, value) -> value.map(path::startsWith));
}
}
@GraphQlRepository
interface ReactiveMockRepository extends Repository<Book, Long>, ReactiveQuerydslPredicateExecutor<Book> {
}