188 lines
4.4 KiB
Plaintext
188 lines
4.4 KiB
Plaintext
[[features-graphql]]
|
|
= GraphQL
|
|
|
|
include::partial$_attributes.adoc[]
|
|
|
|
Since https://graphql.org/[GraphQL] is essentially HTTP you can write a contract for it by creating a standard HTTP contract with an additional `metadata` entry with key `verifier` and a mapping `tool=graphql`.
|
|
|
|
====
|
|
[source,groovy,indent=0,subs="verbatim",role="primary"]
|
|
.Groovy
|
|
----
|
|
import org.springframework.cloud.contract.spec.Contract
|
|
|
|
Contract.make {
|
|
|
|
request {
|
|
method(POST())
|
|
url("/graphql")
|
|
headers {
|
|
contentType("application/json")
|
|
}
|
|
body('''
|
|
{
|
|
"query":"query queryName($personName: String!) {\\n personToCheck(name: $personName) {\\n name\\n age\\n }\\n}\\n\\n\\n\\n",
|
|
"variables":{"personName":"Old Enough"},
|
|
"operationName":"queryName"
|
|
}
|
|
''')
|
|
}
|
|
|
|
response {
|
|
status(200)
|
|
headers {
|
|
contentType("application/json")
|
|
}
|
|
body('''\
|
|
{
|
|
"data": {
|
|
"personToCheck": {
|
|
"name": "Old Enough",
|
|
"age": "40"
|
|
}
|
|
}
|
|
}
|
|
''')
|
|
}
|
|
metadata(verifier: [
|
|
tool: "graphql"
|
|
])
|
|
}
|
|
----
|
|
|
|
[source,yml,indent=0,subs="verbatim",role="secondary"]
|
|
.YAML
|
|
----
|
|
---
|
|
request:
|
|
method: "POST"
|
|
url: "/graphql"
|
|
headers:
|
|
Content-Type: "application/json"
|
|
body:
|
|
query: "query queryName($personName: String!) { personToCheck(name: $personName)
|
|
{ name age } }"
|
|
variables:
|
|
personName: "Old Enough"
|
|
operationName: "queryName"
|
|
matchers:
|
|
headers:
|
|
- key: "Content-Type"
|
|
regex: "application/json.*"
|
|
regexType: "as_string"
|
|
response:
|
|
status: 200
|
|
headers:
|
|
Content-Type: "application/json"
|
|
body:
|
|
data:
|
|
personToCheck:
|
|
name: "Old Enough"
|
|
age: "40"
|
|
matchers:
|
|
headers:
|
|
- key: "Content-Type"
|
|
regex: "application/json.*"
|
|
regexType: "as_string"
|
|
name: "shouldRetrieveOldEnoughPerson"
|
|
metadata:
|
|
verifier:
|
|
tool: "graphql"
|
|
----
|
|
====
|
|
|
|
Adding the metadata section will change the way the default, WireMock stub is built. It will now use the Spring Cloud Contract request matcher, so that e.g. the `query` part of the GraphQL request gets compared against the real request by ignoring whitespaces.
|
|
|
|
[[features-graphql-producer]]
|
|
== Producer Side Setup
|
|
|
|
On the producer side your configuration can look as follows.
|
|
|
|
====
|
|
[source,xml,indent=0,subs="verbatim",role="primary"]
|
|
.Maven
|
|
----
|
|
<plugin>
|
|
<groupId>org.springframework.cloud</groupId>
|
|
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
|
<version>${spring-cloud-contract.version}</version>
|
|
<extensions>true</extensions>
|
|
<configuration>
|
|
<testMode>EXPLICIT</testMode>
|
|
<baseClassForTests>com.example.BaseClass</baseClassForTests>
|
|
</configuration>
|
|
</plugin>
|
|
----
|
|
|
|
[source,groovy,indent=0,subs="verbatim",role="secondary"]
|
|
.Gradle
|
|
----
|
|
contracts {
|
|
testMode = "EXPLICIT"
|
|
baseClassForTests = "com.example.BaseClass"
|
|
}
|
|
----
|
|
====
|
|
|
|
The base class would set up the application running on a random port.
|
|
|
|
====
|
|
[source,java,indent=0,subs="verbatim"]
|
|
.Base Class
|
|
----
|
|
@SpringBootTest(classes = ProducerApplication.class,
|
|
properties = "graphql.servlet.websocket.enabled=false",
|
|
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
public abstract class BaseClass {
|
|
|
|
@LocalServerPort int port;
|
|
|
|
@BeforeEach
|
|
public void setup() {
|
|
RestAssured.baseURI = "http://localhost:" + port;
|
|
}
|
|
}
|
|
|
|
----
|
|
====
|
|
|
|
[[features-graphql-consumer]]
|
|
== Consumer Side Setup
|
|
|
|
Example of a consumer side test of the GraphQL API.
|
|
|
|
====
|
|
[source,java,indent=0,subs="verbatim"]
|
|
.Consumer Side Test
|
|
----
|
|
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
|
|
public class BeerControllerGraphQLTest {
|
|
|
|
@RegisterExtension
|
|
static StubRunnerExtension rule = new StubRunnerExtension()
|
|
.downloadStub("com.example","beer-api-producer-graphql")
|
|
.stubsMode(StubRunnerProperties.StubsMode.LOCAL);
|
|
|
|
private static final String REQUEST_BODY = "{\n"
|
|
+ "\"query\":\"query queryName($personName: String!) {\\n personToCheck(name: $personName) {\\n name\\n age\\n }\\n}\","
|
|
+ "\"variables\":{\"personName\":\"Old Enough\"},\n"
|
|
+ "\"operationName\":\"queryName\"\n"
|
|
+ "}";
|
|
|
|
@Test
|
|
public void should_send_a_graphql_request() {
|
|
ResponseEntity<String> responseEntity = new RestTemplate()
|
|
.exchange(RequestEntity
|
|
.post(URI.create("http://localhost:" + rule.findStubUrl("beer-api-producer-graphql").getPort() + "/graphql"))
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.body(REQUEST_BODY), String.class);
|
|
|
|
BDDAssertions.then(responseEntity.getStatusCodeValue()).isEqualTo(200);
|
|
|
|
}
|
|
}
|
|
|
|
----
|
|
====
|
|
|