Polishing

Closes gh-59
This commit is contained in:
Rossen Stoyanchev
2021-06-24 21:11:59 +01:00
parent 2d53fdc73f
commit 17bf9ee287
6 changed files with 127 additions and 152 deletions

View File

@@ -3,6 +3,7 @@ package io.spring.sample.graphql.repository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.CrudRepository;
public interface ArtifactRepositories extends CrudRepository<ArtifactRepository, String>, QuerydslPredicateExecutor<ArtifactRepository> {
public interface ArtifactRepositories extends
CrudRepository<ArtifactRepository, String>, QuerydslPredicateExecutor<ArtifactRepository> {
}

View File

@@ -17,11 +17,9 @@ public class ArtifactRepositoryDataWiring implements RuntimeWiringCustomizer {
@Override
public void customize(RuntimeWiring.Builder builder) {
builder.type("Query",
typeWiring -> typeWiring.dataFetcher("artifactRepositories", QuerydslDataFetcher
.builder(repositories).many())
.dataFetcher("artifactRepository", QuerydslDataFetcher
.builder(repositories).single()));
builder.type("Query", typeWiring -> typeWiring
.dataFetcher("artifactRepositories", QuerydslDataFetcher.builder(repositories).many())
.dataFetcher("artifactRepository", QuerydslDataFetcher.builder(repositories).single()));
}
}

View File

@@ -51,10 +51,10 @@ class DtoInstantiatingConverter<T> implements Converter<Object, T> {
public DtoInstantiatingConverter(Class<T> dtoType,
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context,
EntityInstantiators entityInstantiators) {
this.targetType = dtoType;
this.context = context;
this.instantiator = entityInstantiators
.getInstantiatorFor(context.getRequiredPersistentEntity(dtoType));
this.instantiator = entityInstantiators.getInstantiatorFor(context.getRequiredPersistentEntity(dtoType));
}
@SuppressWarnings("unchecked")
@@ -65,39 +65,32 @@ class DtoInstantiatingConverter<T> implements Converter<Object, T> {
return (T) source;
}
PersistentEntity<?, ?> sourceEntity = context
.getRequiredPersistentEntity(source.getClass());
PersistentEntity<?, ?> sourceEntity = this.context.getRequiredPersistentEntity(source.getClass());
PersistentPropertyAccessor<?> sourceAccessor = sourceEntity
.getPropertyAccessor(source);
PersistentEntity<?, ?> targetEntity = context
.getRequiredPersistentEntity(targetType);
PreferredConstructor<?, ? extends PersistentProperty<?>> constructor = targetEntity
.getPersistenceConstructor();
PersistentPropertyAccessor<?> sourceAccessor = sourceEntity.getPropertyAccessor(source);
PersistentEntity<?, ?> entity = this.context.getRequiredPersistentEntity(this.targetType);
PreferredConstructor<?, ? extends PersistentProperty<?>> constructor = entity.getPersistenceConstructor();
@SuppressWarnings({"rawtypes", "unchecked"})
Object dto = instantiator
.createInstance(targetEntity, new ParameterValueProvider() {
Object dto = this.instantiator.createInstance(entity, new ParameterValueProvider() {
@Override
public Object getParameterValue(Parameter parameter) {
return sourceAccessor.getProperty(sourceEntity
.getRequiredPersistentProperty(parameter.getName()));
return sourceAccessor.getProperty(
sourceEntity.getRequiredPersistentProperty(parameter.getName()));
}
});
PersistentPropertyAccessor<?> dtoAccessor = targetEntity
.getPropertyAccessor(dto);
PersistentPropertyAccessor<?> dtoAccessor = entity.getPropertyAccessor(dto);
targetEntity.doWithProperties((SimplePropertyHandler) property -> {
entity.doWithProperties((SimplePropertyHandler) property -> {
if (constructor.isConstructorParameter(property)) {
return;
}
dtoAccessor.setProperty(property,
sourceAccessor.getProperty(sourceEntity
.getRequiredPersistentProperty(property.getName())));
sourceAccessor.getProperty(sourceEntity.getRequiredPersistentProperty(property.getName())));
});
return (T) dto;

View File

@@ -39,8 +39,7 @@ class DtoMappingContext extends AbstractMappingContext<DtoMappingContext.DtoPers
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
// No Java std lib type introspection to not interfere with encapsulation.
// We do not want to get into the business of materializing Java types.
if (type.getType().getName().startsWith("java.") || type.getType().getName()
.startsWith("javax.")) {
if (type.getType().getName().startsWith("java.") || type.getType().getName().startsWith("javax.")) {
return false;
}
return super.shouldCreatePersistentEntityFor(type);
@@ -52,8 +51,9 @@ class DtoMappingContext extends AbstractMappingContext<DtoMappingContext.DtoPers
}
@Override
protected DtoPersistentProperty createPersistentProperty(Property property, DtoPersistentEntity<?> owner,
SimpleTypeHolder simpleTypeHolder) {
protected DtoPersistentProperty createPersistentProperty(
Property property, DtoPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new DtoPersistentProperty(property, owner, simpleTypeHolder);
}
@@ -67,8 +67,10 @@ class DtoMappingContext extends AbstractMappingContext<DtoMappingContext.DtoPers
static class DtoPersistentProperty extends AnnotationBasedPersistentProperty<DtoPersistentProperty> {
public DtoPersistentProperty(Property property, PersistentEntity<?, DtoPersistentProperty> owner,
public DtoPersistentProperty(
Property property, PersistentEntity<?, DtoPersistentProperty> owner,
SimpleTypeHolder simpleTypeHolder) {
super(property, owner, simpleTypeHolder);
}

View File

@@ -51,7 +51,7 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* Entrypoint to create {@link DataFetcher} using repositories through Querydsl.
* Entry point to create {@link DataFetcher} using repositories through Querydsl.
* Exposes builders accepting {@link QuerydslPredicateExecutor} or
* {@link ReactiveQuerydslPredicateExecutor} that support customization of bindings
* and interface- and DTO projections. Instances can be created through a
@@ -59,11 +59,11 @@ import org.springframework.util.MultiValueMap;
* {@link Builder#single()} or {@link Builder#many()} objects.
* <p>Example:
* <pre class="code">
* interface BookRepository extends Repository&lt;Book, String&gt;,
* QuerydslPredicateExecutor&lt;Book&gt;{}
* interface BookRepository extends
* Repository&lt;Book, String&gt;, QuerydslPredicateExecutor&lt;Book&gt;{}
*
* BookRepository repository = …;
* TypeRuntimeWiring wiring = …;
* TypeRuntimeWiring wiring = … ;
* BookRepository repository = … ;
*
* wiring.dataFetcher("books", QuerydslDataFetcher.builder(repository).many())
* .dataFetcher("book", QuerydslDataFetcher.builder(repository).single());
@@ -75,11 +75,11 @@ import org.springframework.util.MultiValueMap;
* {@link #builder(ReactiveQuerydslPredicateExecutor) builder}.
* <p>For example:
* <pre class="code">
* interface BookRepository extends Repository&lt;Book, String&gt;,
* ReactiveQuerydslPredicateExecutor&lt;Book&gt;{}
* interface BookRepository extends
* Repository&lt;Book, String&gt;, ReactiveQuerydslPredicateExecutor&lt;Book&gt;{}
*
* BookRepository repository = …;
* TypeRuntimeWiring wiring = …;
* BookRepository repository = …;
*
* wiring.dataFetcher("books", QuerydslDataFetcher.builder(repository).many())
* .dataFetcher("book", QuerydslDataFetcher.builder(repository).single());
@@ -96,15 +96,13 @@ import org.springframework.util.MultiValueMap;
public abstract class QuerydslDataFetcher<T> {
private static final QuerydslPredicateBuilder BUILDER = new QuerydslPredicateBuilder(
DefaultConversionService
.getSharedInstance(), SimpleEntityPathResolver.INSTANCE);
DefaultConversionService.getSharedInstance(), SimpleEntityPathResolver.INSTANCE);
private final TypeInformation<T> domainType;
private final QuerydslBinderCustomizer<EntityPath<?>> customizer;
QuerydslDataFetcher(ClassTypeInformation<T> domainType,
QuerydslBinderCustomizer<EntityPath<?>> customizer) {
QuerydslDataFetcher(ClassTypeInformation<T> domainType, QuerydslBinderCustomizer<EntityPath<?>> customizer) {
this.customizer = customizer;
this.domainType = domainType;
}
@@ -118,13 +116,12 @@ public abstract class QuerydslDataFetcher<T> {
*/
@SuppressWarnings("unchecked")
public static <T> Builder<T, T> builder(QuerydslPredicateExecutor<T> executor) {
Class<?> repositoryInterface = getRepositoryInterface(executor);
DefaultRepositoryMetadata metadata = new DefaultRepositoryMetadata(repositoryInterface);
return new Builder<>(executor, (ClassTypeInformation<T>) ClassTypeInformation
.from(metadata.getDomainType()), (bindings, root) -> {
}, Function.identity());
return new Builder<>(executor,
(ClassTypeInformation<T>) ClassTypeInformation.from(metadata.getDomainType()),
(bindings, root) -> {}, Function.identity());
}
/**
@@ -136,30 +133,27 @@ public abstract class QuerydslDataFetcher<T> {
*/
@SuppressWarnings("unchecked")
public static <T> ReactiveBuilder<T, T> builder(ReactiveQuerydslPredicateExecutor<T> executor) {
Class<?> repositoryInterface = getRepositoryInterface(executor);
DefaultRepositoryMetadata metadata = new DefaultRepositoryMetadata(repositoryInterface);
return new ReactiveBuilder<>(executor, (ClassTypeInformation<T>) ClassTypeInformation
.from(metadata.getDomainType()), (bindings, root) -> {
}, Function.identity());
return new ReactiveBuilder<>(executor,
(ClassTypeInformation<T>) ClassTypeInformation.from(metadata.getDomainType()),
(bindings, root) -> {}, Function.identity());
}
@SuppressWarnings({"unchecked", "rawtypes"})
Predicate buildPredicate(DataFetchingEnvironment environment) {
protected Predicate buildPredicate(DataFetchingEnvironment environment) {
MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<>();
QuerydslBindings bindings = new QuerydslBindings();
EntityPath<?> path = SimpleEntityPathResolver.INSTANCE
.createPath(domainType.getType());
customizer.customize(bindings, path);
EntityPath<?> path = SimpleEntityPathResolver.INSTANCE.createPath(this.domainType.getType());
this.customizer.customize(bindings, path);
for (Map.Entry<String, Object> entry : environment.getArguments().entrySet()) {
parameters.put(entry.getKey(), Collections.singletonList(entry.getValue()));
}
return BUILDER.getPredicate(domainType, (MultiValueMap) parameters, bindings);
return BUILDER.getPredicate(this.domainType, (MultiValueMap) parameters, bindings);
}
private static <S, T> Function<S, T> createProjection(Class<T> projectionType) {
@@ -169,37 +163,31 @@ public abstract class QuerydslDataFetcher<T> {
if (projectionType.isInterface()) {
ProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory();
return element -> projectionFactory
.createProjection(projectionType, element);
return element -> projectionFactory.createProjection(projectionType, element);
}
DtoInstantiatingConverter<T> converter = new DtoInstantiatingConverter<>(projectionType,
new DtoMappingContext(), new EntityInstantiators());
return converter::convert;
}
private static Class<?> getRepositoryInterface(Object executor) {
Assert.isInstanceOf(Repository.class, executor);
Type[] genericInterfaces = executor.getClass().getGenericInterfaces();
for (Type genericInterface : genericInterfaces) {
ResolvableType resolvableType = ResolvableType.forType(genericInterface);
if (resolvableType.getRawClass() == null || MergedAnnotations
.from(resolvableType.getRawClass())
.isPresent(NoRepositoryBean.class)) {
Class<?> rawClass = ResolvableType.forType(genericInterface).getRawClass();
if (rawClass == null || MergedAnnotations.from(rawClass).isPresent(NoRepositoryBean.class)) {
continue;
}
if (Repository.class.isAssignableFrom(resolvableType.getRawClass())) {
return resolvableType.getRawClass();
if (Repository.class.isAssignableFrom(rawClass)) {
return rawClass;
}
}
throw new IllegalArgumentException(String
.format("Cannot resolve repository interface from %s", executor));
throw new IllegalArgumentException(
String.format("Cannot resolve repository interface from %s", executor));
}
/**
@@ -222,6 +210,7 @@ public abstract class QuerydslDataFetcher<T> {
Builder(QuerydslPredicateExecutor<T> executor, ClassTypeInformation<T> domainType,
QuerydslBinderCustomizer<? extends EntityPath<T>> customizer,
Function<T, R> resultConverter) {
this.executor = executor;
this.domainType = domainType;
this.customizer = customizer;
@@ -240,7 +229,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<>(executor, domainType, customizer, createProjection(projectionType));
return new Builder<>(
this.executor, this.domainType, this.customizer, createProjection(projectionType));
}
/**
@@ -252,7 +242,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<>(executor, domainType, customizer, resultConverter);
return new Builder<>(
this.executor, this.domainType, customizer, this.resultConverter);
}
/**
@@ -260,7 +251,8 @@ public abstract class QuerydslDataFetcher<T> {
* @return a {@link DataFetcher} based on Querydsl to fetch one object
*/
public DataFetcher<R> single() {
return new SingleEntityFetcher<>(executor, domainType, customizer, resultConverter);
return new SingleEntityFetcher<>(
this.executor, this.domainType, this.customizer, this.resultConverter);
}
/**
@@ -268,7 +260,8 @@ public abstract class QuerydslDataFetcher<T> {
* @return a {@link DataFetcher} based on Querydsl to fetch many objects
*/
public DataFetcher<Iterable<R>> many() {
return new ManyEntityFetcher<>(executor, domainType, customizer, resultConverter);
return new ManyEntityFetcher<>(
this.executor, this.domainType, this.customizer, this.resultConverter);
}
}
@@ -294,6 +287,7 @@ public abstract class QuerydslDataFetcher<T> {
ClassTypeInformation<T> domainType,
QuerydslBinderCustomizer<? extends EntityPath<T>> customizer,
Function<T, R> resultConverter) {
this.executor = executor;
this.domainType = domainType;
this.customizer = customizer;
@@ -312,7 +306,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<>(executor, domainType, customizer, createProjection(projectionType));
return new ReactiveBuilder<>(
this.executor, this.domainType, this.customizer, createProjection(projectionType));
}
/**
@@ -324,7 +319,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<>(executor, domainType, customizer, resultConverter);
return new ReactiveBuilder<>(
this.executor, this.domainType, customizer, this.resultConverter);
}
/**
@@ -332,7 +328,8 @@ public abstract class QuerydslDataFetcher<T> {
* @return a {@link DataFetcher} based on Querydsl to fetch one object
*/
public DataFetcher<Mono<R>> single() {
return new ReactiveSingleEntityFetcher<>(executor, domainType, customizer, resultConverter);
return new ReactiveSingleEntityFetcher<>(
this.executor, this.domainType, this.customizer, this.resultConverter);
}
/**
@@ -340,12 +337,13 @@ public abstract class QuerydslDataFetcher<T> {
* @return a {@link DataFetcher} based on Querydsl to fetch many objects
*/
public DataFetcher<Flux<R>> many() {
return new ReactiveManyEntityFetcher<>(executor, domainType, customizer, resultConverter);
return new ReactiveManyEntityFetcher<>(
this.executor, this.domainType, this.customizer, this.resultConverter);
}
}
static class SingleEntityFetcher<T, R> extends QuerydslDataFetcher<T> implements DataFetcher<R> {
private static class SingleEntityFetcher<T, R> extends QuerydslDataFetcher<T> implements DataFetcher<R> {
private final QuerydslPredicateExecutor<T> executor;
@@ -356,21 +354,22 @@ public abstract class QuerydslDataFetcher<T> {
ClassTypeInformation<T> domainType,
QuerydslBinderCustomizer<? extends EntityPath<T>> customizer,
Function<T, R> resultConverter) {
super(domainType, (QuerydslBinderCustomizer) customizer);
this.executor = executor;
this.resultConverter = resultConverter;
}
@Override
@SuppressWarnings("ConstantConditions")
public R get(DataFetchingEnvironment environment) {
return executor.findOne(buildPredicate(environment)).map(resultConverter)
.orElse(null);
Predicate predicate = buildPredicate(environment);
return this.executor.findOne(predicate).map(this.resultConverter).orElse(null);
}
}
static class ManyEntityFetcher<T, R> extends QuerydslDataFetcher<T>
implements DataFetcher<Iterable<R>> {
private static class ManyEntityFetcher<T, R> extends QuerydslDataFetcher<T> implements DataFetcher<Iterable<R>> {
private final QuerydslPredicateExecutor<T> executor;
@@ -388,13 +387,13 @@ public abstract class QuerydslDataFetcher<T> {
@Override
public Iterable<R> get(DataFetchingEnvironment environment) {
return Streamable.of(executor.findAll(buildPredicate(environment)))
.map(resultConverter).toList();
Predicate predicate = buildPredicate(environment);
return Streamable.of(this.executor.findAll(predicate)).map(this.resultConverter).toList();
}
}
static class ReactiveSingleEntityFetcher<T, R> extends QuerydslDataFetcher<T> implements DataFetcher<Mono<R>> {
private static class ReactiveSingleEntityFetcher<T, R> extends QuerydslDataFetcher<T> implements DataFetcher<Mono<R>> {
private final ReactiveQuerydslPredicateExecutor<T> executor;
@@ -405,6 +404,7 @@ public abstract class QuerydslDataFetcher<T> {
ClassTypeInformation<T> domainType,
QuerydslBinderCustomizer<? extends EntityPath<T>> customizer,
Function<T, R> resultConverter) {
super(domainType, (QuerydslBinderCustomizer) customizer);
this.executor = executor;
this.resultConverter = resultConverter;
@@ -412,12 +412,12 @@ public abstract class QuerydslDataFetcher<T> {
@Override
public Mono<R> get(DataFetchingEnvironment environment) {
return executor.findOne(buildPredicate(environment)).map(resultConverter);
return this.executor.findOne(buildPredicate(environment)).map(this.resultConverter);
}
}
static class ReactiveManyEntityFetcher<T, R> extends QuerydslDataFetcher<T> implements DataFetcher<Flux<R>> {
private static class ReactiveManyEntityFetcher<T, R> extends QuerydslDataFetcher<T> implements DataFetcher<Flux<R>> {
private final ReactiveQuerydslPredicateExecutor<T> executor;
@@ -428,6 +428,7 @@ public abstract class QuerydslDataFetcher<T> {
ClassTypeInformation<T> domainType,
QuerydslBinderCustomizer<? extends EntityPath<T>> customizer,
Function<T, R> resultConverter) {
super(domainType, (QuerydslBinderCustomizer) customizer);
this.executor = executor;
this.resultConverter = resultConverter;
@@ -435,7 +436,7 @@ public abstract class QuerydslDataFetcher<T> {
@Override
public Flux<R> get(DataFetchingEnvironment environment) {
return executor.findAll(buildPredicate(environment)).map(resultConverter);
return this.executor.findAll(buildPredicate(environment)).map(this.resultConverter);
}
}

View File

@@ -58,18 +58,14 @@ class QuerydslDataFetcherTests {
when(mockRepository.findOne(any())).thenReturn(Optional.of(book));
WebGraphQlHandler handler = initWebGraphQlHandler(builder -> builder
.dataFetcher("bookById", QuerydslDataFetcher
.builder(mockRepository)
.single()));
.dataFetcher("bookById", QuerydslDataFetcher.builder(mockRepository).single()));
WebOutput output = handler.handle(new WebInput(
URI.create("http://abc.org"), new HttpHeaders(), Collections
.singletonMap("query", "{ bookById(id: 1) {name}}"), "1")).block();
WebOutput output = handler.handle(input("{ bookById(id: 1) {name}}")).block();
// TODO: getData interferes with method overrides
assertThat((Object) output.getData())
.isEqualTo(Collections.singletonMap("bookById", Collections
.singletonMap("name", "Hitchhiker's Guide to the Galaxy")));
assertThat((Object) output.getData()).isEqualTo(
Collections.singletonMap("bookById",
Collections.singletonMap("name", "Hitchhiker's Guide to the Galaxy")));
}
@Test
@@ -77,22 +73,17 @@ class QuerydslDataFetcherTests {
MockRepository mockRepository = mock(MockRepository.class);
Book book1 = new Book(42L, "Hitchhiker's Guide to the Galaxy", "Douglas Adams");
Book book2 = new Book(53L, "Breaking Bad", "Heisenberg");
when(mockRepository.findAll((Predicate) null))
.thenReturn(Arrays.asList(book1, book2));
when(mockRepository.findAll((Predicate) null)).thenReturn(Arrays.asList(book1, book2));
WebGraphQlHandler handler = initWebGraphQlHandler(builder -> builder
.dataFetcher("books", QuerydslDataFetcher
.builder(mockRepository)
.many()));
.dataFetcher("books", QuerydslDataFetcher.builder(mockRepository).many()));
WebOutput output = handler.handle(new WebInput(
URI.create("http://abc.org"), new HttpHeaders(), Collections
.singletonMap("query", "{ books {name}}"), "1")).block();
WebOutput output = handler.handle(input("{ books {name}}")).block();
assertThat((Object) output.getData())
.isEqualTo(Collections.singletonMap("books", Arrays.asList(Collections
.singletonMap("name", "Hitchhiker's Guide to the Galaxy"), Collections
.singletonMap("name", "Breaking Bad"))));
assertThat((Object) output.getData()).isEqualTo(
Collections.singletonMap("books", Arrays.asList(
Collections.singletonMap("name", "Hitchhiker's Guide to the Galaxy"),
Collections.singletonMap("name", "Breaking Bad"))));
}
@Test
@@ -107,13 +98,11 @@ class QuerydslDataFetcherTests {
.projectAs(BookProjection.class)
.single()));
WebOutput output = handler.handle(new WebInput(
URI.create("http://abc.org"), new HttpHeaders(), Collections
.singletonMap("query", "{ bookById(id: 1) {name}}"), "1")).block();
WebOutput output = handler.handle(input("{ bookById(id: 1) {name}}")).block();
assertThat((Object) output.getData())
.isEqualTo(Collections.singletonMap("bookById", Collections
.singletonMap("name", "Hitchhiker's Guide to the Galaxy by Douglas Adams")));
assertThat((Object) output.getData()).isEqualTo(
Collections.singletonMap("bookById",
Collections.singletonMap("name", "Hitchhiker's Guide to the Galaxy by Douglas Adams")));
}
@Test
@@ -128,13 +117,11 @@ class QuerydslDataFetcherTests {
.projectAs(BookDto.class)
.single()));
WebOutput output = handler.handle(new WebInput(
URI.create("http://abc.org"), new HttpHeaders(), Collections
.singletonMap("query", "{ bookById(id: 1) {name}}"), "1")).block();
WebOutput output = handler.handle(input("{ bookById(id: 1) {name}}")).block();
assertThat((Object) output.getData())
.isEqualTo(Collections.singletonMap("bookById", Collections
.singletonMap("name", "The book is: Hitchhiker's Guide to the Galaxy")));
assertThat((Object) output.getData()).isEqualTo(
Collections.singletonMap("bookById",
Collections.singletonMap("name", "The book is: Hitchhiker's Guide to the Galaxy")));
}
@Test
@@ -144,13 +131,12 @@ class QuerydslDataFetcherTests {
WebGraphQlHandler handler = initWebGraphQlHandler(builder -> builder
.dataFetcher("books", QuerydslDataFetcher
.builder(mockRepository)
.customizer((QuerydslBinderCustomizer<QBook>) (bindings, book) -> bindings.bind(book.name)
.firstOptional((path, value) -> value.map(path::startsWith)))
.customizer((QuerydslBinderCustomizer<QBook>) (bindings, book) ->
bindings.bind(book.name)
.firstOptional((path, value) -> value.map(path::startsWith)))
.many()));
handler.handle(new WebInput(
URI.create("http://abc.org"), new HttpHeaders(), Collections
.singletonMap("query", "{ books(name: \"H\", author: \"Doug\") {name}}"), "1")).block();
handler.handle(input("{ books(name: \"H\", author: \"Doug\") {name}}")).block();
ArgumentCaptor<Predicate> predicateCaptor = ArgumentCaptor.forClass(Predicate.class);
@@ -158,8 +144,7 @@ class QuerydslDataFetcherTests {
Predicate predicate = predicateCaptor.getValue();
assertThat(predicate).isEqualTo(QBook.book.name.startsWith("H")
.and(QBook.book.author.eq("Doug")));
assertThat(predicate).isEqualTo(QBook.book.name.startsWith("H").and(QBook.book.author.eq("Doug")));
}
@Test
@@ -169,18 +154,14 @@ class QuerydslDataFetcherTests {
when(mockRepository.findOne(any())).thenReturn(Mono.just(book));
WebGraphQlHandler handler = initWebGraphQlHandler(builder -> builder
.dataFetcher("bookById", QuerydslDataFetcher
.builder(mockRepository)
.single()));
.dataFetcher("bookById", QuerydslDataFetcher.builder(mockRepository).single()));
WebOutput output = handler.handle(new WebInput(
URI.create("http://abc.org"), new HttpHeaders(), Collections
.singletonMap("query", "{ bookById(id: 1) {name}}"), "1")).block();
WebOutput output = handler.handle(input("{ bookById(id: 1) {name}}")).block();
// TODO: getData interferes with method overries
assertThat((Object) output.getData())
.isEqualTo(Collections.singletonMap("bookById", Collections
.singletonMap("name", "Hitchhiker's Guide to the Galaxy")));
assertThat((Object) output.getData()).isEqualTo(
Collections.singletonMap("bookById",
Collections.singletonMap("name", "Hitchhiker's Guide to the Galaxy")));
}
@Test
@@ -188,22 +169,17 @@ class QuerydslDataFetcherTests {
ReactiveMockRepository mockRepository = mock(ReactiveMockRepository.class);
Book book1 = new Book(42L, "Hitchhiker's Guide to the Galaxy", "Douglas Adams");
Book book2 = new Book(53L, "Breaking Bad", "Heisenberg");
when(mockRepository.findAll((Predicate) null))
.thenReturn(Flux.just(book1, book2));
when(mockRepository.findAll((Predicate) null)).thenReturn(Flux.just(book1, book2));
WebGraphQlHandler handler = initWebGraphQlHandler(builder -> builder
.dataFetcher("books", QuerydslDataFetcher
.builder(mockRepository)
.many()));
.dataFetcher("books", QuerydslDataFetcher.builder(mockRepository).many()));
WebOutput output = handler.handle(new WebInput(
URI.create("http://abc.org"), new HttpHeaders(), Collections
.singletonMap("query", "{ books {name}}"), "1")).block();
WebOutput output = handler.handle(input("{ books {name}}")).block();
assertThat((Object) output.getData())
.isEqualTo(Collections.singletonMap("books", Arrays.asList(Collections
.singletonMap("name", "Hitchhiker's Guide to the Galaxy"), Collections
.singletonMap("name", "Breaking Bad"))));
assertThat((Object) output.getData()).isEqualTo(
Collections.singletonMap("books", Arrays.asList(
Collections.singletonMap("name", "Hitchhiker's Guide to the Galaxy"),
Collections.singletonMap("name", "Breaking Bad"))));
}
interface MockRepository extends Repository<Book, Long>, QuerydslPredicateExecutor<Book> {
@@ -222,8 +198,7 @@ class QuerydslDataFetcherTests {
private static GraphQlSource graphQlSource(Consumer<TypeRuntimeWiring.Builder> configurer) {
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
TypeRuntimeWiring.Builder wiringBuilder = TypeRuntimeWiring
.newTypeWiring("Query");
TypeRuntimeWiring.Builder wiringBuilder = TypeRuntimeWiring.newTypeWiring("Query");
configurer.accept(wiringBuilder);
builder.type(wiringBuilder);
return GraphQlSource.builder()
@@ -232,6 +207,11 @@ class QuerydslDataFetcherTests {
.build();
}
private WebInput input(String query) {
return new WebInput(URI.create("http://abc.org"), new HttpHeaders(),
Collections.singletonMap("query", query), "1");
}
interface BookProjection {
@Value("#{target.name + ' by ' + target.author}")