Extract GraphQlRequest interface

See gh-332
This commit is contained in:
rstoyanchev
2022-03-18 20:58:21 +00:00
parent 08597c9e8a
commit be05b031d6
10 changed files with 151 additions and 100 deletions

View File

@@ -34,6 +34,7 @@ import com.jayway.jsonpath.TypeRef;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.graphql.DefaultGraphQlRequest;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.graphql.GraphQlResponse;
import org.springframework.graphql.GraphQlResponseError;
@@ -179,7 +180,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
}
private GraphQlRequest request() {
return new GraphQlRequest(this.document, this.operationName, this.variables);
return new DefaultGraphQlRequest(this.document, this.operationName, this.variables);
}
private DefaultResponse mapResponse(GraphQlResponse response, GraphQlRequest request) {

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
/**
* Default implementation of {@link GraphQlRequest}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
public class DefaultGraphQlRequest implements GraphQlRequest {
private final String document;
@Nullable
private final String operationName;
private final Map<String, Object> variables;
/**
* Create a request.
* @param document textual representation of the operation(s)
*/
public DefaultGraphQlRequest(String document) {
this(document, null, null);
}
/**
* Create a request with a complete set of inputs.
* @param document textual representation of the operation(s)
* @param operationName optionally, the name of the operation to execute
* @param variables variables by which the operation is parameterized
*/
public DefaultGraphQlRequest(
String document, @Nullable String operationName, @Nullable Map<String, Object> variables) {
Assert.notNull(document, "'document' is required");
this.document = document;
this.operationName = operationName;
this.variables = (variables != null ? variables : Collections.emptyMap());
}
@Override
public String getDocument() {
return this.document;
}
@Override
@Nullable
public String getOperationName() {
return this.operationName;
}
@Override
public Map<String, Object> getVariables() {
return this.variables;
}
@Override
public Map<String, Object> toMap() {
Map<String, Object> map = new LinkedHashMap<>(3);
map.put("query", getDocument());
if (getOperationName() != null) {
map.put("operationName", getOperationName());
}
if (!CollectionUtils.isEmpty(getVariables())) {
map.put("variables", new LinkedHashMap<>(getVariables()));
}
return map;
}
@Override
public boolean equals(Object o) {
if (! (o instanceof DefaultGraphQlRequest)) {
return false;
}
DefaultGraphQlRequest other = (DefaultGraphQlRequest) 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() + "'" +
((getOperationName() != null) ? ", operationName='" + getOperationName() + "'" : "") +
(!CollectionUtils.isEmpty(getVariables()) ? ", variables=" + getVariables() : "");
}
}

View File

