Detect if repository is a QuerydslBinderCustomizer

We now detect Querydsl customizers during auto-registration for
repositories that implement QuerydslBinderCustomizer as default
interface method.

See gh-201
This commit is contained in:
Mark Paluch
2021-11-25 15:13:24 +01:00
committed by Rossen Stoyanchev
parent 5c5cfdd277
commit 8704177741
2 changed files with 60 additions and 4 deletions

View File

@@ -600,6 +600,7 @@ public abstract class QuerydslDataFetcher<T> {
this.executorMap = initExecutorMap(executors, reactiveExecutors);
}
private Map<String, Function<Boolean, DataFetcher<?>>> initExecutorMap(
List<QuerydslPredicateExecutor<?>> executors,
List<ReactiveQuerydslPredicateExecutor<?>> reactiveExecutors) {
@@ -609,18 +610,20 @@ 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 ?
QuerydslDataFetcher.builder(executor).single() :
QuerydslDataFetcher.builder(executor).many());
builder(executor, customizer).single() :
builder(executor, customizer).many());
}
}
for (ReactiveQuerydslPredicateExecutor<?> reactiveExecutor : reactiveExecutors) {
String typeName = getTypeName(reactiveExecutor);
if (typeName != null) {
QuerydslBinderCustomizer<? extends EntityPath<?>> customizer = detectCustomizer(reactiveExecutor);
map.put(typeName, (single) -> single ?
QuerydslDataFetcher.builder(reactiveExecutor).single() :
QuerydslDataFetcher.builder(reactiveExecutor).many());
reactiveBuilder(reactiveExecutor, customizer).single() :
reactiveBuilder(reactiveExecutor, customizer).many());
}
}
@@ -643,6 +646,18 @@ public abstract class QuerydslDataFetcher<T> {
return metadata.getDomainType().getSimpleName();
}
@SuppressWarnings({"unchecked", "rawtypes"})
private Builder<?,?> builder(QuerydslPredicateExecutor<?> executor,
QuerydslBinderCustomizer<? extends EntityPath<?>> customizer) {
return QuerydslDataFetcher.builder(executor).customizer((QuerydslBinderCustomizer)customizer);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private ReactiveBuilder<?, ?> reactiveBuilder(ReactiveQuerydslPredicateExecutor<?> reactiveExecutor,
QuerydslBinderCustomizer<? extends EntityPath<?>> customizer) {
return QuerydslDataFetcher.builder(reactiveExecutor).customizer((QuerydslBinderCustomizer)customizer);
}
@Override
public TraversalControl visitGraphQLFieldDefinition(
GraphQLFieldDefinition fieldDefinition, TraverserContext<GraphQLSchemaElement> context) {
@@ -690,6 +705,13 @@ public abstract class QuerydslDataFetcher<T> {
DataFetcher<?> fetcher = registry.getDataFetcher(parent, fieldDefinition);
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

@@ -39,6 +39,7 @@ import org.springframework.data.map.MapKeyValueAdapter;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.Repository;
import org.springframework.graphql.Author;
@@ -110,6 +111,29 @@ class QuerydslDataFetcherTests {
tester.accept(graphQlSetup(mockRepository));
}
@Test
void shouldApplyCustomizerInRepository() {
MockWithCustomizerRepository repository = repositoryFactory.getRepository(MockWithCustomizerRepository.class);
Book book1 = new Book(42L, "Hitchhiker's Guide to the Galaxy", new Author(0L, "Douglas", "Adams"));
Book book2 = new Book(53L, "Breaking Bad", new Author(0L, "", "Heisenberg"));
repository.saveAll(Arrays.asList(book1, book2));
Consumer<GraphQlSetup> tester = graphQlSetup -> {
Mono<WebOutput> output = graphQlSetup.toWebGraphQlHandler().handleRequest(input("{ books {name}}"));
List<String> names = GraphQlResponse.from(output).toList("books", Book.class)
.stream().map(Book::getName).collect(Collectors.toList());
assertThat(names).containsExactlyInAnyOrder(book1.getName(), book2.getName());
};
// explicit wiring
tester.accept(graphQlSetup("books", QuerydslDataFetcher.builder(mockRepository).many()));
// auto registration
tester.accept(graphQlSetup(mockRepository));
}
@Test
void shouldFavorExplicitWiring() {
MockRepository mockRepository = mock(MockRepository.class);
@@ -258,6 +282,16 @@ class QuerydslDataFetcherTests {
}
@GraphQlRepository
interface MockWithCustomizerRepository extends CrudRepository<Book, Long>, QuerydslPredicateExecutor<Book>,
QuerydslBinderCustomizer<QBook> {
@Override
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> {