Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

feat: added ability to use strings for GraphQl queries when using GraphQLTestTemplate #807

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.graphql.spring.boot.test;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static java.util.Objects.requireNonNull;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -12,7 +14,10 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.IntFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.Getter;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -32,6 +37,11 @@
/** Helper class to test GraphQL queries and mutations. */
public class GraphQLTestTemplate {

private static final Pattern GRAPHQL_OP_NAME_PATTERN = Pattern.compile(
"(query|mutation|subscription)\\s+([a-z0-9]+)\\s*[({]",
(Pattern.CASE_INSENSITIVE | Pattern.MULTILINE)
);

private final ResourceLoader resourceLoader;
private final TestRestTemplate restTemplate;
private final String graphqlMapping;
Expand All @@ -57,7 +67,9 @@ private String createJsonQuery(String graphql, String operation, ObjectNode vari
if (nonNull(operation)) {
wrapper.put("operationName", operation);
}
wrapper.set("variables", variables);
if (nonNull(variables)) {
wrapper.set("variables", variables);
}
return objectMapper.writeValueAsString(wrapper);
}

Expand All @@ -72,6 +84,16 @@ private String loadResource(Resource resource) throws IOException {
}
}

private String getOperationName(String graphql) {
if (isNull(graphql)) {
return null;
}

Matcher matcher = GRAPHQL_OP_NAME_PATTERN.matcher(graphql);

return (matcher.find() ? matcher.group(2) : null);
}

/**
* Add an HTTP header that will be sent with each request this sends.
*
Expand Down Expand Up @@ -411,6 +433,95 @@ public GraphQLResponse postFiles(
return postRequest(RequestFactory.forMultipart(values, headers));
}

/**
* Performs a GraphQL request using the provided GraphQL query string.
*
* Operation name will be derived from the provided GraphQL query string.
*
* @param graphql the GraphQL query
* @return {@link GraphQLResponse} containing the result of the query execution
* @throws IOException if the request json cannot be created because of issues with one of the
* provided arguments
*/
public GraphQLResponse postForString(String graphql) throws IOException {
return postForString(graphql, getOperationName(graphql), ((ObjectNode) null));
}

/**
* Performs a GraphQL request using the provided GraphQL query string and operation name.
*
* @param graphql the GraphQL query
* @param operation the name of the GraphQL operation to be executed
* @return {@link GraphQLResponse} containing the result of the query execution
* @throws IOException if the request json cannot be created because of issues with one of the
* provided arguments
*/
public GraphQLResponse postForString(String graphql, String operation) throws IOException {
return postForString(graphql, operation, ((ObjectNode) null));
}

/**
* Performs a GraphQL request using the provided GraphQL query string and variables.
*
* Operation name will be derived from the provided GraphQL query string.
*
* @param graphql the GraphQL query
* @param variables the input variables for the GraphQL query
* @return {@link GraphQLResponse} containing the result of the query execution
* @throws IOException if the request json cannot be created because of issues with one of the
* provided arguments
*/
public GraphQLResponse postForString(String graphql, Map<String, ?> variables) throws IOException {
return postForString(graphql, getOperationName(graphql), variables);
}

/**
* Performs a GraphQL request using the provided GraphQL query string, operation name, and
* variables.
*
* @param graphql the GraphQL query
* @param operation the name of the GraphQL operation to be executed
* @param variables the input variables for the GraphQL query
* @return {@link GraphQLResponse} containing the result of the query execution
* @throws IOException if the request json cannot be created because of issues with one of the
* provided arguments
*/
public GraphQLResponse postForString(String graphql, String operation, Map<String, ?> variables) throws IOException {
return postForString(graphql, operation, ((ObjectNode) new ObjectMapper().valueToTree(variables)));
}

/**
* Performs a GraphQL request using the provided GraphQL query string and variables.
*
* Operation name will be derived from the provided GraphQL query string.
*
* @param graphql the GraphQL query
* @param variables the input variables for the GraphQL query
* @return {@link GraphQLResponse} containing the result of the query execution
* @throws IOException if the request json cannot be created because of issues with one of the
* provided arguments
*/
public GraphQLResponse postForString(String graphql, ObjectNode variables) throws IOException {
return post(createJsonQuery(graphql, getOperationName(graphql), variables));
}

/**
* Performs a GraphQL request using the provided GraphQL query string, operation name, and
* variables.
*
* @param graphql the GraphQL query
* @param operation the name of the GraphQL operation to be executed
* @param variables the input variables for the GraphQL query
* @return {@link GraphQLResponse} containing the result of the query execution
* @throws IOException if the request json cannot be created because of issues with one of the
* provided arguments
*/
public GraphQLResponse postForString(String graphql, String operation, ObjectNode variables) throws IOException {
requireNonNull(graphql, "GraphQL query string cannot be null");

return post(createJsonQuery(graphql, operation, variables));
}

