diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingDetectionTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingDetectionTests.java index 8a32ab55..a91a3d71 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingDetectionTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingDetectionTests.java @@ -54,15 +54,16 @@ public class BatchMappingDetectionTests { @Test void registerWithDefaultCoordinates() { - Map> map = + Map> dataFetcherMap = initRuntimeWiringBuilder(BookController.class).build().getDataFetchers(); - assertThat(map).containsOnlyKeys("Book"); - assertThat(map.get("Book")).containsOnlyKeys( + assertThat(dataFetcherMap).containsOnlyKeys("Book"); + assertThat(dataFetcherMap.get("Book")).containsOnlyKeys( "authorFlux", "authorList", "authorMonoMap", "authorMap", "authorEnvironment"); DataLoaderRegistry registry = new DataLoaderRegistry(); this.batchLoaderRegistry.registerDataLoaders(registry, GraphQLContext.newContext().build()); + assertThat(registry.getDataLoadersMap()).containsOnlyKeys( "Book.authorFlux", "Book.authorList", "Book.authorMonoMap", "Book.authorMap", "Book.authorEnvironment"); } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java index d57f7845..3fbc4085 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingInvocationTests.java @@ -15,11 +15,6 @@ */ package org.springframework.graphql.data.method.annotation.support; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -27,8 +22,6 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; import graphql.ExecutionResult; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -37,17 +30,10 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; import org.springframework.graphql.GraphQlResponse; -import org.springframework.graphql.GraphQlService; -import org.springframework.graphql.GraphQlSetup; import org.springframework.graphql.RequestInput; import org.springframework.graphql.data.method.annotation.BatchMapping; -import org.springframework.graphql.data.method.annotation.QueryMapping; -import org.springframework.graphql.execution.BatchLoaderRegistry; -import org.springframework.graphql.execution.DefaultBatchLoaderRegistry; import org.springframework.graphql.execution.ExecutionGraphQlService; -import org.springframework.lang.Nullable; import org.springframework.stereotype.Controller; import static org.assertj.core.api.Assertions.assertThat; @@ -60,42 +46,7 @@ import static org.junit.jupiter.params.provider.Arguments.arguments; * @author Rossen Stoyanchev */ @SuppressWarnings("unused") -public class BatchMappingInvocationTests { - - private static final Map courseMap = new HashMap<>(); - - private static final Map personMap = new HashMap<>(); - - static { - Course.save(11L, "Ethical Hacking", 15L, Arrays.asList(22L, 26L, 31L)); - Course.save(19L, "Docker and Kubernetes", 17L, Arrays.asList(31L, 39L, 44L, 45L)); - - Person.save(15L, "Josh", "Kelly"); - Person.save(17L, "Albert", "Murray"); - Person.save(22L, "Bonnie", "Gray"); - Person.save(26L, "John", "Perry"); - Person.save(31L, "Alaine", "Baily"); - Person.save(39L, "Jeff", "Peterson"); - Person.save(44L, "Jared", "Mccarthy"); - Person.save(45L, "Benjamin", "Brown"); - } - - private static final String schema = "" + - "type Query {" + - " courses: [Course]" + - "}" + - "type Course {" + - " id: ID" + - " name: String" + - " instructor: Person" + - " students: [Person]" + - "}" + - "type Person {" + - " id: ID" + - " firstName: String" + - " lastName: String" + - "}"; - +public class BatchMappingInvocationTests extends BatchMappingTestSupport { private static Stream controllerClasses() { return Stream.of( @@ -106,6 +57,7 @@ public class BatchMappingInvocationTests { ); } + @ParameterizedTest @MethodSource("controllerClasses") void oneToOne(Class controllerClass) { @@ -121,7 +73,7 @@ public class BatchMappingInvocationTests { " }" + "}"; - Mono resultMono = graphQlService(controllerClass, CourseConfig.class) + Mono resultMono = graphQlService(controllerClass) .execute(new RequestInput(query, null, null, null)); List actualCourses = GraphQlResponse.from(resultMono).toList("courses", Course.class); @@ -154,7 +106,7 @@ public class BatchMappingInvocationTests { " }" + "}"; - Mono resultMono = graphQlService(controllerClass, CourseConfig.class) + Mono resultMono = graphQlService(controllerClass) .execute(new RequestInput(query, null, null, null)); List actualCourses = GraphQlResponse.from(resultMono).toList("courses", Course.class); @@ -180,27 +132,19 @@ public class BatchMappingInvocationTests { private ExecutionGraphQlService graphQlService(Class... configClasses) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(configClasses); + applicationContext.register(BatchMappingTestSupport.CourseConfig.class); applicationContext.refresh(); return applicationContext.getBean(ExecutionGraphQlService.class); } - private static class CourseController { - - @QueryMapping - public Collection courses() { - return courseMap.values(); - } - } - @Controller private static class BatchMonoMapController extends CourseController { @BatchMapping public Mono> instructor(List courses) { - return Flux.fromIterable(Course.allCourses()) - .collect(Collectors.toMap(Function.identity(), Course::instructor)); + return Flux.fromIterable(courses).collect(Collectors.toMap(Function.identity(), Course::instructor)); } @BatchMapping @@ -214,8 +158,7 @@ public class BatchMappingInvocationTests { @BatchMapping public Map instructor(List courses) { - return Course.allCourses().stream().collect( - Collectors.toMap(Function.identity(), Course::instructor)); + return courses.stream().collect(Collectors.toMap(Function.identity(), Course::instructor)); } @BatchMapping @@ -252,143 +195,4 @@ public class BatchMappingInvocationTests { } } - - private static class CourseConfig { - - @Bean - public GraphQlService graphQlService(AnnotatedControllerConfigurer configurer, BatchLoaderRegistry registry) { - return GraphQlSetup.schemaContent(schema) - .runtimeWiring(configurer) - .dataLoaders(registry) - .toGraphQlService(); - } - - @Bean - public AnnotatedControllerConfigurer annotatedDataFetcherConfigurer() { - return new AnnotatedControllerConfigurer(); - } - - @Bean - public BatchLoaderRegistry batchLoaderRegistry() { - return new DefaultBatchLoaderRegistry(); - } - } - - - private static class Course { - - private final Long id; - - private final String name; - - private final Long instructorId; - - private final List studentIds; - - @JsonCreator - public Course( - @JsonProperty("id") Long id, @JsonProperty("name") String name, - @JsonProperty("instructor") @Nullable Person instructor, - @JsonProperty("students") @Nullable List students) { - - this.id = id; - this.name = name; - this.instructorId = (instructor != null ? instructor.id() : -1); - this.studentIds = (students != null ? - students.stream().map(Person::id).collect(Collectors.toList()) : - Collections.emptyList()); - } - - public Course(Long id, String name, Long instructorId, List studentIds) { - this.id = id; - this.name = name; - this.instructorId = instructorId; - this.studentIds = studentIds; - } - - public String name() { - return this.name; - } - - public Long instructorId() { - return this.instructorId; - } - - public List studentIds() { - return this.studentIds; - } - - public List students() { - return this.studentIds.stream().map(personMap::get).collect(Collectors.toList()); - } - - public Person instructor() { - return personMap.get(this.instructorId); - } - - public static void save(Long id, String name, Long instructorId, List studentIds) { - Course course = new Course(id, name, instructorId, studentIds); - courseMap.put(id, course); - } - - public static List allCourses() { - return new ArrayList<>(courseMap.values()); - } - - // Course is a key in the DataLoader map - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (other == null || getClass() != other.getClass()) { - return false; - } - return this.id.equals(((Course) other).id); - } - - @Override - public int hashCode() { - return this.id.hashCode(); - } - } - - private static class Person { - - private final Long id; - - private final String firstName; - - private final String lastName; - - @JsonCreator - public Person( - @JsonProperty("id") Long id, - @JsonProperty("firstName") String firstName, - @JsonProperty("lastName") String lastName) { - - this.id = id; - this.firstName = firstName; - this.lastName = lastName; - } - - public Long id() { - return this.id; - } - - public String firstName() { - return this.firstName; - } - - public String lastName() { - return this.lastName; - } - - public static void save(Long id, String firstName, String lastName) { - Person person = new Person(id, firstName, lastName); - personMap.put(id, person); - } - } - } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingTestSupport.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingTestSupport.java new file mode 100644 index 00000000..cb357398 --- /dev/null +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/BatchMappingTestSupport.java @@ -0,0 +1,231 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.graphql.data.method.annotation.support; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.springframework.context.annotation.Bean; +import org.springframework.graphql.GraphQlService; +import org.springframework.graphql.GraphQlSetup; +import org.springframework.graphql.data.method.annotation.QueryMapping; +import org.springframework.graphql.execution.BatchLoaderRegistry; +import org.springframework.graphql.execution.DefaultBatchLoaderRegistry; +import org.springframework.lang.Nullable; + +/** + * Support class for {@code @BatchMapping}, and other batch loading tests, that + * provides a suitable schema, domain types, mock data, and configuration. + * + * @author Rossen Stoyanchev + */ +@SuppressWarnings("unused") +public class BatchMappingTestSupport { + + static final Map courseMap = new HashMap<>(); + + static final Map personMap = new HashMap<>(); + + static { + Course.save(11L, "Ethical Hacking", 15L, Arrays.asList(22L, 26L, 31L)); + Course.save(19L, "Docker and Kubernetes", 17L, Arrays.asList(31L, 39L, 44L, 45L)); + + Person.save(15L, "Josh", "Kelly"); + Person.save(17L, "Albert", "Murray"); + Person.save(22L, "Bonnie", "Gray"); + Person.save(26L, "John", "Perry"); + Person.save(31L, "Alaine", "Baily"); + Person.save(39L, "Jeff", "Peterson"); + Person.save(44L, "Jared", "Mccarthy"); + Person.save(45L, "Benjamin", "Brown"); + } + + static final String schema = "" + + "type Query {" + + " courses: [Course]" + + "}" + + "type Course {" + + " id: ID" + + " name: String" + + " instructor: Person" + + " students: [Person]" + + "}" + + "type Person {" + + " id: ID" + + " firstName: String" + + " lastName: String" + + "}"; + + + + static class Course { + + private final Long id; + + private final String name; + + private final Long instructorId; + + private final List studentIds; + + @JsonCreator + public Course( + @JsonProperty("id") Long id, @JsonProperty("name") String name, + @JsonProperty("instructor") @Nullable Person instructor, + @JsonProperty("students") @Nullable List students) { + + this.id = id; + this.name = name; + this.instructorId = (instructor != null ? instructor.id() : -1); + this.studentIds = (students != null ? + students.stream().map(Person::id).collect(Collectors.toList()) : + Collections.emptyList()); + } + + public Course(Long id, String name, Long instructorId, List studentIds) { + this.id = id; + this.name = name; + this.instructorId = instructorId; + this.studentIds = studentIds; + } + + public String name() { + return this.name; + } + + public Long instructorId() { + return this.instructorId; + } + + public List studentIds() { + return this.studentIds; + } + + public List students() { + return this.studentIds.stream().map(personMap::get).collect(Collectors.toList()); + } + + public Person instructor() { + return personMap.get(this.instructorId); + } + + public static void save(Long id, String name, Long instructorId, List studentIds) { + Course course = new Course(id, name, instructorId, studentIds); + courseMap.put(id, course); + } + + public static List allCourses() { + return new ArrayList<>(courseMap.values()); + } + + // Course is a key in the DataLoader map + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + return this.id.equals(((Course) other).id); + } + + @Override + public int hashCode() { + return this.id.hashCode(); + } + } + + + static class Person { + + private final Long id; + + private final String firstName; + + private final String lastName; + + @JsonCreator + public Person( + @JsonProperty("id") Long id, + @JsonProperty("firstName") String firstName, + @JsonProperty("lastName") String lastName) { + + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + } + + public Long id() { + return this.id; + } + + public String firstName() { + return this.firstName; + } + + public String lastName() { + return this.lastName; + } + + public static void save(Long id, String firstName, String lastName) { + Person person = new Person(id, firstName, lastName); + personMap.put(id, person); + } + } + + + static class CourseController { + + @QueryMapping + public Collection courses() { + return courseMap.values(); + } + } + + + static class CourseConfig { + + @Bean + public GraphQlService graphQlService(AnnotatedControllerConfigurer configurer, BatchLoaderRegistry registry) { + return GraphQlSetup.schemaContent(schema) + .runtimeWiring(configurer) + .dataLoaders(registry) + .toGraphQlService(); + } + + @Bean + public AnnotatedControllerConfigurer annotatedDataFetcherConfigurer() { + return new AnnotatedControllerConfigurer(); + } + + @Bean + public BatchLoaderRegistry batchLoaderRegistry() { + return new DefaultBatchLoaderRegistry(); + } + } + +}