From 8704177741b7d0ebbffd9f6fda3b4c18f0d42df7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 25 Nov 2021 15:13:24 +0100 Subject: [PATCH] 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 --- .../data/querydsl/QuerydslDataFetcher.java | 30 +++++++++++++--- .../querydsl/QuerydslDataFetcherTests.java | 34 +++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcher.java b/spring-graphql/src/main/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcher.java index a022eec8..574c41b2 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcher.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcher.java @@ -600,6 +600,7 @@ public abstract class QuerydslDataFetcher { this.executorMap = initExecutorMap(executors, reactiveExecutors); } + private Map>> initExecutorMap( List> executors, List> reactiveExecutors) { @@ -609,18 +610,20 @@ public abstract class QuerydslDataFetcher { for (QuerydslPredicateExecutor executor : executors) { String typeName = getTypeName(executor); if (typeName != null) { + QuerydslBinderCustomizer> 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> 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 { return metadata.getDomainType().getSimpleName(); } + @SuppressWarnings({"unchecked", "rawtypes"}) + private Builder builder(QuerydslPredicateExecutor executor, + QuerydslBinderCustomizer> customizer) { + return QuerydslDataFetcher.builder(executor).customizer((QuerydslBinderCustomizer)customizer); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private ReactiveBuilder reactiveBuilder(ReactiveQuerydslPredicateExecutor reactiveExecutor, + QuerydslBinderCustomizer> customizer) { + return QuerydslDataFetcher.builder(reactiveExecutor).customizer((QuerydslBinderCustomizer)customizer); + } + @Override public TraversalControl visitGraphQLFieldDefinition( GraphQLFieldDefinition fieldDefinition, TraverserContext context) { @@ -690,6 +705,13 @@ public abstract class QuerydslDataFetcher { DataFetcher fetcher = registry.getDataFetcher(parent, fieldDefinition); return (fetcher != null && !(fetcher instanceof PropertyDataFetcher)); } + + private QuerydslBinderCustomizer> detectCustomizer(Object executor) { + if(executor instanceof QuerydslBinderCustomizer) { + return (QuerydslBinderCustomizer>) executor; + } + return ((bindings, root) -> {}); + } } } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcherTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcherTests.java index ef74029d..33409e13 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcherTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/querydsl/QuerydslDataFetcherTests.java @@ -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 tester = graphQlSetup -> { + Mono output = graphQlSetup.toWebGraphQlHandler().handleRequest(input("{ books {name}}")); + + List 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, QuerydslPredicateExecutor, + QuerydslBinderCustomizer { + + @Override + default void customize(QuerydslBindings bindings, QBook book){ + bindings.bind(book.name).firstOptional((path, value) -> value.map(path::startsWith)); + } + } + @GraphQlRepository interface ReactiveMockRepository extends Repository, ReactiveQuerydslPredicateExecutor {