Rename ~Spec in GraphQlClient and GraphQlTester

As these are expected to be used as local variables, "Spec" reduces
readability and the IDE creates variables called "spec", which
increases that effect. Given those are nested within `GraphQlClient`
and `GraphQlTester`, it makes sense to drop the spec part and local
variable names such as request and response make sense.

See gh-10, see gh-317
This commit is contained in:
rstoyanchev
2022-03-04 14:07:11 +00:00
parent 53fb1d4dc0
commit e67cb5885e
9 changed files with 216 additions and 225 deletions

View File

@@ -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);
}

View File

@@ -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<DefaultRequestSpec> {
private final class DefaultRequest implements Request<DefaultRequest> {
private final String document;
@@ -140,26 +140,26 @@ final class DefaultGraphQlTester implements GraphQlTester {
private final Map<String, Object> 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<Runnable> assertDecorator) {
DocumentContext jsonDocument = JsonPath.parse(result.toSpecification(), jsonPathConfig);
return new DefaultResponseSpec(jsonDocument, errorFilter, assertDecorator);
return new DefaultResponse(jsonDocument, errorFilter, assertDecorator);
}
private Consumer<Runnable> 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<GraphQLError> errorFilter,
Consumer<Runnable> 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<GraphQLError> predicate) {
public Errors filter(Predicate<GraphQLError> predicate) {
this.responseContainer.filterErrors(predicate);
return this;
}
@Override
public ErrorSpec expect(Predicate<GraphQLError> predicate) {
public Errors expect(Predicate<GraphQLError> predicate) {
this.responseContainer.expectErrors(predicate);
return this;
}
@Override
public TraverseSpec verify() {
public Traversable verify() {
this.responseContainer.verifyErrors();
return this;
}
@Override
public TraverseSpec satisfy(Consumer<List<GraphQLError>> consumer) {
public Traversable satisfy(Consumer<List<GraphQLError>> 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 <D> EntitySpec<D, ?> entity(Class<D> entityType) {
public <D> Entity<D, ?> entity(Class<D> 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 <D> EntitySpec<D, ?> entity(ParameterizedTypeReference<D> entityType) {
public <D> Entity<D, ?> entity(ParameterizedTypeReference<D> 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 <D> ListEntitySpec<D> entityList(Class<D> elementType) {
public <D> EntityList<D> entityList(Class<D> elementType) {
List<D> 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 <D> ListEntitySpec<D> entityList(ParameterizedTypeReference<D> elementType) {
public <D> EntityList<D> entityList(ParameterizedTypeReference<D> elementType) {
List<D> 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<D, S extends EntitySpec<D, S>> implements EntitySpec<D, S> {
private static class DefaultEntity<D, S extends Entity<D, S>> implements Entity<D, S> {
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<E> extends DefaultEntitySpec<List<E>, ListEntitySpec<E>>
implements ListEntitySpec<E> {
private static final class DefaultEntityList<E> extends DefaultEntity<List<E>, EntityList<E>>
implements EntityList<E> {
private DefaultListEntitySpec(List<E> entity, ResponseContainer responseContainer, String path) {
private DefaultEntityList(List<E> entity, ResponseContainer responseContainer, String path) {
super(entity, responseContainer, path);
}
@Override
@SuppressWarnings("unchecked")
public ListEntitySpec<E> contains(E... elements) {
public EntityList<E> contains(E... elements) {
doAssert(() -> {
List<E> 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<E> doesNotContain(E... elements) {
public EntityList<E> doesNotContain(E... elements) {
doAssert(() -> {
List<E> expected = Arrays.asList(elements);
AssertionErrors.assertTrue(
@@ -603,7 +603,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
@Override
@SuppressWarnings("unchecked")
public ListEntitySpec<E> containsExactly(E... elements) {
public EntityList<E> containsExactly(E... elements) {
doAssert(() -> {
List<E> expected = Arrays.asList(elements);
AssertionErrors.assertTrue(
@@ -614,14 +614,14 @@ final class DefaultGraphQlTester implements GraphQlTester {
}
@Override
public ListEntitySpec<E> hasSize(int size) {
public EntityList<E> hasSize(int size) {
doAssert(() -> AssertionErrors.assertTrue("List at path '" + getInputPath() + "' should have size " + size,
getEntity().size() == size));
return this;
}
@Override
public ListEntitySpec<E> hasSizeLessThan(int boundary) {
public EntityList<E> 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<E> hasSizeGreaterThan(int boundary) {
public EntityList<E> hasSizeGreaterThan(int boundary) {
doAssert(() -> AssertionErrors.assertTrue(
"List at path '" + getInputPath() + "' should have size greater than " + boundary,
getEntity().size() > boundary));

View File

@@ -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);
}
};
}

View File

@@ -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<B extends Builder<B>> {
/**
* 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<T extends RequestSpec<T>> extends ExecuteSpec {
interface Request<T extends Request<T>> {
/**
* 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
* <a href="https://spec.graphql.org/June2018/#sec-Errors">errors</a> 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 <D> the target entity type
* @return spec to assert the converted entity with
*/
<D> EntitySpec<D, ?> entity(Class<D> entityType);
<D> Entity<D, ?> entity(Class<D> entityType);
/**
* Convert the data at the given path to the target type.
@@ -272,7 +265,7 @@ public interface GraphQlTester {
* @param <D> the target entity type
* @return spec to assert the converted entity with
*/
<D> EntitySpec<D, ?> entity(ParameterizedTypeReference<D> entityType);
<D> Entity<D, ?> entity(ParameterizedTypeReference<D> entityType);
/**
* Convert the data at the given path to a List of the target type.
@@ -280,7 +273,7 @@ public interface GraphQlTester {
* @param <D> the target entity type
* @return spec to assert the converted List of entities with
*/
<D> ListEntitySpec<D> entityList(Class<D> elementType);
<D> EntityList<D> entityList(Class<D> elementType);
/**
* Convert the data at the given path to a List of the target type.
@@ -288,7 +281,7 @@ public interface GraphQlTester {
* @param <D> the target entity type
* @return spec to assert the converted List of entities with
*/
<D> ListEntitySpec<D> entityList(ParameterizedTypeReference<D> elementType);
<D> EntityList<D> entityList(ParameterizedTypeReference<D> 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 <D> the entity type
* @param <S> the spec type, including subtypes
*/
interface EntitySpec<D, S extends EntitySpec<D, S>> extends TraverseSpec {
interface Entity<D, S extends Entity<D, S>> 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 <E> the type of elements in the list
*/
interface ListEntitySpec<E> extends EntitySpec<List<E>, ListEntitySpec<E>> {
interface EntityList<E> extends Entity<List<E>, EntityList<E>> {
/**
* Assert the list contains the given elements.
@@ -396,7 +389,7 @@ public interface GraphQlTester {
* @return the same spec for more assertions
*/
@SuppressWarnings("unchecked")
ListEntitySpec<E> contains(E... elements);
EntityList<E> 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<E> doesNotContain(E... elements);
EntityList<E> 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<E> containsExactly(E... elements);
EntityList<E> 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<E> hasSize(int size);
EntityList<E> 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<E> hasSizeLessThan(int boundary);
EntityList<E> 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<E> hasSizeGreaterThan(int boundary);
EntityList<E> 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.
* <p>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.
* <p>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<GraphQLError> errorPredicate);
Errors filter(Predicate<GraphQLError> errorPredicate);
/**
* Use this to declare errors that are expected.
* <p>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.
* <p>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<GraphQLError> errorPredicate);
Errors expect(Predicate<GraphQLError> 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 <a href="https://spec.graphql.org/June2018/#sec-Errors">errors</a> 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<List<GraphQLError>> errorsConsumer);
Traversable satisfy(Consumer<List<GraphQLError>> 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<ResponseSpec> toFlux();
Flux<Response> toFlux();
}

View File

@@ -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<MovieCharacter> 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<Map<String, MovieCharacter>>() {})
.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<MovieCharacter> actual = spec.path("me.friends").entityList(MovieCharacter.class)
List<MovieCharacter> 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<MovieCharacter>() {})
.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);

View File

@@ -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);
}

View File

@@ -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<String> 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<String> documentMono;
@@ -120,7 +123,7 @@ final class DefaultGraphQlClient implements GraphQlClient {
private final Configuration jsonPathConfig;
DefaultRequestSpec(Mono<String> documentMono, GraphQlTransport transport, Configuration jsonPathConfig) {
DefaultRequest(Mono<String> 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<String, Object> variables) {
public Request variables(Map<String, Object> variables) {
this.variables.putAll(variables);
return this;
}
@Override
public Mono<ResponseSpec> execute() {
public Mono<Response> execute() {
return getRequestMono()
.flatMap(this.transport::execute)
.map(payload -> new DefaultResponseSpec(payload, this.jsonPathConfig));
.map(payload -> new DefaultResponse(payload, this.jsonPathConfig));
}
@Override
public Flux<ResponseSpec> executeSubscription() {
public Flux<Response> executeSubscription() {
return getRequestMono()
.flatMapMany(this.transport::executeSubscription)
.map(payload -> new DefaultResponseSpec(payload, this.jsonPathConfig));
.map(payload -> new DefaultResponse(payload, this.jsonPathConfig));
}
private Mono<GraphQlRequest> 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<GraphQLError> 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();

View File

@@ -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<String, Object> 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<ResponseSpec> execute();
Mono<Response> execute();
/**
* Execute a "subscription" request with a stream of responses.
@@ -132,38 +155,7 @@ public interface GraphQlClient {
* <p>The {@code Flux} may be cancelled to notify the server to end the
* subscription stream.
*/
Flux<ResponseSpec> 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<String, Object> variables);
Flux<Response> 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

View File

@@ -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<String, MovieCharacter> map = spec.toEntity("", new ParameterizedTypeReference<Map<String, MovieCharacter>>() {});
Map<String, MovieCharacter> map = response.toEntity("", new ParameterizedTypeReference<Map<String, MovieCharacter>>() {});
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<MovieCharacter> characters = spec.toEntityList("me.friends", MovieCharacter.class);
List<MovieCharacter> characters = response.toEntityList("me.friends", MovieCharacter.class);
assertThat(characters).containsExactly(han, leia);
characters = spec.toEntityList("me.friends", new ParameterizedTypeReference<MovieCharacter>() {});
characters = response.toEntityList("me.friends", new ParameterizedTypeReference<MovieCharacter>() {});
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;
}
}