Skip to content

Commit

Permalink
Add RetrieveSpec to GraphQlClient
Browse files Browse the repository at this point in the history
Provides a shortcut alternative to the execute methods, for decoding
a single field from the response.

See gh-10
  • Loading branch information
rstoyanchev committed Mar 15, 2022
1 parent 38a9eb1 commit e7b2f72
Show file tree
Hide file tree
Showing 11 changed files with 407 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

/**
* Represents a GraphQL request with the inputs to pass to a GraphQL service
Expand Down Expand Up @@ -63,7 +64,7 @@ public GraphQlRequest(String document, @Nullable String operationName, @Nullable
Assert.notNull(document, "'document' is required");
this.document = document;
this.operationName = operationName;
this.variables = ((variables != null) ? variables : Collections.emptyMap());
this.variables = (variables != null ? variables : Collections.emptyMap());
}


Expand Down Expand Up @@ -115,6 +116,24 @@ public Map<String, Object> toMap() {
return map;
}

@Override
public boolean equals(Object o) {
if (! (o instanceof GraphQlRequest)) {
return false;
}
GraphQlRequest other = (GraphQlRequest) o;
return (getDocument().equals(other.getDocument()) &&
ObjectUtils.nullSafeEquals(getOperationName(), other.getOperationName()) &&
ObjectUtils.nullSafeEquals(getVariables(), other.getVariables()));
}

@Override
public int hashCode() {
return this.document.hashCode() +
31 * ObjectUtils.nullSafeHashCode(this.operationName) +
31 * this.variables.hashCode();
}

@Override
public String toString() {
return "document='" + getDocument() + "'" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ protected AbstractDelegatingGraphQlClient(GraphQlClient graphQlClient) {
}


public Request document(String document) {
public RequestSpec document(String document) {
return this.graphQlClient.document(document);
}

public Request documentName(String name) {
public RequestSpec documentName(String name) {
return this.graphQlClient.documentName(name);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@


import org.springframework.core.ParameterizedTypeReference;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.graphql.GraphQlResponse;
import org.springframework.lang.Nullable;

Expand All @@ -30,6 +31,11 @@
*/
public interface ClientGraphQlResponse extends GraphQlResponse {

/**
* Return the request associated with this response.
*/
GraphQlRequest getRequest();

/**
* Navigate to the given path under the "data" key of the response map where
* the path is a dot-separated string with optional array indexes.
Expand All @@ -44,7 +50,7 @@ public interface ClientGraphQlResponse extends GraphQlResponse {
* @param path relative to the "data" key
* @return representation for the field with further options to inspect or
* decode its value; use {@link ResponseField#isValid()} to check if the
* field actually exists and its value is present
* field actually exists, has a value, or field errors
*/
ResponseField field(String path);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ final class DefaultClientGraphQlResponse extends MapGraphQlResponse implements C
}


@Override
public GraphQlRequest getRequest() {
return this.request;
}

