diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractDelegatingGraphQlTester.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractDelegatingGraphQlTester.java index b1e0248f..ac410321 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractDelegatingGraphQlTester.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractDelegatingGraphQlTester.java @@ -40,12 +40,12 @@ public abstract class AbstractDelegatingGraphQlTester implements GraphQlTester { @Override - public RequestSpec document(String document) { + public Request document(String document) { return this.delegate.document(document); } @Override - public RequestSpec documentName(String documentName) { + public Request documentName(String documentName) { return this.delegate.documentName(documentName); } diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java index f21d9ad9..5cae4842 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java @@ -90,12 +90,12 @@ final class DefaultGraphQlTester implements GraphQlTester { @Override - public RequestSpec document(String document) { - return new DefaultRequestSpec(document); + public Request document(String document) { + return new DefaultRequest(document); } @Override - public RequestSpec documentName(String documentName) { + public Request documentName(String documentName) { String document = this.documentSource.getDocument(documentName).block(this.responseTimeout); Assert.notNull(document, "Expected document content or an error"); return document(document); @@ -129,9 +129,9 @@ final class DefaultGraphQlTester implements GraphQlTester { /** - * {@link RequestSpec} that gathers the document, operationName, and variables. + * {@link Request} that gathers the document, operationName, and variables. */ - private final class DefaultRequestSpec implements RequestSpec { + private final class DefaultRequest implements Request { private final String document; @@ -140,26 +140,26 @@ final class DefaultGraphQlTester implements GraphQlTester { private final Map variables = new LinkedHashMap<>(); - private DefaultRequestSpec(String document) { + private DefaultRequest(String document) { Assert.notNull(document, "`document` is required"); this.document = document; } @Override - public DefaultRequestSpec operationName(@Nullable String name) { + public DefaultRequest operationName(@Nullable String name) { this.operationName = name; return this; } @Override - public DefaultRequestSpec variable(String name, @Nullable Object value) { + public DefaultRequest variable(String name, @Nullable Object value) { this.variables.put(name, value); return this; } @SuppressWarnings("ConstantConditions") @Override - public ResponseSpec execute() { + public Response execute() { GraphQlRequest request = createRequest(); return transport.execute(request) .map(result -> createResponseSpec(result, assertDecorator(request))) @@ -172,7 +172,7 @@ final class DefaultGraphQlTester implements GraphQlTester { } @Override - public SubscriptionSpec executeSubscription() { + public Subscription executeSubscription() { GraphQlRequest request = createRequest(); return () -> transport.executeSubscription(request) .map(result -> createResponseSpec(result, assertDecorator(request))); @@ -182,11 +182,11 @@ final class DefaultGraphQlTester implements GraphQlTester { return new GraphQlRequest(this.document, this.operationName, this.variables); } - private GraphQlTester.ResponseSpec createResponseSpec( + private Response createResponseSpec( ExecutionResult result, Consumer assertDecorator) { DocumentContext jsonDocument = JsonPath.parse(result.toSpecification(), jsonPathConfig); - return new DefaultResponseSpec(jsonDocument, errorFilter, assertDecorator); + return new DefaultResponse(jsonDocument, errorFilter, assertDecorator); } private Consumer assertDecorator(GraphQlRequest request) { @@ -307,13 +307,13 @@ final class DefaultGraphQlTester implements GraphQlTester { /** - * {@link ResponseSpec} that operates on the response from a GraphQL HTTP request. + * Default {@link GraphQlTester.Response} implementation. */ - private static final class DefaultResponseSpec implements ResponseSpec, ErrorSpec { + private static final class DefaultResponse implements Response, Errors { private final ResponseContainer responseContainer; - private DefaultResponseSpec( + private DefaultResponse( DocumentContext documentContext, @Nullable Predicate errorFilter, Consumer assertDecorator) { @@ -321,36 +321,36 @@ final class DefaultGraphQlTester implements GraphQlTester { } @Override - public PathSpec path(String path) { + public Path path(String path) { this.responseContainer.verifyErrors(); - return new DefaultPathSpec(path, this.responseContainer); + return new DefaultPath(path, this.responseContainer); } @Override - public ErrorSpec errors() { + public Errors errors() { return this; } @Override - public ErrorSpec filter(Predicate predicate) { + public Errors filter(Predicate predicate) { this.responseContainer.filterErrors(predicate); return this; } @Override - public ErrorSpec expect(Predicate predicate) { + public Errors expect(Predicate predicate) { this.responseContainer.expectErrors(predicate); return this; } @Override - public TraverseSpec verify() { + public Traversable verify() { this.responseContainer.verifyErrors(); return this; } @Override - public TraverseSpec satisfy(Consumer> consumer) { + public Traversable satisfy(Consumer> consumer) { this.responseContainer.consumeErrors(consumer); return this; } @@ -358,9 +358,9 @@ final class DefaultGraphQlTester implements GraphQlTester { } /** - * {@link PathSpec} implementation. + * Default {@link GraphQlTester.Path} implementation. */ - private static final class DefaultPathSpec implements PathSpec { + private static final class DefaultPath implements Path { private final String inputPath; @@ -370,7 +370,7 @@ final class DefaultGraphQlTester implements GraphQlTester { private final JsonPathExpectationsHelper pathHelper; - private DefaultPathSpec(String path, ResponseContainer responseContainer) { + private DefaultPath(String path, ResponseContainer responseContainer) { Assert.notNull(path, "`path` is required"); Assert.notNull(responseContainer, "ResponseContainer is required"); this.inputPath = path; @@ -390,78 +390,78 @@ final class DefaultGraphQlTester implements GraphQlTester { } @Override - public PathSpec path(String path) { - return new DefaultPathSpec(path, this.responseContainer); + public Path path(String path) { + return new DefaultPath(path, this.responseContainer); } @Override - public PathSpec pathExists() { + public Path pathExists() { this.responseContainer.doAssert(() -> this.pathHelper.hasJsonPath(this.responseContainer.jsonContent())); return this; } @Override - public PathSpec pathDoesNotExist() { + public Path pathDoesNotExist() { this.responseContainer.doAssert(() -> this.pathHelper.doesNotHaveJsonPath(this.responseContainer.jsonContent())); return this; } @Override - public PathSpec valueExists() { + public Path valueExists() { this.responseContainer.doAssert(() -> this.pathHelper.exists(this.responseContainer.jsonContent())); return this; } @Override - public PathSpec valueDoesNotExist() { + public Path valueDoesNotExist() { this.responseContainer.doAssert(() -> this.pathHelper.doesNotExist(this.responseContainer.jsonContent())); return this; } @Override - public PathSpec valueIsEmpty() { + public Path valueIsEmpty() { this.responseContainer.doAssert(() -> this.pathHelper.assertValueIsEmpty(this.responseContainer.jsonContent())); return this; } @Override - public PathSpec valueIsNotEmpty() { + public Path valueIsNotEmpty() { this.responseContainer.doAssert(() -> this.pathHelper.assertValueIsNotEmpty(this.responseContainer.jsonContent())); return this; } @Override - public EntitySpec entity(Class entityType) { + public Entity entity(Class entityType) { D entity = this.responseContainer.read(this.jsonPath, new TypeRefAdapter<>(entityType)); - return new DefaultEntitySpec<>(entity, this.responseContainer, this.inputPath); + return new DefaultEntity<>(entity, this.responseContainer, this.inputPath); } @Override - public EntitySpec entity(ParameterizedTypeReference entityType) { + public Entity entity(ParameterizedTypeReference entityType) { D entity = this.responseContainer.read(this.jsonPath, new TypeRefAdapter<>(entityType)); - return new DefaultEntitySpec<>(entity, this.responseContainer, this.inputPath); + return new DefaultEntity<>(entity, this.responseContainer, this.inputPath); } @Override - public ListEntitySpec entityList(Class elementType) { + public EntityList entityList(Class elementType) { List entity = this.responseContainer.read(this.jsonPath, new TypeRefAdapter<>(List.class, elementType)); - return new DefaultListEntitySpec<>(entity, this.responseContainer, this.inputPath); + return new DefaultEntityList<>(entity, this.responseContainer, this.inputPath); } @Override - public ListEntitySpec entityList(ParameterizedTypeReference elementType) { + public EntityList entityList(ParameterizedTypeReference elementType) { List entity = this.responseContainer.read(this.jsonPath, new TypeRefAdapter<>(List.class, elementType)); - return new DefaultListEntitySpec<>(entity, this.responseContainer, this.inputPath); + return new DefaultEntityList<>(entity, this.responseContainer, this.inputPath); } @Override - public PathSpec matchesJson(String expectedJson) { + public Path matchesJson(String expectedJson) { matchesJson(expectedJson, false); return this; } @Override - public PathSpec matchesJsonStrictly(String expectedJson) { + public Path matchesJsonStrictly(String expectedJson) { matchesJson(expectedJson, true); return this; } @@ -486,9 +486,9 @@ final class DefaultGraphQlTester implements GraphQlTester { /** - * {@link EntitySpec} implementation. + * Default {@link GraphQlTester.Entity} implementation. */ - private static class DefaultEntitySpec> implements EntitySpec { + private static class DefaultEntity> implements Entity { private final D entity; @@ -496,7 +496,7 @@ final class DefaultGraphQlTester implements GraphQlTester { private final String inputPath; - protected DefaultEntitySpec(D entity, ResponseContainer responseContainer, String path) { + protected DefaultEntity(D entity, ResponseContainer responseContainer, String path) { this.entity = entity; this.responseContainer = responseContainer; this.inputPath = path; @@ -515,8 +515,8 @@ final class DefaultGraphQlTester implements GraphQlTester { } @Override - public PathSpec path(String path) { - return new DefaultPathSpec(path, this.responseContainer); + public Path path(String path) { + return new DefaultPath(path, this.responseContainer); } @Override @@ -569,18 +569,18 @@ final class DefaultGraphQlTester implements GraphQlTester { /** - * {@link ListEntitySpec} implementation. + * Default {@link EntityList} implementation. */ - private static final class DefaultListEntitySpec extends DefaultEntitySpec, ListEntitySpec> - implements ListEntitySpec { + private static final class DefaultEntityList extends DefaultEntity, EntityList> + implements EntityList { - private DefaultListEntitySpec(List entity, ResponseContainer responseContainer, String path) { + private DefaultEntityList(List entity, ResponseContainer responseContainer, String path) { super(entity, responseContainer, path); } @Override @SuppressWarnings("unchecked") - public ListEntitySpec contains(E... elements) { + public EntityList contains(E... elements) { doAssert(() -> { List expected = Arrays.asList(elements); AssertionErrors.assertTrue("List at path '" + getInputPath() + "' does not contain " + expected, @@ -591,7 +591,7 @@ final class DefaultGraphQlTester implements GraphQlTester { @Override @SuppressWarnings("unchecked") - public ListEntitySpec doesNotContain(E... elements) { + public EntityList doesNotContain(E... elements) { doAssert(() -> { List expected = Arrays.asList(elements); AssertionErrors.assertTrue( @@ -603,7 +603,7 @@ final class DefaultGraphQlTester implements GraphQlTester { @Override @SuppressWarnings("unchecked") - public ListEntitySpec containsExactly(E... elements) { + public EntityList containsExactly(E... elements) { doAssert(() -> { List expected = Arrays.asList(elements); AssertionErrors.assertTrue( @@ -614,14 +614,14 @@ final class DefaultGraphQlTester implements GraphQlTester { } @Override - public ListEntitySpec hasSize(int size) { + public EntityList hasSize(int size) { doAssert(() -> AssertionErrors.assertTrue("List at path '" + getInputPath() + "' should have size " + size, getEntity().size() == size)); return this; } @Override - public ListEntitySpec hasSizeLessThan(int boundary) { + public EntityList hasSizeLessThan(int boundary) { doAssert(() -> AssertionErrors.assertTrue( "List at path '" + getInputPath() + "' should have size less than " + boundary, getEntity().size() < boundary)); @@ -629,7 +629,7 @@ final class DefaultGraphQlTester implements GraphQlTester { } @Override - public ListEntitySpec hasSizeGreaterThan(int boundary) { + public EntityList hasSizeGreaterThan(int boundary) { doAssert(() -> AssertionErrors.assertTrue( "List at path '" + getInputPath() + "' should have size greater than " + boundary, getEntity().size() > boundary)); diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultWebSocketGraphQlTester.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultWebSocketGraphQlTester.java index 00735f1c..42b2339f 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultWebSocketGraphQlTester.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultWebSocketGraphQlTester.java @@ -151,7 +151,7 @@ final class DefaultWebSocketGraphQlTester extends AbstractDelegatingGraphQlTeste .operationName(request.getOperationName()) .variables(request.getVariables()) .execute() - .map(GraphQlClient.ResponseSpec::andReturn); + .map(GraphQlClient.Response::andReturn); } @Override @@ -160,7 +160,7 @@ final class DefaultWebSocketGraphQlTester extends AbstractDelegatingGraphQlTeste .document(request.getDocument()) .operationName(request.getOperationName()) .variables(request.getVariables()) - .executeSubscription().map(GraphQlClient.ResponseSpec::andReturn); + .executeSubscription().map(GraphQlClient.Response::andReturn); } }; } diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java index 72aa413e..392ed9b7 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java @@ -60,7 +60,7 @@ public interface GraphQlTester { * @return spec for response assertions * @throws AssertionError if the response status is not 200 (OK) */ - RequestSpec document(String document); + Request document(String document); /** * Variant of {@link #document(String)} that uses the given key to resolve @@ -70,7 +70,7 @@ public interface GraphQlTester { * @throws IllegalArgumentException if the documentName cannot be resolved * @throws AssertionError if the response status is not 200 (OK) */ - RequestSpec documentName(String documentName); + Request documentName(String documentName); /** * Create a builder initialized from the configuration of "this" tester. @@ -98,7 +98,7 @@ public interface GraphQlTester { interface Builder> { /** - * Configure a global {@link ErrorSpec#filter(Predicate) filter} that + * Configure a global {@link Errors#filter(Predicate) filter} that * applies to all requests. * @param predicate the error filter to add * @return the same builder instance @@ -126,40 +126,10 @@ public interface GraphQlTester { GraphQlTester build(); } - /** - * Declare options to perform a GraphQL request. - */ - interface ExecuteSpec { - - /** - * Execute the GraphQL request and return a spec for further inspection of - * response data and errors. - * @return options for asserting the response - * @throws AssertionError if the request is performed over HTTP and the response - * status is not 200 (OK). - */ - ResponseSpec execute(); - - /** - * Execute the GraphQL request and verify the response contains no errors. - */ - void executeAndVerify(); - - /** - * Execute the GraphQL request as a subscription and return a spec with options to - * transform the result stream. - * @return spec with options to transform the subscription result stream - * @throws AssertionError if the request is performed over HTTP and the response - * status is not 200 (OK). - */ - SubscriptionSpec executeSubscription(); - - } - /** * Declare options to gather input for a GraphQL request and execute it. */ - interface RequestSpec> extends ExecuteSpec { + interface Request> { /** * Set the operation name. @@ -177,12 +147,35 @@ public interface GraphQlTester { */ T variable(String name, @Nullable Object value); + /** + * Execute the GraphQL request and return a spec for further inspection of + * response data and errors. + * @return options for asserting the response + * @throws AssertionError if the request is performed over HTTP and the response + * status is not 200 (OK). + */ + Response execute(); + + /** + * Execute the GraphQL request and verify the response contains no errors. + */ + void executeAndVerify(); + + /** + * Execute the GraphQL request as a subscription and return a spec with options to + * transform the result stream. + * @return spec with options to transform the subscription result stream + * @throws AssertionError if the request is performed over HTTP and the response + * status is not 200 (OK). + */ + Subscription executeSubscription(); + } /** * Declare options to switch to different part of the GraphQL response. */ - interface TraverseSpec { + interface Traversable { /** * Switch to a path under the "data" section of the GraphQL response. The path can @@ -193,16 +186,16 @@ public interface GraphQlTester { * @return spec for asserting the content under the given path * @throws AssertionError if the GraphQL response contains * errors that have - * not be checked via {@link ResponseSpec#errors()} + * not be checked via {@link Response#errors()} */ - PathSpec path(String path); + Path path(String path); } /** * Declare options to check the data and errors of a GraphQL response. */ - interface ResponseSpec extends TraverseSpec { + interface Response extends Traversable { /** * Return a spec to filter out or inspect errors. This must be used before @@ -210,39 +203,39 @@ public interface GraphQlTester { * be filtered out. * @return the error spec */ - ErrorSpec errors(); + Errors errors(); } /** * Declare options available to assert data at a given path. */ - interface PathSpec extends TraverseSpec { + interface Path extends Traversable { /** * Assert the given path exists, even if the value is {@code null}. * @return spec to assert the converted entity with */ - PathSpec pathExists(); + Path pathExists(); /** * Assert the given path does not {@link #pathExists() exist}. * @return spec to assert the converted entity with */ - PathSpec pathDoesNotExist(); + Path pathDoesNotExist(); /** * Assert a value exists at the given path where the value is any {@code non-null} * value, possibly an empty array or map. * @return spec to assert the converted entity with */ - PathSpec valueExists(); + Path valueExists(); /** * Assert a value does not {@link #valueExists() exist} at the given path. * @return spec to assert the converted entity with */ - PathSpec valueDoesNotExist(); + Path valueDoesNotExist(); /** * Assert the value at the given path does exist but is empty as defined @@ -250,13 +243,13 @@ public interface GraphQlTester { * @return spec to assert the converted entity with * @see org.springframework.util.ObjectUtils#isEmpty(Object) */ - PathSpec valueIsEmpty(); + Path valueIsEmpty(); /** * Assert the value at the given path is not {@link #valueIsEmpty() empty}. * @return spec to assert the converted entity with */ - PathSpec valueIsNotEmpty(); + Path valueIsNotEmpty(); /** * Convert the data at the given path to the target type. @@ -264,7 +257,7 @@ public interface GraphQlTester { * @param the target entity type * @return spec to assert the converted entity with */ - EntitySpec entity(Class entityType); + Entity entity(Class entityType); /** * Convert the data at the given path to the target type. @@ -272,7 +265,7 @@ public interface GraphQlTester { * @param the target entity type * @return spec to assert the converted entity with */ - EntitySpec entity(ParameterizedTypeReference entityType); + Entity entity(ParameterizedTypeReference entityType); /** * Convert the data at the given path to a List of the target type. @@ -280,7 +273,7 @@ public interface GraphQlTester { * @param the target entity type * @return spec to assert the converted List of entities with */ - ListEntitySpec entityList(Class elementType); + EntityList entityList(Class elementType); /** * Convert the data at the given path to a List of the target type. @@ -288,7 +281,7 @@ public interface GraphQlTester { * @param the target entity type * @return spec to assert the converted List of entities with */ - ListEntitySpec entityList(ParameterizedTypeReference elementType); + EntityList entityList(ParameterizedTypeReference elementType); /** * Parse the JSON at the given path and the given expected JSON and assert that @@ -302,7 +295,7 @@ public interface GraphQlTester { * @see org.springframework.test.util.JsonExpectationsHelper#assertJsonEqual(String, * String) */ - TraverseSpec matchesJson(String expectedJson); + Traversable matchesJson(String expectedJson); /** * Parse the JSON at the given path and the given expected JSON and assert that @@ -314,7 +307,7 @@ public interface GraphQlTester { * @see org.springframework.test.util.JsonExpectationsHelper#assertJsonEqual(String, * String, boolean) */ - TraverseSpec matchesJsonStrictly(String expectedJson); + Traversable matchesJsonStrictly(String expectedJson); } @@ -324,7 +317,7 @@ public interface GraphQlTester { * @param the entity type * @param the spec type, including subtypes */ - interface EntitySpec> extends TraverseSpec { + interface Entity> extends Traversable { /** * Assert the converted entity equals the given Object. @@ -383,12 +376,12 @@ public interface GraphQlTester { } /** - * Extension of {@link EntitySpec} with options available to assert data converted to + * Extension of {@link Entity} with options available to assert data converted to * a List of entities. * * @param the type of elements in the list */ - interface ListEntitySpec extends EntitySpec, ListEntitySpec> { + interface EntityList extends Entity, EntityList> { /** * Assert the list contains the given elements. @@ -396,7 +389,7 @@ public interface GraphQlTester { * @return the same spec for more assertions */ @SuppressWarnings("unchecked") - ListEntitySpec contains(E... elements); + EntityList contains(E... elements); /** * Assert the list does not contain the given elements. @@ -404,7 +397,7 @@ public interface GraphQlTester { * @return the same spec for more assertions */ @SuppressWarnings("unchecked") - ListEntitySpec doesNotContain(E... elements); + EntityList doesNotContain(E... elements); /** * Assert the list contains the given elements. @@ -412,28 +405,28 @@ public interface GraphQlTester { * @return the same spec for more assertions */ @SuppressWarnings("unchecked") - ListEntitySpec containsExactly(E... elements); + EntityList containsExactly(E... elements); /** * Assert the list contains the specified number of elements. * @param size the number of elements expected * @return the same spec for more assertions */ - ListEntitySpec hasSize(int size); + EntityList hasSize(int size); /** * Assert the list contains fewer elements than the specified number. * @param boundary the number to compare the number of elements to * @return the same spec for more assertions */ - ListEntitySpec hasSizeLessThan(int boundary); + EntityList hasSizeLessThan(int boundary); /** * Assert the list contains more elements than the specified number. * @param boundary the number to compare the number of elements to * @return the same spec for more assertions */ - ListEntitySpec hasSizeGreaterThan(int boundary); + EntityList hasSizeGreaterThan(int boundary); } @@ -441,7 +434,7 @@ public interface GraphQlTester { * Declare options to filter out expected errors or inspect all errors and verify * there are no unexpected errors. */ - interface ErrorSpec { + interface Errors { /** * Use this to filter out errors that are expected and can be ignored. @@ -449,18 +442,18 @@ public interface GraphQlTester { * with the data. *

The configured filters are applied to all errors. Those that match * are treated as expected and are ignored on {@link #verify()} or when - * {@link TraverseSpec#path(String) traversing} to a data path. + * {@link Traversable#path(String) traversing} to a data path. *

In contrast to {@link #expect(Predicate)}, filters do not have to * match any errors, and don't imply that the errors must be present. * @param errorPredicate the error filter to add * @return the same spec to add more filters before {@link #verify()} */ - ErrorSpec filter(Predicate errorPredicate); + Errors filter(Predicate errorPredicate); /** * Use this to declare errors that are expected. *

Errors that match are treated as expected and are ignored on - * {@link #verify()} or when {@link TraverseSpec#path(String) traversing} + * {@link #verify()} or when {@link Traversable#path(String) traversing} * to a data path. *

In contrast to {@link #filter(Predicate)}, use of this option * does imply that errors are present or else an {@link AssertionError} @@ -468,30 +461,30 @@ public interface GraphQlTester { * @param errorPredicate the predicate for the expected error * @return the same spec to add more filters or expected errors */ - ErrorSpec expect(Predicate errorPredicate); + Errors expect(Predicate errorPredicate); /** * Verify there are either no errors or that there no unexpected errors that have * not been {@link #filter(Predicate) filtered out}. * @return a spec to switch to a data path */ - TraverseSpec verify(); + Traversable verify(); /** * Inspect errors in * the response, if any. Use of this method effectively suppresses all errors and - * allows {@link TraverseSpec#path(String) traversing} to a data path. + * allows {@link Traversable#path(String) traversing} to a data path. * @param errorsConsumer to inspect errors with * @return a spec to switch to a data path */ - TraverseSpec satisfy(Consumer> errorsConsumer); + Traversable satisfy(Consumer> errorsConsumer); } /** * Declare options available to assert a GraphQL Subscription response. */ - interface SubscriptionSpec { + interface Subscription { /** * Return a {@link Flux} of entities converted from some part of the data in each @@ -507,12 +500,12 @@ public interface GraphQlTester { } /** - * Return a {@link Flux} of {@link ResponseSpec} instances, each representing an + * Return a {@link Flux} of {@link Response} instances, each representing an * individual subscription event. * @return a {@code Flux} of {@code ResponseSpec} instances that can be further * inspected, e.g. with {@code reactor.test.StepVerifier} */ - Flux toFlux(); + Flux toFlux(); } diff --git a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java index abb6b9ea..9ebf9964 100644 --- a/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java +++ b/spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java @@ -45,11 +45,11 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { String document = "{me {name, friends}}"; setMockResponse("{\"me\": {\"name\":\"Luke Skywalker\", \"friends\":[]}}"); - GraphQlTester.ResponseSpec spec = graphQlTester().document(document).execute(); + GraphQlTester.Response response = graphQlTester().document(document).execute(); - spec.path("me.name").pathExists().valueExists(); - spec.path("me.friends").pathExists().valueExists(); - spec.path("hero").pathDoesNotExist().valueDoesNotExist(); + response.path("me.name").pathExists().valueExists(); + response.path("me.friends").pathExists().valueExists(); + response.path("hero").pathDoesNotExist().valueDoesNotExist(); assertThat(requestInput().getDocument()).contains(document); } @@ -60,12 +60,12 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { String document = "{me {name, friends}}"; setMockResponse("{\"me\": {\"name\":null, \"friends\":[]}}"); - GraphQlTester.ResponseSpec spec = graphQlTester().document(document).execute(); + GraphQlTester.Response response = graphQlTester().document(document).execute(); - spec.path("me.name").valueIsEmpty(); - spec.path("me.friends").valueIsEmpty(); + response.path("me.name").valueIsEmpty(); + response.path("me.friends").valueIsEmpty(); - assertThatThrownBy(() -> spec.path("hero").valueIsEmpty()) + assertThatThrownBy(() -> response.path("hero").valueIsEmpty()) .as("Path does not even exist") .hasMessageContaining("No value at JSON path \"$['data']['hero']"); @@ -78,14 +78,14 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { String document = "{me {name}}"; setMockResponse("{\"me\": {\"name\":\"Luke Skywalker\", \"friends\":[]}}"); - GraphQlTester.ResponseSpec spec = graphQlTester().document(document).execute(); + GraphQlTester.Response response = graphQlTester().document(document).execute(); - spec.path("").matchesJson("{\"me\": {\"name\":\"Luke Skywalker\",\"friends\":[]}}"); - spec.path("me").matchesJson("{\"name\":\"Luke Skywalker\"}"); - spec.path("me").matchesJson("{\"friends\":[]}"); // lenient match with subset of + response.path("").matchesJson("{\"me\": {\"name\":\"Luke Skywalker\",\"friends\":[]}}"); + response.path("me").matchesJson("{\"name\":\"Luke Skywalker\"}"); + response.path("me").matchesJson("{\"friends\":[]}"); // lenient match with subset of // fields - assertThatThrownBy(() -> spec.path("me").matchesJsonStrictly("{\"friends\":[]}")) + assertThatThrownBy(() -> response.path("me").matchesJsonStrictly("{\"friends\":[]}")) .as("Extended fields should fail in strict mode") .hasMessageContaining("Unexpected: name"); @@ -98,13 +98,13 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { String document = "{me {name}}"; setMockResponse("{\"me\": {\"name\":\"Luke Skywalker\"}}"); - GraphQlTester.ResponseSpec spec = graphQlTester().document(document).execute(); + GraphQlTester.Response response = graphQlTester().document(document).execute(); MovieCharacter luke = MovieCharacter.create("Luke Skywalker"); MovieCharacter han = MovieCharacter.create("Han Solo"); AtomicReference personRef = new AtomicReference<>(); - MovieCharacter actual = spec.path("me").entity(MovieCharacter.class) + MovieCharacter actual = response.path("me").entity(MovieCharacter.class) .isEqualTo(luke) .isNotEqualTo(han) .satisfies(personRef::set) @@ -115,7 +115,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { assertThat(actual.getName()).isEqualTo("Luke Skywalker"); - spec.path("") + response.path("") .entity(new ParameterizedTypeReference>() {}) .isEqualTo(Collections.singletonMap("me", luke)); @@ -133,13 +133,13 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { " }" + "}"); - GraphQlTester.ResponseSpec spec = graphQlTester().document(document).execute(); + GraphQlTester.Response response = graphQlTester().document(document).execute(); MovieCharacter han = MovieCharacter.create("Han Solo"); MovieCharacter leia = MovieCharacter.create("Leia Organa"); MovieCharacter jabba = MovieCharacter.create("Jabba the Hutt"); - List actual = spec.path("me.friends").entityList(MovieCharacter.class) + List actual = response.path("me.friends").entityList(MovieCharacter.class) .contains(han) .containsExactly(han, leia) .doesNotContain(jabba) @@ -150,7 +150,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { assertThat(actual).containsExactly(han, leia); - spec.path("me.friends") + response.path("me.friends") .entityList(new ParameterizedTypeReference() {}) .containsExactly(han, leia); @@ -168,14 +168,14 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport { setMockResponse("{\"hero\": {\"name\":\"R2-D2\"}}"); - GraphQlTester.ResponseSpec spec = graphQlTester().document(document) + GraphQlTester.Response response = graphQlTester().document(document) .operationName("HeroNameAndFriends") .variable("episode", "JEDI") .variable("foo", "bar") .variable("keyOnly", null) .execute(); - spec.path("hero").entity(MovieCharacter.class).isEqualTo(MovieCharacter.create("R2-D2")); + response.path("hero").entity(MovieCharacter.class).isEqualTo(MovieCharacter.create("R2-D2")); RequestInput input = requestInput(); assertThat(input.getDocument()).contains(document); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractDelegatingGraphQlClient.java b/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractDelegatingGraphQlClient.java index 94035429..c714f32d 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractDelegatingGraphQlClient.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractDelegatingGraphQlClient.java @@ -42,11 +42,11 @@ public abstract class AbstractDelegatingGraphQlClient implements GraphQlClient { } - public GraphQlClient.RequestSpec document(String document) { + public Request document(String document) { return this.graphQlClient.document(document); } - public GraphQlClient.RequestSpec documentName(String name) { + public Request documentName(String name) { return this.graphQlClient.documentName(name); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java index efb53847..42d1e5c7 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java @@ -69,14 +69,14 @@ final class DefaultGraphQlClient implements GraphQlClient { @Override - public RequestSpec document(String document) { - return new DefaultRequestSpec(Mono.just(document), this.transport, this.jsonPathConfig); + public Request document(String document) { + return new DefaultRequest(Mono.just(document), this.transport, this.jsonPathConfig); } @Override - public RequestSpec documentName(String name) { + public Request documentName(String name) { Mono document = this.documentSource.getDocument(name); - return new DefaultRequestSpec(document, this.transport, this.jsonPathConfig); + return new DefaultRequest(document, this.transport, this.jsonPathConfig); } @Override @@ -107,7 +107,10 @@ final class DefaultGraphQlClient implements GraphQlClient { } - private static final class DefaultRequestSpec implements RequestSpec { + /** + * Default {@link GraphQlClient.Request} implementation. + */ + private static final class DefaultRequest implements Request { private final Mono documentMono; @@ -120,7 +123,7 @@ final class DefaultGraphQlClient implements GraphQlClient { private final Configuration jsonPathConfig; - DefaultRequestSpec(Mono documentMono, GraphQlTransport transport, Configuration jsonPathConfig) { + DefaultRequest(Mono documentMono, GraphQlTransport transport, Configuration jsonPathConfig) { Assert.notNull(documentMono, "'documentMono' is required"); this.documentMono = documentMono; this.transport = transport; @@ -128,35 +131,35 @@ final class DefaultGraphQlClient implements GraphQlClient { } @Override - public DefaultRequestSpec operationName(@Nullable String operationName) { + public DefaultRequest operationName(@Nullable String operationName) { this.operationName = operationName; return this; } @Override - public DefaultRequestSpec variable(String name, Object value) { + public DefaultRequest variable(String name, Object value) { this.variables.put(name, value); return this; } @Override - public RequestSpec variables(Map variables) { + public Request variables(Map variables) { this.variables.putAll(variables); return this; } @Override - public Mono execute() { + public Mono execute() { return getRequestMono() .flatMap(this.transport::execute) - .map(payload -> new DefaultResponseSpec(payload, this.jsonPathConfig)); + .map(payload -> new DefaultResponse(payload, this.jsonPathConfig)); } @Override - public Flux executeSubscription() { + public Flux executeSubscription() { return getRequestMono() .flatMapMany(this.transport::executeSubscription) - .map(payload -> new DefaultResponseSpec(payload, this.jsonPathConfig)); + .map(payload -> new DefaultResponse(payload, this.jsonPathConfig)); } private Mono getRequestMono() { @@ -167,7 +170,10 @@ final class DefaultGraphQlClient implements GraphQlClient { } - private static class DefaultResponseSpec implements ResponseSpec { + /** + * Default {@link GraphQlClient.Response} implementation. + */ + private static class DefaultResponse implements Response { private final ExecutionResult result; @@ -175,7 +181,7 @@ final class DefaultGraphQlClient implements GraphQlClient { private final List errors; - private DefaultResponseSpec(ExecutionResult result, Configuration jsonPathConfig) { + private DefaultResponse(ExecutionResult result, Configuration jsonPathConfig) { this.result = result; this.jsonPathDocument = JsonPath.parse(result.toSpecification(), jsonPathConfig); this.errors = result.getErrors(); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClient.java b/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClient.java index 0df68f6d..8d042d0b 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClient.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClient.java @@ -54,7 +54,7 @@ public interface GraphQlClient { * @param document the document for the request * @return spec to further define or execute the request */ - RequestSpec document(String document); + Request document(String document); /** * Variant of {@link #document(String)} that uses the given key to resolve @@ -62,7 +62,7 @@ public interface GraphQlClient { * {@link DocumentSource} that the client is configured with. * @throws IllegalArgumentException if the content could not be loaded */ - RequestSpec documentName(String name); + Request documentName(String name); /** * Return a builder initialized from the configuration of "this" client @@ -105,9 +105,32 @@ public interface GraphQlClient { /** - * Declare options for GraphQL request execution. + * Declare options to gather input for a GraphQL request and execute it. */ - interface ExecuteSpec { + interface Request { + + /** + * Set the name of the operation in the {@link #document(String) document} + * to execute, if the document contains multiple operations. + * @param operationName the operation name + * @return this request spec + */ + Request operationName(@Nullable String operationName); + + /** + * Add a value for a variable defined by the operation. + * @param name the variable name + * @param value the variable value + * @return this request spec + */ + Request variable(String name, Object value); + + /** + * Add all given values for variables defined by the operation. + * @param variables the variable values + * @return this request spec + */ + Request variables(Map variables); /** * Execute as a request with a single response such as a "query" or @@ -116,7 +139,7 @@ public interface GraphQlClient { * decoding of the response. The {@code Mono} may end wth an error due * to transport level issues. */ - Mono execute(); + Mono execute(); /** * Execute a "subscription" request with a stream of responses. @@ -132,38 +155,7 @@ public interface GraphQlClient { *

The {@code Flux} may be cancelled to notify the server to end the * subscription stream. */ - Flux executeSubscription(); - - } - - - /** - * Declare options to gather input for a GraphQL request and execute it. - */ - interface RequestSpec extends ExecuteSpec { - - /** - * Set the name of the operation in the {@link #document(String) document} - * to execute, if the document contains multiple operations. - * @param operationName the operation name - * @return this request spec - */ - RequestSpec operationName(@Nullable String operationName); - - /** - * Add a value for a variable defined by the operation. - * @param name the variable name - * @param value the variable value - * @return this request spec - */ - RequestSpec variable(String name, Object value); - - /** - * Add all given values for variables defined by the operation. - * @param variables the variable values - * @return this request spec - */ - RequestSpec variables(Map variables); + Flux executeSubscription(); } @@ -171,7 +163,7 @@ public interface GraphQlClient { /** * Declare options to decode a response. */ - interface ResponseSpec { + interface Response { /** * Switch to the given the "data" path of the GraphQL response and diff --git a/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTests.java b/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTests.java index c6562737..dd55995c 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/client/GraphQlClientTests.java @@ -40,12 +40,12 @@ public class GraphQlClientTests extends GraphQlClientTestSupport { String document = "{me {name}}"; setMockResponse("{\"me\": {\"name\":\"Luke Skywalker\"}}"); - GraphQlClient.ResponseSpec spec = execute(document); + GraphQlClient.Response response = execute(document); MovieCharacter luke = MovieCharacter.create("Luke Skywalker"); - assertThat(spec.toEntity("me", MovieCharacter.class)).isEqualTo(luke); + assertThat(response.toEntity("me", MovieCharacter.class)).isEqualTo(luke); - Map map = spec.toEntity("", new ParameterizedTypeReference>() {}); + Map map = response.toEntity("", new ParameterizedTypeReference>() {}); assertThat(map).containsEntry("me", luke); assertThat(request().getDocument()).contains(document); @@ -62,15 +62,15 @@ public class GraphQlClientTests extends GraphQlClientTestSupport { " }" + "}"); - GraphQlClient.ResponseSpec spec = execute(document); + GraphQlClient.Response response = execute(document); MovieCharacter han = MovieCharacter.create("Han Solo"); MovieCharacter leia = MovieCharacter.create("Leia Organa"); - List characters = spec.toEntityList("me.friends", MovieCharacter.class); + List characters = response.toEntityList("me.friends", MovieCharacter.class); assertThat(characters).containsExactly(han, leia); - characters = spec.toEntityList("me.friends", new ParameterizedTypeReference() {}); + characters = response.toEntityList("me.friends", new ParameterizedTypeReference() {}); assertThat(characters).containsExactly(han, leia); assertThat(request().getDocument()).contains(document); @@ -86,7 +86,7 @@ public class GraphQlClientTests extends GraphQlClientTestSupport { "}"; setMockResponse("{\"hero\": {\"name\":\"R2-D2\"}}"); - GraphQlClient.ResponseSpec spec = graphQlClient().document(document) + GraphQlClient.Response response = graphQlClient().document(document) .operationName("HeroNameAndFriends") .variable("episode", "JEDI") .variable("foo", "bar") @@ -94,9 +94,9 @@ public class GraphQlClientTests extends GraphQlClientTestSupport { .execute() .block(TIMEOUT); - assertThat(spec).isNotNull(); + assertThat(response).isNotNull(); - MovieCharacter character = spec.toEntity("hero", MovieCharacter.class); + MovieCharacter character = response.toEntity("hero", MovieCharacter.class); assertThat(character).isEqualTo(MovieCharacter.create("R2-D2")); GraphQlRequest request = request(); @@ -116,16 +116,16 @@ public class GraphQlClientTests extends GraphQlClientTestSupport { GraphqlErrorBuilder.newError().message("some error").build(), GraphqlErrorBuilder.newError().message("some other error").build()); - GraphQlClient.ResponseSpec spec = execute(document); + GraphQlClient.Response response = execute(document); - assertThat(spec.errors()).extracting(GraphQLError::getMessage) + assertThat(response.errors()).extracting(GraphQLError::getMessage) .containsExactly("some error", "some other error"); } - private GraphQlClient.ResponseSpec execute(String document) { - GraphQlClient.ResponseSpec spec = graphQlClient().document(document).execute().block(TIMEOUT); - assertThat(spec).isNotNull(); - return spec; + private GraphQlClient.Response execute(String document) { + GraphQlClient.Response response = graphQlClient().document(document).execute().block(TIMEOUT); + assertThat(response).isNotNull(); + return response; } }