diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/querybyexample/QueryByExampleDataFetcher.java b/spring-graphql/src/main/java/org/springframework/graphql/data/querybyexample/QueryByExampleDataFetcher.java index fe397dac..970c8dbd 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/querybyexample/QueryByExampleDataFetcher.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/querybyexample/QueryByExampleDataFetcher.java @@ -45,7 +45,6 @@ import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.MergedAnnotations; -import org.springframework.core.convert.ConversionService; import org.springframework.data.domain.Example; import org.springframework.data.domain.Sort; import org.springframework.data.repository.NoRepositoryBean; @@ -87,35 +86,65 @@ import org.springframework.util.StringUtils; * wiring.dataFetcher("book", QueryByExampleDataFetcher.builder(repository).single()); * * - *

See methods on {@link QueryByExampleDataFetcher.Builder} and {@link QueryByExampleDataFetcher.ReactiveBuilder} for further - * options on GraphQL Query argument to Query by Example bindings, result projections, and sorting. + *

See methods on {@link QueryByExampleDataFetcher.Builder} and + * {@link QueryByExampleDataFetcher.ReactiveBuilder} for further options on + * GraphQL Query argument to Query by Example bindings, result projections, and + * sorting. * * @param returned result type * @author Greg Turnquist + * @since 1.0.0 + * * @see QueryByExampleExecutor * @see ReactiveQueryByExampleExecutor * @see Example * @see * Spring Data Query By Example extension - * @since 1.0.0 */ public abstract class QueryByExampleDataFetcher { private final TypeInformation domainType; - private GraphQlArgumentInstantiator instantiator; + private final GraphQlArgumentInstantiator instantiator; - QueryByExampleDataFetcher(TypeInformation domainType, @Nullable ConversionService conversionService) { + + QueryByExampleDataFetcher(TypeInformation domainType) { this.domainType = domainType; - this.instantiator = new GraphQlArgumentInstantiator(conversionService); + this.instantiator = new GraphQlArgumentInstantiator(null); } + + /** + * Prepare an {@link Example} from GraphQL query arguments. + * @param env contextual info for the GraphQL query + * @return the resulting example + */ + protected Example buildExample(DataFetchingEnvironment env) { + return Example.of(this.instantiator.instantiate(env.getArguments(), this.domainType.getType())); + } + + protected boolean requiresProjection(Class resultType) { + return !resultType.equals(this.domainType.getType()); + } + + protected Collection buildPropertyPaths(DataFetchingFieldSelectionSet selection, Class resultType) { + + // Compute selection only for non-projections + if (this.domainType.getType().equals(resultType) || + this.domainType.getType().isAssignableFrom(resultType) || + this.domainType.isSubTypeOf(resultType)) { + return PropertySelection.create(this.domainType, selection).toList(); + } + return Collections.emptyList(); + } + + /** * Create a new {@link Builder} accepting {@link QueryByExampleExecutor} * to build a {@link DataFetcher}. * - * @param executor the repository object to use - * @param result type + * @param executor the QBE repository object to use + * @param the domain type of the repository * @return a new builder */ public static Builder builder(QueryByExampleExecutor executor) { @@ -124,10 +153,10 @@ public abstract class QueryByExampleDataFetcher { /** * Create a new {@link ReactiveBuilder} accepting - * {@link ReactiveQueryByExampleExecutor} to build a reactive {@link DataFetcher}. + * {@link ReactiveQueryByExampleExecutor} to build a {@link DataFetcher}. * - * @param executor the repository object to use - * @param result type + * @param executor the QBE repository object to use + * @param the domain type of the repository * @return a new builder */ public static ReactiveBuilder builder(ReactiveQueryByExampleExecutor executor) { @@ -177,31 +206,6 @@ public abstract class QueryByExampleDataFetcher { String.format("Cannot resolve repository interface from %s", executor)); } - /** - * Prepare an {@link Example} from GraphQL query arguments. - * - * @param environment contextual info for the GraphQL query - * @return the resulting example - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - protected Example buildExample(DataFetchingEnvironment environment) { - return Example.of(instantiator.instantiate(environment.getArguments(), domainType.getType())); - } - - protected boolean requiresProjection(Class resultType) { - return !resultType.equals(this.domainType.getType()); - } - - protected Collection buildPropertyPaths(DataFetchingFieldSelectionSet selection, Class resultType) { - - // Compute selection only for non-projections - if (this.domainType.getType().equals(resultType) || - this.domainType.getType().isAssignableFrom(resultType) || - this.domainType.isSubTypeOf(resultType)) { - return PropertySelection.create(this.domainType, selection).toList(); - } - return Collections.emptyList(); - } /** * Builder for a Query by Example-based {@link DataFetcher}. Note that builder @@ -221,78 +225,64 @@ public abstract class QueryByExampleDataFetcher { private final Sort sort; - private final ConversionService conversionService; - @SuppressWarnings("unchecked") Builder(QueryByExampleExecutor executor, Class domainType) { - this(executor, - ClassTypeInformation.from((Class) domainType), - domainType, - Sort.unsorted(), - null); + this(executor, ClassTypeInformation.from((Class) domainType), domainType, Sort.unsorted()); } - Builder(QueryByExampleExecutor executor, ClassTypeInformation domainType, Class resultType, Sort sort, ConversionService conversionService) { + Builder(QueryByExampleExecutor executor, ClassTypeInformation domainType, Class resultType, Sort sort) { this.executor = executor; this.domainType = domainType; this.resultType = resultType; this.sort = sort; - this.conversionService = conversionService; } /** * Project results returned from the {@link QueryByExampleExecutor} * into the target {@code projectionType}. Projection types can be - * either interfaces declaring getters for properties to expose or - * regular classes outside the entity type hierarchy for - * DTO projection. - * + * either interfaces with property getters to expose or regular classes + * outside the entity type hierarchy for DTO projections. * @param projectionType projection type * @return a new {@link Builder} instance with all previously * configured options and {@code projectionType} applied */ public

Builder projectAs(Class

projectionType) { Assert.notNull(projectionType, "Projection type must not be null"); - return new Builder<>(this.executor, this.domainType, projectionType, this.sort, this.conversionService); + return new Builder<>(this.executor, this.domainType, projectionType, this.sort); } /** * Apply a {@link Sort} order. - * * @param sort the default sort order * @return a new {@link Builder} instance with all previously configured * options and {@code Sort} applied */ public Builder sortBy(Sort sort) { Assert.notNull(sort, "Sort must not be null"); - return new Builder<>(this.executor, this.domainType, this.resultType, sort, this.conversionService); + return new Builder<>(this.executor, this.domainType, this.resultType, sort); } /** * Build a {@link DataFetcher} to fetch single object instances. - * - * @return a {@link DataFetcher} based on Query by Example to fetch one object */ public DataFetcher single() { - return new SingleEntityFetcher<>( - this.executor, this.domainType, this.resultType, this.sort, this.conversionService); + return new SingleEntityFetcher<>(this.executor, this.domainType, this.resultType, this.sort); } /** * Build a {@link DataFetcher} to fetch many object instances. - * - * @return a {@link DataFetcher} based on Query Example to fetch many objects */ public DataFetcher> many() { - return new ManyEntityFetcher<>( - this.executor, this.domainType, this.resultType, this.sort, this.conversionService); + return new ManyEntityFetcher<>(this.executor, this.domainType, this.resultType, this.sort); } + } + /** - * Builder for a reactive Query by Example-based {@link DataFetcher}. Note that builder - * instances are immutable and return a new instance of the builder when - * calling configuration methods. + * Builder for a reactive Query by Example-based {@link DataFetcher}. + * Note that builder instances are immutable and return a new instance of + * the builder when calling configuration methods. * * @param domain type * @param result type @@ -307,78 +297,63 @@ public abstract class QueryByExampleDataFetcher { private final Sort sort; - private final ConversionService conversionService; - @SuppressWarnings("unchecked") ReactiveBuilder(ReactiveQueryByExampleExecutor executor, Class domainType) { - this(executor, - ClassTypeInformation.from((Class) domainType), - domainType, - Sort.unsorted(), - null); + this(executor, ClassTypeInformation.from((Class) domainType), domainType, Sort.unsorted()); } - ReactiveBuilder(ReactiveQueryByExampleExecutor executor, - TypeInformation domainType, - Class resultType, - Sort sort, - ConversionService conversionService) { + ReactiveBuilder( + ReactiveQueryByExampleExecutor executor, TypeInformation domainType, + Class resultType, Sort sort) { + this.executor = executor; this.domainType = domainType; this.resultType = resultType; this.sort = sort; - this.conversionService = conversionService; } /** - * Project results returned from the {@link QueryByExampleExecutor} + * Project results returned from the {@link ReactiveQueryByExampleExecutor} * into the target {@code projectionType}. Projection types can be - * either interfaces declaring getters for properties to expose or - * regular classes outside the entity type hierarchy for - * DTO projection. - * + * either interfaces with property getters to expose or regular classes + * outside the entity type hierarchy for DTO projections. * @param projectionType projection type * @return a new {@link ReactiveBuilder} instance with all previously * configured options and {@code projectionType} applied */ public

ReactiveBuilder projectAs(Class

projectionType) { Assert.notNull(projectionType, "Projection type must not be null"); - return new ReactiveBuilder<>(this.executor, this.domainType, projectionType, this.sort, this.conversionService); + return new ReactiveBuilder<>(this.executor, this.domainType, projectionType, this.sort); } /** * Apply a {@link Sort} order. - * * @param sort the default sort order * @return a new {@link ReactiveBuilder} instance with all previously configured * options and {@code Sort} applied */ public ReactiveBuilder sortBy(Sort sort) { Assert.notNull(sort, "Sort must not be null"); - return new ReactiveBuilder<>(this.executor, this.domainType, this.resultType, sort, this.conversionService); + return new ReactiveBuilder<>(this.executor, this.domainType, this.resultType, sort); } /** - * Build a {@link DataFetcher} to fetch single object instances through {@link Mono}. - * - * @return a {@link DataFetcher} based on Query by Example to fetch one object + * Build a {@link DataFetcher} to fetch single object instances. */ public DataFetcher> single() { - return new ReactiveSingleEntityFetcher<>( - this.executor, this.domainType, this.resultType, this.sort, this.conversionService); + return new ReactiveSingleEntityFetcher<>(this.executor, this.domainType, this.resultType, this.sort); } /** - * Build a {@link DataFetcher} to fetch many object instances through {@link Flux}. - * - * @return a {@link DataFetcher} based on Query by Example to fetch many objects + * Build a {@link DataFetcher} to fetch many object instances. */ public DataFetcher> many() { - return new ReactiveManyEntityFetcher<>( - this.executor, this.domainType, this.resultType, this.sort, this.conversionService); + return new ReactiveManyEntityFetcher<>(this.executor, this.domainType, this.resultType, this.sort); } + } + private static class SingleEntityFetcher extends QueryByExampleDataFetcher implements DataFetcher { private final QueryByExampleExecutor executor; @@ -388,13 +363,10 @@ public abstract class QueryByExampleDataFetcher { private final Sort sort; @SuppressWarnings({"unchecked", "rawtypes"}) - SingleEntityFetcher(QueryByExampleExecutor executor, - TypeInformation domainType, - Class resultType, - Sort sort, - ConversionService conversionService) { + SingleEntityFetcher( + QueryByExampleExecutor executor, TypeInformation domainType, Class resultType, Sort sort) { - super(domainType, conversionService); + super(domainType); this.executor = executor; this.resultType = resultType; this.sort = sort; @@ -424,6 +396,7 @@ public abstract class QueryByExampleDataFetcher { } + private static class ManyEntityFetcher extends QueryByExampleDataFetcher implements DataFetcher> { private final QueryByExampleExecutor executor; @@ -433,12 +406,11 @@ public abstract class QueryByExampleDataFetcher { private final Sort sort; @SuppressWarnings({"unchecked", "rawtypes"}) - ManyEntityFetcher(QueryByExampleExecutor executor, - TypeInformation domainType, - Class resultType, - Sort sort, - ConversionService conversionService) { - super(domainType, conversionService); + ManyEntityFetcher( + QueryByExampleExecutor executor, TypeInformation domainType, + Class resultType, Sort sort) { + + super(domainType); this.executor = executor; this.resultType = resultType; this.sort = sort; @@ -467,6 +439,7 @@ public abstract class QueryByExampleDataFetcher { } + private static class ReactiveSingleEntityFetcher extends QueryByExampleDataFetcher implements DataFetcher> { private final ReactiveQueryByExampleExecutor executor; @@ -476,13 +449,11 @@ public abstract class QueryByExampleDataFetcher { private final Sort sort; @SuppressWarnings({"unchecked", "rawtypes"}) - ReactiveSingleEntityFetcher(ReactiveQueryByExampleExecutor executor, - TypeInformation domainType, - Class resultType, - Sort sort, - ConversionService conversionService) { + ReactiveSingleEntityFetcher( + ReactiveQueryByExampleExecutor executor, TypeInformation domainType, + Class resultType, Sort sort) { - super(domainType, conversionService); + super(domainType); this.executor = executor; this.resultType = resultType; this.sort = sort; @@ -511,6 +482,7 @@ public abstract class QueryByExampleDataFetcher { } + private static class ReactiveManyEntityFetcher extends QueryByExampleDataFetcher implements DataFetcher> { private final ReactiveQueryByExampleExecutor executor; @@ -520,13 +492,11 @@ public abstract class QueryByExampleDataFetcher { private final Sort sort; @SuppressWarnings({"unchecked", "rawtypes"}) - ReactiveManyEntityFetcher(ReactiveQueryByExampleExecutor executor, - TypeInformation domainType, - Class resultType, - Sort sort, - ConversionService conversionService) { + ReactiveManyEntityFetcher( + ReactiveQueryByExampleExecutor executor, TypeInformation domainType, + Class resultType, Sort sort) { - super(domainType, conversionService); + super(domainType); this.executor = executor; this.resultType = resultType; this.sort = sort; @@ -552,8 +522,10 @@ public abstract class QueryByExampleDataFetcher { return queryToUse.all(); }); } + } + /** * GraphQLTypeVisitor that auto-registers Query By Example Spring Data repositories. */ @@ -659,4 +631,5 @@ public abstract class QueryByExampleDataFetcher { return (fetcher != null && !(fetcher instanceof PropertyDataFetcher)); } } + } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/querybyexample/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/data/querybyexample/package-info.java index 6b1a7652..869707d3 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/querybyexample/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/querybyexample/package-info.java @@ -15,8 +15,11 @@ */ /** - * Support for {@link graphql.schema.DataFetcher}s backed by Querydsl based - * Spring Data repositories. + * {@link graphql.schema.DataFetcher} implementations built on the Spring Data, + * Query by Example support. + * + * @see + * Spring Data Query By Example extension */ @NonNullApi @NonNullFields 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 446aacc4..a022eec8 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 @@ -49,6 +49,7 @@ 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; @@ -255,11 +256,10 @@ public abstract class QuerydslDataFetcher { } /** - * Project results returned from the {@link QuerydslPredicateExecutor} + * Project results returned from the {@link QueryByExampleExecutor} * into the target {@code projectionType}. Projection types can be - * either interfaces declaring getters for properties to expose or - * regular classes outside the entity type hierarchy for - * DTO projection. + * either interfaces with property getters to expose or regular classes + * outside the entity type hierarchy for DTO projections. * @param projectionType projection type * @return a new {@link Builder} instance with all previously * configured options and {@code projectionType} applied @@ -293,7 +293,6 @@ public abstract class QuerydslDataFetcher { /** * Build a {@link DataFetcher} to fetch single object instances. - * @return a {@link DataFetcher} based on Querydsl to fetch one object */ public DataFetcher single() { return new SingleEntityFetcher<>( @@ -302,7 +301,6 @@ public abstract class QuerydslDataFetcher { /** * Build a {@link DataFetcher} to fetch many object instances. - * @return a {@link DataFetcher} based on Querydsl to fetch many objects */ public DataFetcher> many() { return new ManyEntityFetcher<>( @@ -354,11 +352,10 @@ public abstract class QuerydslDataFetcher { } /** - * Project results returned from the {@link QuerydslPredicateExecutor} + * Project results returned from the {@link ReactiveQueryByExampleExecutor} * into the target {@code projectionType}. Projection types can be - * either interfaces declaring getters for properties to expose or - * regular classes outside the entity type hierarchy for - * DTO projection. + * either interfaces with property getters to expose or regular classes + * outside the entity type hierarchy for DTO projections. * @param projectionType projection type * @return a new {@link Builder} instance with all previously * configured options and {@code projectionType} applied @@ -391,8 +388,7 @@ public abstract class QuerydslDataFetcher { } /** - * Build a {@link DataFetcher} to fetch single object instances through {@link Mono}. - * @return a {@link DataFetcher} based on Querydsl to fetch one object + * Build a {@link DataFetcher} to fetch single object instances}. */ public DataFetcher> single() { return new ReactiveSingleEntityFetcher<>( @@ -400,8 +396,7 @@ public abstract class QuerydslDataFetcher { } /** - * Build a {@link DataFetcher} to fetch many object instances through {@link Flux}. - * @return a {@link DataFetcher} based on Querydsl to fetch many objects + * Build a {@link DataFetcher} to fetch many object instances. */ public DataFetcher> many() { return new ReactiveManyEntityFetcher<>( diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/querydsl/package-info.java b/spring-graphql/src/main/java/org/springframework/graphql/data/querydsl/package-info.java index 40193a5c..512bbbdd 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/querydsl/package-info.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/querydsl/package-info.java @@ -15,8 +15,11 @@ */ /** - * Support for {@link graphql.schema.DataFetcher}s backed by Querydsl based - * Spring Data repositories. + * {@link graphql.schema.DataFetcher} implementations built on Querydsl, Spring + * Data repositories. + * + * @see + * Spring Data Querydsl extension */ @NonNullApi @NonNullFields diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/jpa/BookRepository.java b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/jpa/BookJpaRepository.java similarity index 74% rename from spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/jpa/BookRepository.java rename to spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/jpa/BookJpaRepository.java index 62d8cc7b..9fbb8576 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/jpa/BookRepository.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/jpa/BookJpaRepository.java @@ -4,5 +4,5 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.graphql.data.GraphQlRepository; @GraphQlRepository -public interface BookRepository extends JpaRepository { +public interface BookJpaRepository extends JpaRepository { } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/jpa/QueryByExampleDataFetcherJpaTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/jpa/QueryByExampleDataFetcherJpaTests.java index 22ec804e..bacf0048 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/jpa/QueryByExampleDataFetcherJpaTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/jpa/QueryByExampleDataFetcherJpaTests.java @@ -70,27 +70,8 @@ import static org.mockito.Mockito.when; class QueryByExampleDataFetcherJpaTests { @Autowired - private BookRepository repository; + private BookJpaRepository repository; - static GraphQlSetup graphQlSetup(String fieldName, DataFetcher fetcher) { - return initGraphQlSetup(null).queryFetcher(fieldName, fetcher); - } - - static GraphQlSetup graphQlSetup(@Nullable QueryByExampleExecutor executor) { - return initGraphQlSetup(executor); - } - - private static GraphQlSetup initGraphQlSetup( - @Nullable QueryByExampleExecutor executor) { - - GraphQLTypeVisitor visitor = QueryByExampleDataFetcher.registrationTypeVisitor( - executor != null - ? Collections.singletonList(executor) - : Collections.emptyList(), - Collections.emptyList()); - - return GraphQlSetup.schemaResource(BookSource.schema).typeVisitor(visitor); - } @Test void shouldFetchSingleItems() { @@ -137,7 +118,7 @@ class QueryByExampleDataFetcherJpaTests { @Test void shouldFavorExplicitWiring() { - BookRepository mockRepository = mock(BookRepository.class); + BookJpaRepository mockRepository = mock(BookJpaRepository.class); Book book = new Book(42L, "Hitchhiker's Guide to the Galaxy", new Author(0L, "Douglas", "Adams")); when(mockRepository.findBy(any(), any())).thenReturn(Optional.of(book)); @@ -188,6 +169,23 @@ class QueryByExampleDataFetcherJpaTests { assertThat(actualBook.getName()).isEqualTo("The book is: Hitchhiker's Guide to the Galaxy"); } + private static GraphQlSetup graphQlSetup(String fieldName, DataFetcher fetcher) { + return initGraphQlSetup(null).queryFetcher(fieldName, fetcher); + } + + private static GraphQlSetup graphQlSetup(@Nullable QueryByExampleExecutor executor) { + return initGraphQlSetup(executor); + } + + private static GraphQlSetup initGraphQlSetup(@Nullable QueryByExampleExecutor executor) { + + GraphQLTypeVisitor visitor = QueryByExampleDataFetcher.registrationTypeVisitor( + executor != null ? Collections.singletonList(executor) : Collections.emptyList(), + Collections.emptyList()); + + return GraphQlSetup.schemaResource(BookSource.schema).typeVisitor(visitor); + } + private WebInput input(String query) { return new WebInput(URI.create("/"), new HttpHeaders(), Collections.singletonMap("query", query), null, "1"); } @@ -200,6 +198,7 @@ class QueryByExampleDataFetcherJpaTests { } + static class BookDto { private final String name; @@ -214,6 +213,7 @@ class QueryByExampleDataFetcherJpaTests { } + @Configuration @EnableJpaRepositories(considerNestedRepositories = true) static class TestConfig { @@ -246,4 +246,5 @@ class QueryByExampleDataFetcherJpaTests { return transactionManager; } } + } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/BookRepository.java b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/BookMongoRepository.java similarity index 73% rename from spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/BookRepository.java rename to spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/BookMongoRepository.java index 3e731fc4..a987b91b 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/BookRepository.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/BookMongoRepository.java @@ -4,5 +4,5 @@ import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.graphql.data.GraphQlRepository; @GraphQlRepository -public interface BookRepository extends MongoRepository { +public interface BookMongoRepository extends MongoRepository { } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/ReactiveBookRepository.java b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/BookReactiveMongoRepository.java similarity index 70% rename from spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/ReactiveBookRepository.java rename to spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/BookReactiveMongoRepository.java index e901e741..f4d8fc12 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/ReactiveBookRepository.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/BookReactiveMongoRepository.java @@ -4,5 +4,5 @@ import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.graphql.data.GraphQlRepository; @GraphQlRepository -public interface ReactiveBookRepository extends ReactiveMongoRepository { +public interface BookReactiveMongoRepository extends ReactiveMongoRepository { } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/QueryByExampleDataFetcherMongoDbTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/QueryByExampleDataFetcherMongoDbTests.java index b6a9e7f8..00ee0d33 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/QueryByExampleDataFetcherMongoDbTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/QueryByExampleDataFetcherMongoDbTests.java @@ -64,32 +64,15 @@ import static org.mockito.Mockito.when; */ @ExtendWith(SpringExtension.class) @ContextConfiguration -@Testcontainers +@Testcontainers(disabledWithoutDocker = true) class QueryByExampleDataFetcherMongoDbTests { @Container static MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10")); @Autowired - private BookRepository repository; + private BookMongoRepository repository; - static GraphQlSetup graphQlSetup(String fieldName, DataFetcher fetcher) { - return initGraphQlSetup(null).queryFetcher(fieldName, fetcher); - } - - static GraphQlSetup graphQlSetup(@Nullable QueryByExampleExecutor executor) { - return initGraphQlSetup(executor); - } - - private static GraphQlSetup initGraphQlSetup( - @Nullable QueryByExampleExecutor executor) { - - GraphQLTypeVisitor visitor = QueryByExampleDataFetcher.registrationTypeVisitor( - (executor != null ? Collections.singletonList(executor) : Collections.emptyList()), - Collections.emptyList()); - - return GraphQlSetup.schemaResource(BookSource.schema).typeVisitor(visitor); - } @Test void shouldFetchSingleItems() { @@ -134,7 +117,7 @@ class QueryByExampleDataFetcherMongoDbTests { @Test void shouldFavorExplicitWiring() { - BookRepository mockRepository = mock(BookRepository.class); + BookMongoRepository mockRepository = mock(BookMongoRepository.class); Book book = new Book("42", "Hitchhiker's Guide to the Galaxy", new Author("0", "Douglas", "Adams")); when(mockRepository.findBy(any(), any())).thenReturn(Optional.of(book)); @@ -184,16 +167,35 @@ class QueryByExampleDataFetcherMongoDbTests { assertThat(actualBook.getName()).isEqualTo("The book is: Hitchhiker's Guide to the Galaxy"); } + private static GraphQlSetup graphQlSetup(String fieldName, DataFetcher fetcher) { + return initGraphQlSetup(null).queryFetcher(fieldName, fetcher); + } + + private static GraphQlSetup graphQlSetup(@Nullable QueryByExampleExecutor executor) { + return initGraphQlSetup(executor); + } + + private static GraphQlSetup initGraphQlSetup(@Nullable QueryByExampleExecutor executor) { + + GraphQLTypeVisitor visitor = QueryByExampleDataFetcher.registrationTypeVisitor( + (executor != null ? Collections.singletonList(executor) : Collections.emptyList()), + Collections.emptyList()); + + return GraphQlSetup.schemaResource(BookSource.schema).typeVisitor(visitor); + } + private WebInput input(String query) { return new WebInput(URI.create("/"), new HttpHeaders(), Collections.singletonMap("query", query), null, "1"); } + interface BookProjection { @Value("#{target.name + ' by ' + target.author.firstName + ' ' + target.author.lastName}") String getName(); } + static class BookDto { private final String name; @@ -207,6 +209,7 @@ class QueryByExampleDataFetcherMongoDbTests { } } + @Configuration @EnableMongoRepositories(considerNestedRepositories = true) static class TestConfig { @@ -215,8 +218,8 @@ class QueryByExampleDataFetcherMongoDbTests { MongoTemplate mongoTemplate() { return new MongoTemplate(MongoClients.create(String.format("mongodb://%s:%d", mongoDBContainer.getContainerIpAddress(), - mongoDBContainer.getFirstMappedPort())), - "test"); + mongoDBContainer.getFirstMappedPort())), "test"); } } + } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/QueryByExampleDataFetcherReactiveMongoDbTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/QueryByExampleDataFetcherReactiveMongoDbTests.java index 798a8325..c575d00e 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/QueryByExampleDataFetcherReactiveMongoDbTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/querybyexample/mongodb/QueryByExampleDataFetcherReactiveMongoDbTests.java @@ -60,32 +60,15 @@ import static org.assertj.core.api.Assertions.assertThat; */ @ExtendWith(SpringExtension.class) @ContextConfiguration -@Testcontainers +@Testcontainers(disabledWithoutDocker = true) class QueryByExampleDataFetcherReactiveMongoDbTests { @Container static MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10")); @Autowired - private ReactiveBookRepository repository; + private BookReactiveMongoRepository repository; - static GraphQlSetup graphQlSetup(String fieldName, DataFetcher fetcher) { - return initGraphQlSetup(null).queryFetcher(fieldName, fetcher); - } - - static GraphQlSetup graphQlSetup(@Nullable ReactiveQueryByExampleExecutor executor) { - return initGraphQlSetup(executor); - } - - private static GraphQlSetup initGraphQlSetup( - @Nullable ReactiveQueryByExampleExecutor reactiveExecutor) { - - GraphQLTypeVisitor visitor = QueryByExampleDataFetcher.registrationTypeVisitor( - Collections.emptyList(), - (reactiveExecutor != null ? Collections.singletonList(reactiveExecutor) : Collections.emptyList())); - - return GraphQlSetup.schemaResource(BookSource.schema).typeVisitor(visitor); - } @Test void shouldReactivelyFetchSingleItems() { @@ -156,16 +139,36 @@ class QueryByExampleDataFetcherReactiveMongoDbTests { tester.accept(graphQlSetup(repository)); } + private static GraphQlSetup graphQlSetup(String fieldName, DataFetcher fetcher) { + return initGraphQlSetup(null).queryFetcher(fieldName, fetcher); + } + + private static GraphQlSetup graphQlSetup(@Nullable ReactiveQueryByExampleExecutor executor) { + return initGraphQlSetup(executor); + } + + private static GraphQlSetup initGraphQlSetup(@Nullable ReactiveQueryByExampleExecutor executor) { + + GraphQLTypeVisitor visitor = QueryByExampleDataFetcher.registrationTypeVisitor( + Collections.emptyList(), + (executor != null ? Collections.singletonList(executor) : Collections.emptyList())); + + return GraphQlSetup.schemaResource(BookSource.schema).typeVisitor(visitor); + } + private WebInput input(String query) { return new WebInput(URI.create("/"), new HttpHeaders(), Collections.singletonMap("query", query), null, "1"); } + interface BookProjection { @Value("#{target.name + ' by ' + target.author.firstName + ' ' + target.author.lastName}") String getName(); + } + static class BookDto { private final String name; @@ -177,8 +180,10 @@ class QueryByExampleDataFetcherReactiveMongoDbTests { public String getName() { return "The book is: " + name; } + } + @Configuration @EnableReactiveMongoRepositories(considerNestedRepositories = true) static class TestConfig { @@ -190,5 +195,7 @@ class QueryByExampleDataFetcherReactiveMongoDbTests { mongoDBContainer.getFirstMappedPort())), "test"); } + } + }