@Override
public ResponseField field(String path) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
package org.springframework.graphql.client;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.codec.Decoder;
import org.springframework.core.codec.Encoder;
import org.springframework.graphql.GraphQlRequest;
Expand Down Expand Up @@ -69,13 +71,13 @@ final class DefaultGraphQlClient implements GraphQlClient {


@Override
public Request document(String document) {
return new DefaultRequest(Mono.just(document));
public RequestSpec document(String document) {
return new DefaultRequestSpec(Mono.just(document));
}

@Override
public Request documentName(String name) {
return new DefaultRequest(this.documentSource.getDocument(name));
public RequestSpec documentName(String name) {
return new DefaultRequestSpec(this.documentSource.getDocument(name));
}

@Override
Expand Down Expand Up @@ -107,9 +109,9 @@ public GraphQlClient build() {


/**
* Default {@link GraphQlClient.Request} implementation.
* Default {@link RequestSpec} implementation.
*/
private final class DefaultRequest implements Request {
private final class DefaultRequestSpec implements RequestSpec {

private final Mono<String> documentMono;

Expand All @@ -118,29 +120,39 @@ private final class DefaultRequest implements Request {

private final Map<String, Object> variables = new LinkedHashMap<>();

DefaultRequest(Mono<String> documentMono) {
DefaultRequestSpec(Mono<String> documentMono) {
Assert.notNull(documentMono, "'document' is required");
this.documentMono = documentMono;
}

@Override
public DefaultRequest operationName(@Nullable String operationName) {
public DefaultRequestSpec operationName(@Nullable String operationName) {
this.operationName = operationName;
return this;
}

@Override
public DefaultRequest variable(String name, Object value) {
public DefaultRequestSpec variable(String name, @Nullable Object value) {
this.variables.put(name, value);
return this;
}

@Override
public Request variables(Map<String, Object> variables) {
public RequestSpec variables(Map<String, Object> variables) {
this.variables.putAll(variables);
return this;
}

@Override
public RetrieveSpec retrieve(String path) {
return new DefaultRetrieveSpec(execute(), path);
}

@Override
public RetrieveSubscriptionSpec retrieveSubscription(String path) {
return new DefaultRetrieveSubscriptionSpec(executeSubscription(), path);
}

@Override
public Mono<ClientGraphQlResponse> execute() {
return initRequest().flatMap(request ->
Expand Down Expand Up @@ -177,4 +189,87 @@ private <T> Mono<T> toGraphQlTransportException(Throwable ex, GraphQlRequest req
}


private static class RetrieveSpecSupport {

private final String path;

protected RetrieveSpecSupport(String path) {
this.path = path;
}

protected ResponseField getField(ClientGraphQlResponse response) {
ResponseField field = response.field(this.path);
if (!field.isValid() || !field.getErrors().isEmpty()) {
GraphQlRequest request = response.getRequest();
throw new FieldAccessException(request, response, field);
}
return field;
}

}


private static class DefaultRetrieveSpec extends RetrieveSpecSupport implements RetrieveSpec {

private final Mono<ClientGraphQlResponse> responseMono;

DefaultRetrieveSpec(Mono<ClientGraphQlResponse> responseMono, String path) {
super(path);
this.responseMono = responseMono;
}

@Override
public <D> Mono<D> toEntity(Class<D> entityType) {
return this.responseMono.map(this::getField).mapNotNull(field -> field.toEntity(entityType));
}

@Override
public <D> Mono<D> toEntity(ParameterizedTypeReference<D> entityType) {
return this.responseMono.map(this::getField).mapNotNull(field -> field.toEntity(entityType));
}

@Override
public <D> Mono<List<D>> toEntityList(Class<D> elementType) {
return this.responseMono.map(this::getField).map(field -> field.toEntityList(elementType));
}

@Override
public <D> Mono<List<D>> toEntityList(ParameterizedTypeReference<D> elementType) {
return this.responseMono.map(this::getField).map(field -> field.toEntityList(elementType));
}

}


private static class DefaultRetrieveSubscriptionSpec extends RetrieveSpecSupport implements RetrieveSubscriptionSpec {

private final Flux<ClientGraphQlResponse> responseFlux;

DefaultRetrieveSubscriptionSpec(Flux<ClientGraphQlResponse> responseFlux, String path) {
super(path);
this.responseFlux = responseFlux;
}

@Override
public <D> Flux<D> toEntity(Class<D> entityType) {
return this.responseFlux.map(this::getField).mapNotNull(field -> field.toEntity(entityType));
}

@Override
public <D> Flux<D> toEntity(ParameterizedTypeReference<D> entityType) {
return this.responseFlux.map(this::getField).mapNotNull(field -> field.toEntity(entityType));
}

@Override
public <D> Flux<List<D>> toEntityList(Class<D> elementType) {
return this.responseFlux.map(this::getField).map(field -> field.toEntityList(elementType));
}

@Override
public <D> Flux<List<D>> toEntityList(ParameterizedTypeReference<D> elementType) {
return this.responseFlux.map(this::getField).map(field -> field.toEntityList(elementType));
}

}

}
Loading

0 comments on commit e7b2f72

Please sign in to comment.