@@ -16,14 +16,10 @@
package org.springframework.graphql;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
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
@@ -36,62 +32,26 @@ import org.springframework.util.ObjectUtils;
* @author Rossen Stoyanchev
* @since 1.0.0
*/
public class GraphQlRequest {
private final String document;
@Nullable
private final String operationName;
private final Map<String, Object> variables;
/**
* Create a request.
* @param document textual representation of the operation(s)
*/
public GraphQlRequest(String document) {
this(document, null, null);
}
/**
* Create a request with a complete set of inputs.
* @param document textual representation of the operation(s)
* @param operationName optionally, the name of the operation to execute
* @param variables variables by which the operation is parameterized
*/
public GraphQlRequest(String document, @Nullable String operationName, @Nullable Map<String, Object> variables) {
Assert.notNull(document, "'document' is required");
this.document = document;
this.operationName = operationName;
this.variables = (variables != null ? variables : Collections.emptyMap());
}
public interface GraphQlRequest {
/**
* Return the GraphQL document which is the textual representation of an
* operation (or operations) to perform, including any selection sets and
* fragments.
*/
public String getDocument() {
return this.document;
}
String getDocument();
/**
* Return the name of the operation in the {@link #getDocument() document}
* to execute, if the document contains multiple operations.
*/
@Nullable
public String getOperationName() {
return this.operationName;
}
String getOperationName();
/**
* Return values for variable defined by the operation.
*/
public Map<String, Object> getVariables() {
return this.variables;
}
Map<String, Object> getVariables();
/**
* Convert the request to a {@link Map} as defined in
@@ -104,41 +64,6 @@ public class GraphQlRequest {
* <tr><td>variables</td><td>{@link #getVariables() variables}</td></tr>
* </table>
*/
public Map<String, Object> toMap() {
Map<String, Object> map = new LinkedHashMap<>(3);
map.put("query", getDocument());
if (getOperationName() != null) {
map.put("operationName", getOperationName());
}
if (!CollectionUtils.isEmpty(getVariables())) {
map.put("variables", new LinkedHashMap<>(getVariables()));
}
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() + "'" +
((getOperationName() != null) ? ", operationName='" + getOperationName() + "'" : "") +
(!CollectionUtils.isEmpty(getVariables()) ? ", variables=" + getVariables() : "");
}
Map<String, Object> toMap();
}

View File

@@ -29,10 +29,9 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Extension of {@link GraphQlRequest} for server side handling, adding the
* transport (e.g. HTTP or WebSocket handler) assigned {@link #getId() id} and
* {@link #getLocale() locale} in the addition to the {@link GraphQlRequest}
* inputs.
* {@link GraphQlRequest} for server side handling, adding the transport (e.g. HTTP
* or WebSocket handler) assigned {@link #getId() id} and {@link #getLocale()
* locale} in the addition to the {@code GraphQlRequest} inputs.
*
* <p>{@code RequestInput} supports the initialization of {@link ExecutionInput}
* that is passed to {@link graphql.GraphQL}. You can customize that via
@@ -42,7 +41,7 @@ import org.springframework.util.Assert;
* @author Brian Clozel
* @since 1.0.0
*/
public class RequestInput extends GraphQlRequest {
public class RequestInput extends DefaultGraphQlRequest {
private final String id;

View File

@@ -27,6 +27,7 @@ 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.DefaultGraphQlRequest;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.graphql.GraphQlResponse;
import org.springframework.graphql.support.DocumentSource;
@@ -176,7 +177,7 @@ final class DefaultGraphQlClient implements GraphQlClient {
private Mono<GraphQlRequest> initRequest() {
return this.documentMono.map(document ->
new GraphQlRequest(document, this.operationName, this.variables));
new DefaultGraphQlRequest(document, this.operationName, this.variables));
}
private DefaultClientGraphQlResponse initResponse(GraphQlRequest request, GraphQlResponse response) {

View File

@@ -28,7 +28,7 @@ import graphql.execution.ResultPath;
import org.junit.jupiter.api.Test;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.graphql.DefaultGraphQlRequest;
import org.springframework.graphql.GraphQlResponseError;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
@@ -158,7 +158,7 @@ public class DefaultGraphQlClientResponseTests {
private ClientGraphQlResponse creatResponse(Map<String, Object> responseMap) {
return new DefaultClientGraphQlResponse(
new GraphQlRequest("{test}"), GraphQlTransport.wrapResponseMap(responseMap),
new DefaultGraphQlRequest("{test}"), GraphQlTransport.wrapResponseMap(responseMap),
new Jackson2JsonEncoder(), new Jackson2JsonDecoder());
}

View File

@@ -19,7 +19,6 @@ package org.springframework.graphql.client;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.graphql.support.DocumentSource;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -28,6 +28,7 @@ import graphql.GraphQLError;
import org.mockito.ArgumentCaptor;
import reactor.core.publisher.Mono;
import org.springframework.graphql.DefaultGraphQlRequest;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
@@ -49,7 +50,7 @@ public class GraphQlClientTestSupport {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private final ArgumentCaptor<GraphQlRequest> requestCaptor = ArgumentCaptor.forClass(GraphQlRequest.class);
private final ArgumentCaptor<GraphQlRequest> requestCaptor = ArgumentCaptor.forClass(DefaultGraphQlRequest.class);
private final GraphQlTransport transport = mock(GraphQlTransport.class);
@@ -72,15 +73,15 @@ public class GraphQlClientTestSupport {
protected void initDataResponse(String document, String responseData) {
initResponse(new GraphQlRequest(document), responseData);
initResponse(new DefaultGraphQlRequest(document), responseData);
}
protected void initErrorResponse(String document, GraphQLError... errors) {
initResponse(new GraphQlRequest(document), null, errors);
initResponse(new DefaultGraphQlRequest(document), null, errors);
}
protected void initResponse(String document, String responseData, GraphQLError... errors) {
initResponse(new GraphQlRequest(document), responseData, errors);
initResponse(new DefaultGraphQlRequest(document), responseData, errors);
}
protected void initResponse(GraphQlRequest request, @Nullable String responseData, GraphQLError... errors) {

View File

@@ -30,6 +30,7 @@ import graphql.validation.ValidationErrorType;
import org.junit.jupiter.api.Test;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.graphql.DefaultGraphQlRequest;
import org.springframework.graphql.GraphQlRequest;
import static org.assertj.core.api.Assertions.assertThat;
@@ -100,7 +101,7 @@ public class GraphQlClientTests extends GraphQlClientTestSupport {
vars.put("foo", "bar");
vars.put("keyOnly", null);
GraphQlRequest request = new GraphQlRequest("mockRequest1", "HeroNameAndFriends", vars);
GraphQlRequest request = new DefaultGraphQlRequest("mockRequest1", "HeroNameAndFriends", vars);
initResponse(request, "{\"hero\": {\"name\":\"R2-D2\"}}");
MovieCharacter character = graphQlClient().document(document)

View File

@@ -31,6 +31,7 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.graphql.DefaultGraphQlRequest;
import org.springframework.graphql.GraphQlRequest;
import org.springframework.graphql.GraphQlResponse;
import org.springframework.graphql.GraphQlResponseError;
@@ -175,7 +176,7 @@ public class MockWebSocketGraphQlTransportTests {
TestWebSocketClient client = new TestWebSocketClient(new PingResponseHandler(this.response1));
WebSocketGraphQlTransport transport = createTransport(client);
StepVerifier.create(transport.execute(new GraphQlRequest("{Query1}")))
StepVerifier.create(transport.execute(new DefaultGraphQlRequest("{Query1}")))
.expectNext(this.response1)
.expectComplete()
.verify(TIMEOUT);
@@ -183,7 +184,7 @@ public class MockWebSocketGraphQlTransportTests {
assertActualClientMessages(client.getConnection(0),
GraphQlMessage.connectionInit(null),
GraphQlMessage.pong(null),
GraphQlMessage.subscribe("1", new GraphQlRequest("{Query1}")));
GraphQlMessage.subscribe("1", new DefaultGraphQlRequest("{Query1}")));
}
@Test
@@ -303,7 +304,7 @@ public class MockWebSocketGraphQlTransportTests {
String expectedMessage = "disconnected with CloseStatus[code=4400, reason=Invalid message]";
StepVerifier.create(transport.execute(new GraphQlRequest("{Query1}")))
StepVerifier.create(transport.execute(new DefaultGraphQlRequest("{Query1}")))
.expectErrorSatisfies(ex -> assertThat(ex).hasMessageEndingWith(expectedMessage))
.verify(TIMEOUT);
}
@@ -381,7 +382,7 @@ public class MockWebSocketGraphQlTransportTests {
GraphQlMessage outputMessage = (inputMessage.resolvedType() == GraphQlMessageType.CONNECTION_INIT ?
GraphQlMessage.connectionAck(null) :
GraphQlMessage.subscribe(id, new GraphQlRequest("")));
GraphQlMessage.subscribe(id, new DefaultGraphQlRequest("")));
return Flux.just(this.codecDelegate.encode(session, outputMessage));
}));