From 6480ab7e9f48e773438229f648e47749134dac33 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Fri, 4 Mar 2022 14:27:10 +0000 Subject: [PATCH] Polishing in GraphQlClient and GraphQlTester See gh-10, see gh-317 --- .../test/tester/DefaultGraphQlTester.java | 169 +++++++++--------- .../graphql/client/DefaultGraphQlClient.java | 14 +- 2 files changed, 88 insertions(+), 95 deletions(-) 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 5cae4842..836a6cc4 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 @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.stream.Collectors; import com.jayway.jsonpath.Configuration; @@ -52,7 +53,7 @@ import org.springframework.util.StringUtils; * requests and handle responses. It is transport agnostic and depends on a * {@link GraphQlTransport} to execute requests with. * - *

This class is final but works with any transport. + *

This class is final and works with any transport. * * @author Rossen Stoyanchev */ @@ -97,7 +98,7 @@ final class DefaultGraphQlTester implements GraphQlTester { @Override public Request documentName(String documentName) { String document = this.documentSource.getDocument(documentName).block(this.responseTimeout); - Assert.notNull(document, "Expected document content or an error"); + Assert.notNull(document, "DocumentSource completed empty"); return document(document); } @@ -160,10 +161,7 @@ final class DefaultGraphQlTester implements GraphQlTester { @SuppressWarnings("ConstantConditions") @Override public Response execute() { - GraphQlRequest request = createRequest(); - return transport.execute(request) - .map(result -> createResponseSpec(result, assertDecorator(request))) - .block(responseTimeout); + return transport.execute(request()).map(result -> response(result, request())).block(responseTimeout); } @Override @@ -173,20 +171,15 @@ final class DefaultGraphQlTester implements GraphQlTester { @Override public Subscription executeSubscription() { - GraphQlRequest request = createRequest(); - return () -> transport.executeSubscription(request) - .map(result -> createResponseSpec(result, assertDecorator(request))); + return () -> transport.executeSubscription(request()).map(result -> response(result, request())); } - private GraphQlRequest createRequest() { + private GraphQlRequest request() { return new GraphQlRequest(this.document, this.operationName, this.variables); } - private Response createResponseSpec( - ExecutionResult result, Consumer assertDecorator) { - - DocumentContext jsonDocument = JsonPath.parse(result.toSpecification(), jsonPathConfig); - return new DefaultResponse(jsonDocument, errorFilter, assertDecorator); + private DefaultResponse response(ExecutionResult result, GraphQlRequest request) { + return new DefaultResponse(result, errorFilter, assertDecorator(request), jsonPathConfig); } private Consumer assertDecorator(GraphQlRequest request) { @@ -206,7 +199,7 @@ final class DefaultGraphQlTester implements GraphQlTester { /** * Container for GraphQL response data and errors along with convenience methods. */ - private final static class ResponseContainer { + private final static class ResponseDelegate { private static final TypeRef> ERROR_LIST_TYPE = new TypeRef>() {}; @@ -215,30 +208,28 @@ final class DefaultGraphQlTester implements GraphQlTester { private static final Predicate MATCH_ALL_PREDICATE = (error) -> true; - private final DocumentContext documentContext; + private final DocumentContext jsonDoc; - private final String jsonContent; + private final Supplier jsonContent; private final List errors; private final Consumer assertDecorator; - private ResponseContainer( - DocumentContext documentContext, @Nullable Predicate errorFilter, - Consumer assertDecorator) { + private ResponseDelegate( + ExecutionResult result, @Nullable Predicate errorFilter, + Consumer assertDecorator, Configuration jsonPathConfig) { - Assert.notNull(assertDecorator, "`assertDecorator` is required"); - this.documentContext = documentContext; - this.jsonContent = this.documentContext.jsonString(); - this.errors = readErrors(documentContext); + this.jsonDoc = JsonPath.parse(result.toSpecification(), jsonPathConfig); + this.jsonContent = this.jsonDoc::jsonString; + this.errors = readErrors(this.jsonDoc); this.assertDecorator = assertDecorator; filterErrors(errorFilter); } private static List readErrors(DocumentContext documentContext) { - Assert.notNull(documentContext, "DocumentContext is required"); try { return documentContext.read(ERRORS_PATH, ERROR_LIST_TYPE); } @@ -248,13 +239,13 @@ final class DefaultGraphQlTester implements GraphQlTester { } String jsonContent() { - return this.jsonContent; + return this.jsonContent.get(); } String jsonContent(JsonPath jsonPath) { try { - Object content = this.documentContext.read(jsonPath); - return this.documentContext.configuration().jsonProvider().toJson(content); + Object content = this.jsonDoc.read(jsonPath); + return this.jsonDoc.configuration().jsonProvider().toJson(content); } catch (Exception ex) { throw new AssertionError("JSON parsing error", ex); @@ -262,7 +253,7 @@ final class DefaultGraphQlTester implements GraphQlTester { } T read(JsonPath jsonPath, TypeRef typeRef) { - return this.documentContext.read(jsonPath, typeRef); + return this.jsonDoc.read(jsonPath, typeRef); } void doAssert(Runnable task) { @@ -311,19 +302,19 @@ final class DefaultGraphQlTester implements GraphQlTester { */ private static final class DefaultResponse implements Response, Errors { - private final ResponseContainer responseContainer; + private final ResponseDelegate delegate; private DefaultResponse( - DocumentContext documentContext, @Nullable Predicate errorFilter, - Consumer assertDecorator) { + ExecutionResult result, @Nullable Predicate errorFilter, + Consumer assertDecorator, Configuration jsonPathConfig) { - this.responseContainer = new ResponseContainer(documentContext, errorFilter, assertDecorator); + this.delegate = new ResponseDelegate(result, errorFilter, assertDecorator, jsonPathConfig); } @Override public Path path(String path) { - this.responseContainer.verifyErrors(); - return new DefaultPath(path, this.responseContainer); + this.delegate.verifyErrors(); + return new DefaultPath(path, this.delegate); } @Override @@ -333,25 +324,25 @@ final class DefaultGraphQlTester implements GraphQlTester { @Override public Errors filter(Predicate predicate) { - this.responseContainer.filterErrors(predicate); + this.delegate.filterErrors(predicate); return this; } @Override public Errors expect(Predicate predicate) { - this.responseContainer.expectErrors(predicate); + this.delegate.expectErrors(predicate); return this; } @Override public Traversable verify() { - this.responseContainer.verifyErrors(); + this.delegate.verifyErrors(); return this; } @Override public Traversable satisfy(Consumer> consumer) { - this.responseContainer.consumeErrors(consumer); + this.delegate.consumeErrors(consumer); return this; } @@ -362,19 +353,21 @@ final class DefaultGraphQlTester implements GraphQlTester { */ private static final class DefaultPath implements Path { - private final String inputPath; + private final String path; - private final ResponseContainer responseContainer; + private final ResponseDelegate delegate; private final JsonPath jsonPath; private final JsonPathExpectationsHelper pathHelper; - private DefaultPath(String path, ResponseContainer responseContainer) { + private DefaultPath(String path, ResponseDelegate delegate) { + Assert.notNull(path, "`path` is required"); - Assert.notNull(responseContainer, "ResponseContainer is required"); - this.inputPath = path; - this.responseContainer = responseContainer; + Assert.notNull(delegate, "ResponseContainer is required"); + + this.path = path; + this.delegate = delegate; this.jsonPath = initJsonPath(path); this.pathHelper = new JsonPathExpectationsHelper(this.jsonPath.getPath()); } @@ -391,67 +384,67 @@ final class DefaultGraphQlTester implements GraphQlTester { @Override public Path path(String path) { - return new DefaultPath(path, this.responseContainer); + return new DefaultPath(path, this.delegate); } @Override public Path pathExists() { - this.responseContainer.doAssert(() -> this.pathHelper.hasJsonPath(this.responseContainer.jsonContent())); + this.delegate.doAssert(() -> this.pathHelper.hasJsonPath(this.delegate.jsonContent())); return this; } @Override public Path pathDoesNotExist() { - this.responseContainer.doAssert(() -> this.pathHelper.doesNotHaveJsonPath(this.responseContainer.jsonContent())); + this.delegate.doAssert(() -> this.pathHelper.doesNotHaveJsonPath(this.delegate.jsonContent())); return this; } @Override public Path valueExists() { - this.responseContainer.doAssert(() -> this.pathHelper.exists(this.responseContainer.jsonContent())); + this.delegate.doAssert(() -> this.pathHelper.exists(this.delegate.jsonContent())); return this; } @Override public Path valueDoesNotExist() { - this.responseContainer.doAssert(() -> this.pathHelper.doesNotExist(this.responseContainer.jsonContent())); + this.delegate.doAssert(() -> this.pathHelper.doesNotExist(this.delegate.jsonContent())); return this; } @Override public Path valueIsEmpty() { - this.responseContainer.doAssert(() -> this.pathHelper.assertValueIsEmpty(this.responseContainer.jsonContent())); + this.delegate.doAssert(() -> this.pathHelper.assertValueIsEmpty(this.delegate.jsonContent())); return this; } @Override public Path valueIsNotEmpty() { - this.responseContainer.doAssert(() -> this.pathHelper.assertValueIsNotEmpty(this.responseContainer.jsonContent())); + this.delegate.doAssert(() -> this.pathHelper.assertValueIsNotEmpty(this.delegate.jsonContent())); return this; } @Override public Entity entity(Class entityType) { - D entity = this.responseContainer.read(this.jsonPath, new TypeRefAdapter<>(entityType)); - return new DefaultEntity<>(entity, this.responseContainer, this.inputPath); + D entity = this.delegate.read(this.jsonPath, new TypeRefAdapter<>(entityType)); + return new DefaultEntity<>(entity, this.path, this.delegate); } @Override public Entity entity(ParameterizedTypeReference entityType) { - D entity = this.responseContainer.read(this.jsonPath, new TypeRefAdapter<>(entityType)); - return new DefaultEntity<>(entity, this.responseContainer, this.inputPath); + D entity = this.delegate.read(this.jsonPath, new TypeRefAdapter<>(entityType)); + return new DefaultEntity<>(entity, this.path, this.delegate); } @Override public EntityList entityList(Class elementType) { - List entity = this.responseContainer.read(this.jsonPath, new TypeRefAdapter<>(List.class, elementType)); - return new DefaultEntityList<>(entity, this.responseContainer, this.inputPath); + List entity = this.delegate.read(this.jsonPath, new TypeRefAdapter<>(List.class, elementType)); + return new DefaultEntityList<>(entity, this.path, this.delegate); } @Override public EntityList entityList(ParameterizedTypeReference elementType) { - List entity = this.responseContainer.read(this.jsonPath, new TypeRefAdapter<>(List.class, elementType)); - return new DefaultEntityList<>(entity, this.responseContainer, this.inputPath); + List entity = this.delegate.read(this.jsonPath, new TypeRefAdapter<>(List.class, elementType)); + return new DefaultEntityList<>(entity, this.path, this.delegate); } @Override @@ -467,14 +460,14 @@ final class DefaultGraphQlTester implements GraphQlTester { } private void matchesJson(String expected, boolean strict) { - this.responseContainer.doAssert(() -> { - String actual = this.responseContainer.jsonContent(this.jsonPath); + this.delegate.doAssert(() -> { + String actual = this.delegate.jsonContent(this.jsonPath); try { new JsonExpectationsHelper().assertJsonEqual(expected, actual, strict); } catch (AssertionError ex) { throw new AssertionError(ex.getMessage() + "\n\n" + "Expected JSON content:\n'" + expected + "'\n\n" - + "Actual JSON content:\n'" + actual + "'\n\n" + "Input path: '" + this.inputPath + "'\n", + + "Actual JSON content:\n'" + actual + "'\n\n" + "Input path: '" + this.path + "'\n", ex); } catch (Exception ex) { @@ -492,14 +485,14 @@ final class DefaultGraphQlTester implements GraphQlTester { private final D entity; - private final ResponseContainer responseContainer; + private final String path; - private final String inputPath; + private final ResponseDelegate delegate; - protected DefaultEntity(D entity, ResponseContainer responseContainer, String path) { + protected DefaultEntity(D entity, String path, ResponseDelegate delegate) { this.entity = entity; - this.responseContainer = responseContainer; - this.inputPath = path; + this.delegate = delegate; + this.path = path; } protected D getEntity() { @@ -507,52 +500,52 @@ final class DefaultGraphQlTester implements GraphQlTester { } protected void doAssert(Runnable task) { - this.responseContainer.doAssert(task); + this.delegate.doAssert(task); } - protected String getInputPath() { - return this.inputPath; + protected String getPath() { + return this.path; } @Override public Path path(String path) { - return new DefaultPath(path, this.responseContainer); + return new DefaultPath(path, this.delegate); } @Override public T isEqualTo(Object expected) { - this.responseContainer.doAssert(() -> AssertionErrors.assertEquals(this.inputPath, expected, this.entity)); + this.delegate.doAssert(() -> AssertionErrors.assertEquals(this.path, expected, this.entity)); return self(); } @Override public T isNotEqualTo(Object other) { - this.responseContainer.doAssert(() -> AssertionErrors.assertNotEquals(this.inputPath, other, this.entity)); + this.delegate.doAssert(() -> AssertionErrors.assertNotEquals(this.path, other, this.entity)); return self(); } @Override public T isSameAs(Object expected) { - this.responseContainer.doAssert(() -> AssertionErrors.assertTrue(this.inputPath, expected == this.entity)); + this.delegate.doAssert(() -> AssertionErrors.assertTrue(this.path, expected == this.entity)); return self(); } @Override public T isNotSameAs(Object other) { - this.responseContainer.doAssert(() -> AssertionErrors.assertTrue(this.inputPath, other != this.entity)); + this.delegate.doAssert(() -> AssertionErrors.assertTrue(this.path, other != this.entity)); return self(); } @Override public T matches(Predicate predicate) { - this.responseContainer - .doAssert(() -> AssertionErrors.assertTrue(this.inputPath, predicate.test(this.entity))); + this.delegate + .doAssert(() -> AssertionErrors.assertTrue(this.path, predicate.test(this.entity))); return self(); } @Override public T satisfies(Consumer consumer) { - this.responseContainer.doAssert(() -> consumer.accept(this.entity)); + this.delegate.doAssert(() -> consumer.accept(this.entity)); return self(); } @@ -574,8 +567,8 @@ final class DefaultGraphQlTester implements GraphQlTester { private static final class DefaultEntityList extends DefaultEntity, EntityList> implements EntityList { - private DefaultEntityList(List entity, ResponseContainer responseContainer, String path) { - super(entity, responseContainer, path); + private DefaultEntityList(List entity, String path, ResponseDelegate delegate) { + super(entity, path, delegate); } @Override @@ -583,7 +576,7 @@ final class DefaultGraphQlTester implements GraphQlTester { public EntityList contains(E... elements) { doAssert(() -> { List expected = Arrays.asList(elements); - AssertionErrors.assertTrue("List at path '" + getInputPath() + "' does not contain " + expected, + AssertionErrors.assertTrue("List at path '" + getPath() + "' does not contain " + expected, getEntity().containsAll(expected)); }); return this; @@ -595,7 +588,7 @@ final class DefaultGraphQlTester implements GraphQlTester { doAssert(() -> { List expected = Arrays.asList(elements); AssertionErrors.assertTrue( - "List at path '" + getInputPath() + "' should not have contained " + expected, + "List at path '" + getPath() + "' should not have contained " + expected, !getEntity().containsAll(expected)); }); return this; @@ -607,7 +600,7 @@ final class DefaultGraphQlTester implements GraphQlTester { doAssert(() -> { List expected = Arrays.asList(elements); AssertionErrors.assertTrue( - "List at path '" + getInputPath() + "' should have contained exactly " + expected, + "List at path '" + getPath() + "' should have contained exactly " + expected, getEntity().containsAll(expected)); }); return this; @@ -615,7 +608,7 @@ final class DefaultGraphQlTester implements GraphQlTester { @Override public EntityList hasSize(int size) { - doAssert(() -> AssertionErrors.assertTrue("List at path '" + getInputPath() + "' should have size " + size, + doAssert(() -> AssertionErrors.assertTrue("List at path '" + getPath() + "' should have size " + size, getEntity().size() == size)); return this; } @@ -623,7 +616,7 @@ final class DefaultGraphQlTester implements GraphQlTester { @Override public EntityList hasSizeLessThan(int boundary) { doAssert(() -> AssertionErrors.assertTrue( - "List at path '" + getInputPath() + "' should have size less than " + boundary, + "List at path '" + getPath() + "' should have size less than " + boundary, getEntity().size() < boundary)); return this; } @@ -631,7 +624,7 @@ final class DefaultGraphQlTester implements GraphQlTester { @Override public EntityList hasSizeGreaterThan(int boundary) { doAssert(() -> AssertionErrors.assertTrue( - "List at path '" + getInputPath() + "' should have size greater than " + boundary, + "List at path '" + getPath() + "' should have size greater than " + boundary, getEntity().size() > boundary)); return this; } 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 42d1e5c7..1bdb2cb8 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 @@ -124,7 +124,7 @@ final class DefaultGraphQlClient implements GraphQlClient { private final Configuration jsonPathConfig; DefaultRequest(Mono documentMono, GraphQlTransport transport, Configuration jsonPathConfig) { - Assert.notNull(documentMono, "'documentMono' is required"); + Assert.notNull(documentMono, "'document' is required"); this.documentMono = documentMono; this.transport = transport; this.jsonPathConfig = jsonPathConfig; @@ -177,34 +177,34 @@ final class DefaultGraphQlClient implements GraphQlClient { private final ExecutionResult result; - private final DocumentContext jsonPathDocument; + private final DocumentContext jsonPathDoc; private final List errors; private DefaultResponse(ExecutionResult result, Configuration jsonPathConfig) { this.result = result; - this.jsonPathDocument = JsonPath.parse(result.toSpecification(), jsonPathConfig); + this.jsonPathDoc = JsonPath.parse(result.toSpecification(), jsonPathConfig); this.errors = result.getErrors(); } @Override public D toEntity(String path, Class entityType) { - return this.jsonPathDocument.read(initJsonPath(path), new TypeRefAdapter<>(entityType)); + return this.jsonPathDoc.read(initJsonPath(path), new TypeRefAdapter<>(entityType)); } @Override public D toEntity(String path, ParameterizedTypeReference entityType) { - return this.jsonPathDocument.read(initJsonPath(path), new TypeRefAdapter<>(entityType)); + return this.jsonPathDoc.read(initJsonPath(path), new TypeRefAdapter<>(entityType)); } @Override public List toEntityList(String path, Class elementType) { - return this.jsonPathDocument.read(initJsonPath(path), new TypeRefAdapter<>(List.class, elementType)); + return this.jsonPathDoc.read(initJsonPath(path), new TypeRefAdapter<>(List.class, elementType)); } @Override public List toEntityList(String path, ParameterizedTypeReference elementType) { - return this.jsonPathDocument.read(initJsonPath(path), new TypeRefAdapter<>(List.class, elementType)); + return this.jsonPathDoc.read(initJsonPath(path), new TypeRefAdapter<>(List.class, elementType)); } private static JsonPath initJsonPath(String path) {