Skip to content

Commit

Permalink
👍 0.7.0 - match method will not throw any exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
nilwurtz committed Sep 26, 2023
1 parent b64acbd commit ed1522d
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 55 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Changed

## [0.7.0] - 2023-09-27
### Changed
- Throws `InvalidQueryException` and `InvalidJsonException` when `withRequest` is called.
- When `match` method is called, it will not throw any exception if the request is invalid.

## [0.6.2] - 2023-09-08
### Added
- Added `withRequest` method which can used easily when using remote wiremock server.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ repositories {
}
dependencies {
testImplementation 'io.github.nilwurtz:wiremock-graphql-extension:0.6.2'
testImplementation 'io.github.nilwurtz:wiremock-graphql-extension:0.7.0'
}
```

Expand All @@ -40,7 +40,7 @@ dependencies {
<dependency>
<groupId>io.github.nilwurtz</groupId>
<artifactId>wiremock-graphql-extension</artifactId>
<version>0.6.2</version>
<version>0.7.0</version>
<scope>test</scope>
</dependency>
```
Expand Down Expand Up @@ -103,15 +103,15 @@ Please download `wiremock-graphql-extension-x.y.z-jar-with-dependencies.jar` fro
docker run -it --rm \
-p 8080:8080 \
--name wiremock \
-v /path/to/wiremock-graphql-extension-0.6.2-jar-with-dependencies.jar:/var/wiremock/extensions/wiremock-graphql-extension-0.6.2-jar-with-dependencies.jar \
-v /path/to/wiremock-graphql-extension-0.7.0-jar-with-dependencies.jar:/var/wiremock/extensions/wiremock-graphql-extension-0.7.0-jar-with-dependencies.jar \
wiremock/wiremock \
--extensions io.github.nilwurtz.GraphqlBodyMatcher
```

#### When building with `docker build`:
```dockerfile
FROM wiremock/wiremock:latest
COPY ./wiremock-graphql-extension-0.6.2-jar-with-dependencies.jar /var/wiremock/extensions/wiremock-graphql-extension-0.6.2-jar-with-dependencies.jar
COPY ./wiremock-graphql-extension-0.7.0-jar-with-dependencies.jar /var/wiremock/extensions/wiremock-graphql-extension-0.7.0-jar-with-dependencies.jar
CMD ["--extensions", "io.github.nilwurtz.GraphqlBodyMatcher"]
```

Expand Down
6 changes: 6 additions & 0 deletions e2e/fixtures/with-other/request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"query": "query GetHero($episode: Episode) { hero(episode: $episode) { name age } }",
"variables": {
"episode": "EMPIRE"
}
}
6 changes: 6 additions & 0 deletions e2e/fixtures/with-other/setup.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"query": "query GetHero($episode: Episode) { hero(episode: $episode) { name age } }",
"variables": {
"episode": "EMPIRE"
}
}
2 changes: 1 addition & 1 deletion e2e/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<dependency>
<artifactId>wiremock-graphql-extension</artifactId>
<groupId>io.github.nilwurtz</groupId>
<version>0.6.2</version>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.gauge</groupId>
Expand Down
22 changes: 22 additions & 0 deletions e2e/specs/with-other-mappings.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# With other mappings

## When using a mapping with this extension and normal mapping, graphql request should work
tags: remote
* Register a stub to return 200 upon receiving json <file:./fixtures/with-other/setup.json>
* Register a normal stub to return 200 upon receiving json "{\"foo\": \"bar\"}"
* Send a POST request to URL "/graphql" with body <file:./fixtures/with-other/request.json>
* The response status code should be "200"

## When using a mapping with this extension and normal mapping, normal request should work
tags: remote
* Register a stub to return 200 upon receiving json <file:./fixtures/with-other/setup.json>
* Register a normal stub to return 200 upon receiving json "{\"foo\": \"bar\"}"
* Send a POST request to URL "/graphql" with body "{\"foo\": \"bar\"}"
* The response status code should be "200"

## When using a mapping with this extension and normal mapping (change order), normal request should work
tags: remote
* Register a normal stub to return 200 upon receiving json "{\"foo\": \"bar\"}"
* Register a stub to return 200 upon receiving json <file:./fixtures/with-other/setup.json>
* Send a POST request to URL "/graphql" with body "{\"foo\": \"bar\"}"
* The response status code should be "200"
40 changes: 32 additions & 8 deletions e2e/src/test/kotlin/Steps.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,54 @@ class Steps {
@Step("Register a stub to return 200 upon receiving json <json>")
fun setupGraphqlJsonStub(json: String) {
// for remote
Datastore.client()?.register(post(urlEqualTo("/graphql"))
.andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.withRequest(json)).willReturn(ok()))
Datastore.client()?.register(
post(urlEqualTo("/graphql"))
.andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.withRequest(json)).willReturn(ok())
)
// for local
Datastore.localServer()
?.stubFor(post(urlEqualTo("/graphql"))
.andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.withRequest(json)).willReturn(ok()))
?.stubFor(
post(urlEqualTo("/graphql"))
.andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.withRequest(json))
.willReturn(ok())
)
}

@Step("Register a normal stub to return 200 upon receiving json <json>")
fun setupJsonPostStub(json: String) {
Datastore.client()?.register(
post(urlEqualTo("/graphql"))
.withRequestBody(equalToJson(json)).willReturn(ok())
)
Datastore.localServer()
?.stubFor(
post(urlEqualTo("/graphql"))
.withRequestBody(equalToJson(json))
.willReturn(ok())
)
}

@Step("Register a stub to return 200 upon receiving the query<query>")
fun setupGraphqlQueryStub(query: String) {
Datastore.localServer()
?.stubFor(post(urlEqualTo("/graphql"))
.andMatching(GraphqlBodyMatcher.withRequestQueryAndVariables(query)).willReturn(ok()))
?.stubFor(
post(urlEqualTo("/graphql"))
.andMatching(GraphqlBodyMatcher.withRequestQueryAndVariables(query)).willReturn(ok())
)
}

@Step("Register a stub to return 200 upon receiving the query<query> and variables<variables>")
fun setupGraphqlQueryAndVariables(query: String, variables: String) {
Datastore.localServer()
?.stubFor(post(urlEqualTo("/graphql"))
.andMatching(GraphqlBodyMatcher.withRequestQueryAndVariables(query, variables)).willReturn(ok()))
?.stubFor(
post(urlEqualTo("/graphql"))
.andMatching(GraphqlBodyMatcher.withRequestQueryAndVariables(query, variables)).willReturn(ok())
)
}

@Step("Send a POST request to URL <uri> with body <json>")
fun requestPost(uri: String, json: String) {
println("Sending request to ${Configuration.baseUrl + uri} with body $json")
HttpClient.newHttpClient().sendAsync(
HttpRequest.newBuilder(URI.create(Configuration.baseUrl + uri))
.POST(HttpRequest.BodyPublishers.ofString(json))
Expand Down
2 changes: 1 addition & 1 deletion wiremock-graphql-extension/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<artifactId>wiremock-graphql-extension</artifactId>
<groupId>io.github.nilwurtz</groupId>
<version>0.6.2</version>
<version>0.7.0</version>
<packaging>jar</packaging>
<name>wiremock-graphql-extension</name>
<description>A WireMock extension for handling GraphQL requests, allowing for easy mocking of GraphQL APIs in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.github.tomakehurst.wiremock.extension.Parameters
import com.github.tomakehurst.wiremock.http.Request
import com.github.tomakehurst.wiremock.matching.MatchResult
import com.github.tomakehurst.wiremock.matching.RequestMatcherExtension
import com.github.tomakehurst.wiremock.stubbing.SubEvent
import graphql.language.Document
import graphql.parser.Parser
import io.github.nilwurtz.exceptions.InvalidJsonException
Expand Down Expand Up @@ -57,15 +58,19 @@ class GraphqlBodyMatcher() : RequestMatcherExtension() {
}

/**
* Creates a Parameters instance containing the given raw JSON string expected in the GraphQL request.
*
* This method is used to set up JSON expected in remote requests. The expectedJson parameter should be a raw JSON string that encapsulates the expected query and optionally variables for the GraphQL request. This string is used to create a parameters object utilized internally in the GraphqlBodyMatcher.
*
* @param expectedJson A raw JSON string that contains the GraphQL query and optionally variables expected in the requests.
* @return A Parameters instance created based on the expected JSON string.
*/
* Creates a Parameters instance containing the given raw JSON string expected in the GraphQL request.
*
* This method is used to set up JSON expected in remote requests. The expectedJson parameter should be a raw JSON string that encapsulates the expected query and optionally variables for the GraphQL request. This string is used to create a parameters object utilized internally in the GraphqlBodyMatcher.
*
* @param expectedJson A raw JSON string that contains the GraphQL query and optionally variables expected in the requests.
* @return A Parameters instance created based on the expected JSON string.
* @throws InvalidJsonException if the given JSON is malformed.
* @throws InvalidQueryException if the given query is invalid.
*/
fun withRequest(expectedJson: String): Parameters {
return Parameters.one(expectedJsonKey, expectedJson)
// check if the json and query is valid
expectedJson.toJSONObject().graphqlQueryDocument()
return Parameters.one(expectedJsonKey, expectedJson)
}
}

Expand All @@ -82,20 +87,9 @@ class GraphqlBodyMatcher() : RequestMatcherExtension() {
* @throws InvalidQueryException if the given query inside the JSON is invalid.
*/
private fun initExpectedRequestJson(expectedJson: String) {
try {
expectedRequestJson = JSONObject(expectedJson)
// Attempt to parse and normalize the query to check for validity
expectedRequestJson.getString("query").run {
Parser().parseDocument(this)
}
} catch (e: JSONException) {
throw InvalidJsonException("Failed to parse the provided JSON string: $expectedJson", e)
} catch (e: Exception) {
throw InvalidQueryException(
"Failed to parse the provided GraphQL query: ${expectedRequestJson.getString("query")}",
e
)
}
expectedRequestJson = expectedJson.toJSONObject()
// Attempt to parse and normalize the query to check for validity
expectedRequestJson.graphqlQueryDocument()
}

/**
Expand All @@ -107,24 +101,27 @@ class GraphqlBodyMatcher() : RequestMatcherExtension() {
* @param parameters Additional parameters that may be used for matching.
* @return [MatchResult.exactMatch] if the request query and variables match the expected query and variables,
* [MatchResult.noMatch] otherwise.
* @throws InvalidJsonException if the request JSON or the expected JSON is invalid.
* @throws InvalidQueryException if the request query or the expected query is invalid.
*/
override fun match(request: Request, parameters: Parameters): MatchResult {
// for remote call
if (parameters.containsKey(expectedJsonKey)) {
expectedRequestJson = JSONObject(parameters.getString(expectedJsonKey))
}
val requestJson = JSONObject(request.bodyAsString)
try {
// for remote call
if (parameters.containsKey(expectedJsonKey)) {
expectedRequestJson = parameters.getString(expectedJsonKey).toJSONObject()
}
val requestJson = request.bodyAsString.toJSONObject()

val isQueryMatch =
requestJson.graphqlQueryDocument().normalize().toString() == expectedRequestJson.graphqlQueryDocument()
.normalize().toString()
val isVariablesMatch = requestJson.graphqlVariables().similar(expectedRequestJson.graphqlVariables())

val isQueryMatch =
requestJson.graphqlQueryDocument().normalize().toString() == expectedRequestJson.graphqlQueryDocument()
.normalize().toString()
val isVariablesMatch = requestJson.graphqlVariables().similar(expectedRequestJson.graphqlVariables())
return when {
isQueryMatch && isVariablesMatch -> MatchResult.exactMatch()
else -> MatchResult.noMatch(SubEvent.info("Request query is not matched. Expected query: ${expectedRequestJson.getString("query")}"))
}
} catch (e: Exception) {
return MatchResult.noMatch(SubEvent.warning(e.message))

return when {
isQueryMatch && isVariablesMatch -> MatchResult.exactMatch()
else -> MatchResult.noMatch()
}
}

Expand All @@ -133,10 +130,22 @@ class GraphqlBodyMatcher() : RequestMatcherExtension() {
}
}

private fun String.toJSONObject(): JSONObject {
try {
return JSONObject(this)
} catch (e: Exception) {
throw InvalidJsonException("Failed to parse the provided JSON string: $this", e)
}
}

private fun JSONObject.graphqlQueryDocument(): Document {
return this.optString("query")
.let { Parser().parseDocument(it) }
?: throw InvalidQueryException("Invalid query")
try {
return this.optString("query")
.let { Parser().parseDocument(it) }
?: throw InvalidQueryException("Invalid query")
} catch (e: Exception) {
throw InvalidQueryException("Failed to parse the provided GraphQL query: ${this.optString("query")}", e)
}
}

private fun JSONObject.graphqlVariables(): JSONObject {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,8 @@ class GraphqlBodyMatcherTest {
}

@Nested
@DisplayName("test `withRemoteRequestJson`")
inner class WithRemoteRequestJson {
@DisplayName("test `withRequest`")
inner class WithRequestTest {
@Test
@DisplayName("returns Parameters with expectedJsonKey")
fun testMatchedIdentical() {
Expand All @@ -304,5 +304,53 @@ class GraphqlBodyMatcherTest {
assertTrue(actual.containsKey("expectedJson"))
assertEquals(json, actual.getString("expectedJson"))
}

@Test
@DisplayName("Throws InvalidJsonException when json is empty")
fun testUnmatchedEmptyJson() {
val emptyJson = ""

assertThrows<InvalidJsonException> {
GraphqlBodyMatcher.withRequest(emptyJson)
}
}

@Test
@DisplayName("Throws InvalidJsonException when json is invalid")
fun testUnmatchedInvalidJson() {
val invalidJson = """
{
"query": "{ hero { name, age, height }"
""".trimIndent()

assertThrows<InvalidJsonException> {
GraphqlBodyMatcher.withRequest(invalidJson)
}
}

@Test
@DisplayName("Throws InvalidQueryException when query is invalid")
fun testUnmatchedInvalidQuery() {
// language=json
val invalidQueryJson = """
{
"query": "{ hero { name, age, height "
}
""".trimIndent()

assertThrows<InvalidQueryException> {
GraphqlBodyMatcher.withRequest(invalidQueryJson)
}
}

@Test
@DisplayName("Throws InvalidQueryException when query is empty")
fun testUnmatchedEmptyQuery() {
val json = """{ "query": "" }"""

assertThrows<InvalidQueryException> {
GraphqlBodyMatcher.withRequest(json)
}
}
}
}

1 comment on commit ed1522d

@nilwurtz
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolve #12

Please sign in to comment.