/**
* Performs a GraphQL request with the provided payload.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.graphql.spring.boot.test;

import static org.junit.jupiter.api.Assertions.assertThrows;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
Expand All @@ -8,7 +10,9 @@
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -274,4 +278,194 @@ void testPerformWithIndividualFileUpload() throws IOException {
.asString()
.isEqualTo(fileNames.get(0));
}

@Test
@DisplayName("Test postForString without operation name and without variables.")
void testPostString() throws IOException {
// GIVEN
final String graphql = "query {\n"
+ " otherQuery\n"
+ "}";
// WHEN - THEN
graphQLTestTemplate
.postForString(graphql)
.assertThatNoErrorsArePresent()
.assertThatField(DATA_FIELD_OTHER_QUERY)
.asString()
.isEqualTo(TEST);
}

@Test
@DisplayName("Test postForString with embedded operation name and without variables.")
void testPostStringWithEmbeddedOperationName() throws IOException {
// GIVEN
final String graphql = "query TestOperationYo {\n"
+ " otherQuery\n"
+ "}";
// WHEN - THEN
graphQLTestTemplate
.postForString(graphql)
.assertThatNoErrorsArePresent()
.assertThatField(DATA_FIELD_OTHER_QUERY)
.asString()
.isEqualTo(TEST);
}

@Test
@DisplayName("Test postForString with explicit operation name and without variables.")
void testPostStringWithExplicitOperationName() throws IOException {
// GIVEN
final String graphql = "query TestOperationYo {\n"
+ " otherQuery\n"
+ "}";
// WHEN - THEN
graphQLTestTemplate
.postForString(graphql, "TestOperationYo")
.assertThatNoErrorsArePresent()
.assertThatField(DATA_FIELD_OTHER_QUERY)
.asString()
.isEqualTo(TEST);
}

@Test
@DisplayName("Test postForString with fragments.")
void testPostForStringWithFragments() throws IOException {
graphQLTestTemplate
.postForString(
"query($foo: String, $bar: String) {\n"
+ " fooBar(foo: $foo, bar: $bar) {\n"
+ " ...FooBarFragment\n"
+ " }\n"
+ "}"
+ "fragment FooBarFragment on FooBar {\n"
+ " foo\n"
+ " bar\n"
+ "}"
)
.assertThatNoErrorsArePresent()
.assertThatField(DATA_FIELD_FOO_BAR)
.as(FooBar.class)
.usingRecursiveComparison()
.ignoringAllOverriddenEquals()
.isEqualTo(FooBar.builder().foo(FOO).bar(BAR).build());
}

@Test
@DisplayName("Test postForString with ObjectNode variables.")
void testPostForStringWithObjectNodeVariables() throws IOException {
// GIVEN
final ObjectNode variables = objectMapper.createObjectNode();
variables.put(INPUT_STRING_NAME, INPUT_STRING_VALUE);

// WHEN - THEN
graphQLTestTemplate
.postForString(
"query ($input: String!) {"
+ " queryWithVariables(input: $input)"
+ "}",
variables
)
.assertThatNoErrorsArePresent()
.assertThatField(DATA_FIELD_QUERY_WITH_VARIABLES)
.asString()
.isEqualTo(INPUT_STRING_VALUE);
}

@Test
@DisplayName("Test postForString with Map variables.")
void testPostForStringWithMapVariables() throws IOException {
// GIVEN
final Map<String, Object> variables = new LinkedHashMap<>();
variables.put(INPUT_STRING_NAME, INPUT_STRING_VALUE);

// WHEN - THEN
graphQLTestTemplate
.postForString(
"query ($input: String!) {"
+ " queryWithVariables(input: $input)"
+ "}",
variables
)
.assertThatNoErrorsArePresent()
.assertThatField(DATA_FIELD_QUERY_WITH_VARIABLES)
.asString()
.isEqualTo(INPUT_STRING_VALUE);
}

@Test
@DisplayName("Test postForString with embedded operation name and variables.")
void testPostForStringWithEmbeddedOperationNameAndVariables() throws IOException {
// GIVEN
final ObjectNode variables = objectMapper.createObjectNode();
variables.put(INPUT_STRING_NAME, INPUT_STRING_VALUE);

// WHEN - THEN
graphQLTestTemplate
.postForString(
"query TestOperationYo ($input: String!) {"
+ " queryWithVariables(input: $input)"
+ "}",
variables
)
.assertThatNoErrorsArePresent()
.assertThatField(DATA_FIELD_QUERY_WITH_VARIABLES)
.asString()
.isEqualTo(INPUT_STRING_VALUE);
}

@Test
@DisplayName("Test postForString with explicit operation name and ObjectNode variables.")
void testPostForStringWithExplicitOperationNameAndObjectNodeVariables() throws IOException {
// GIVEN
final ObjectNode variables = objectMapper.createObjectNode();
variables.put(INPUT_STRING_NAME, INPUT_STRING_VALUE);

// WHEN - THEN
graphQLTestTemplate
.postForString(
"query TestOperationYo ($input: String!) {"
+ " queryWithVariables(input: $input)"
+ "}",
"TestOperationYo",
variables
)
.assertThatNoErrorsArePresent()
.assertThatField(DATA_FIELD_QUERY_WITH_VARIABLES)
.asString()
.isEqualTo(INPUT_STRING_VALUE);
}

@Test
@DisplayName("Test postForString with explicit operation name and Map variables.")
void testPostForStringWithExplicitOperationNameAndMapVariables() throws IOException {
// GIVEN
final Map<String, Object> variables = new LinkedHashMap<>();
variables.put(INPUT_STRING_NAME, INPUT_STRING_VALUE);

// WHEN - THEN
graphQLTestTemplate
.postForString(
"query TestOperationYo ($input: String!) {"
+ " queryWithVariables(input: $input)"
+ "}",
"TestOperationYo",
variables
)
.assertThatNoErrorsArePresent()
.assertThatField(DATA_FIELD_QUERY_WITH_VARIABLES)
.asString()
.isEqualTo(INPUT_STRING_VALUE);
}

@Test
@DisplayName("Test postForString with null string.")
void testPostStringWithNullString() {
// GIVEN
final String graphql = null;

// WHEN - THEN
assertThrows(NullPointerException.class, () -> {
graphQLTestTemplate.postForString(graphql);
});
}
}