Merge branch '1.0.x'

This commit is contained in:
Marcin Grzejszczak
2017-01-11 19:13:59 +01:00
12 changed files with 151 additions and 76 deletions

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.contract.verifier.builder
import com.jayway.jsonpath.JsonPath
import com.jayway.jsonpath.PathNotFoundException
import groovy.json.JsonOutput
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
@@ -311,19 +312,17 @@ abstract class MethodBodyBuilder {
bb.startBlock()
// for the rest we'll do JsonPath matching in brute force
bodyMatchers.jsonPathMatchers().each {
if (it.value()) {
if (it.value() || it.matchingType() == MatchingType.EQUALITY) {
String comparisonMethod = it.matchingType() == MatchingType.EQUALITY ? "isEqualTo" : "matches"
String valueAsParam = it.value() instanceof String ? quotedAndEscaped(it.value().toString()) : it.value().toString()
String classToCastTo = "${it.value().class.simpleName}.class"
Object retrievedValue = value(copiedBody, it)
String valueAsParam = retrievedValue instanceof String ? quotedAndEscaped(retrievedValue.toString()) : retrievedValue.toString()
String classToCastTo = "${retrievedValue.class.simpleName}.class"
String path = quotedAndEscaped(it.path())
String method = "assertThat(parsedJson.read(${path}, ${classToCastTo})).${comparisonMethod}(${valueAsParam})"
bb.addLine(postProcessJsonPathCall(method))
addColonIfRequired(bb)
} else {
Object elementFromBody = JsonPath.parse(copiedBody).read(it.path())
if (!elementFromBody) {
throw new IllegalStateException("Entry for the provided JSON path [${it.path()}] doesn't exist in the body [${JsonOutput.toJson(copiedBody)}]")
}
Object elementFromBody = value(copiedBody, it)
if (it.minTypeOccurrence() || it.maxTypeOccurrence()) {
checkType(bb, it, elementFromBody)
String method = "assertThat(parsedJson.read(${quotedAndEscaped(it.path())}, java.util.Collection.class).size()).${sizeCheckMethod(it)}"
@@ -338,6 +337,21 @@ abstract class MethodBodyBuilder {
processBodyElement(bb, "", convertedResponseBody)
}
protected Object value(def body, BodyMatcher bodyMatcher) {
if (bodyMatcher.matchingType() == MatchingType.EQUALITY || !bodyMatcher.value()) {
return retrieveObjectByPath(body, bodyMatcher.path())
}
return bodyMatcher.value()
}
protected Object retrieveObjectByPath(def body, String path) {
try {
return JsonPath.parse(body).read(path)
} catch (PathNotFoundException e) {
throw new IllegalStateException("Entry for the provided JSON path [${path}] doesn't exist in the body [${JsonOutput.toJson(body)}]", e)
}
}
protected void checkType(BlockBuilder bb, BodyMatcher it, Object elementFromBody) {
String method = "assertThat((Object) parsedJson.read(${quotedAndEscaped(it.path())})).isInstanceOf(${classToCheck(elementFromBody).name}.class)"
bb.addLine(postProcessJsonPathCall(method))

View File

@@ -75,8 +75,8 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
}
ContentType contentType = tryToGetContentType(request.body.clientValue, request.headers)
if (contentType == ContentType.JSON) {
def body = getMatchingStrategyFromBody(request.body)?.clientValue
body = JsonToJsonPathsConverter.removeMatchingJsonPaths(body, request.matchers)
def originalBody = getMatchingStrategyFromBody(request.body)?.clientValue
def body = JsonToJsonPathsConverter.removeMatchingJsonPaths(originalBody, request.matchers)
JsonPaths values = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(body)
if (values.empty && !request.matchers?.hasMatchers()) {
requestPattern.withRequestBody(WireMock.equalToJson(JsonOutput.toJson(getMatchingStrategy(request.body.clientValue).clientValue), false, false))
@@ -87,7 +87,7 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
}
if (request.matchers?.hasMatchers()) {
request.matchers.jsonPathMatchers().each {
String newPath = JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(it)
String newPath = JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(it, originalBody)
requestPattern.withRequestBody(WireMock.matchingJsonPath(newPath.replace("\\\\", "\\")))
}
}

View File

@@ -16,19 +16,18 @@
package org.springframework.cloud.contract.verifier.util
import com.jayway.jsonpath.DocumentContext
import com.jayway.jsonpath.JsonPath
import com.jayway.jsonpath.PathNotFoundException
import com.toomuchcoding.jsonassert.JsonAssertion
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.util.logging.Slf4j
import org.springframework.cloud.contract.spec.internal.BodyMatcher
import org.springframework.cloud.contract.spec.internal.BodyMatchers
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.MatchingType
import org.springframework.cloud.contract.spec.internal.OptionalProperty
import org.springframework.cloud.contract.spec.internal.*
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import java.util.regex.Pattern
/**
* I would like to apologize to anyone who is reading this class. Since JSON is a hectic structure
* this class is also hectic. The idea is to traverse the JSON structure and build a set of
@@ -71,12 +70,14 @@ class JsonToJsonPathsConverter {
* @return json with removed entries
*/
static def removeMatchingJsonPaths(def json, BodyMatchers bodyMatchers) {
def jsonCopy = json.clone()
DocumentContext context = JsonPath.parse(jsonCopy)
if (bodyMatchers?.hasMatchers()) {
bodyMatchers.jsonPathMatchers().each { BodyMatcher matcher ->
JsonPath.parse(json).delete(matcher.path())
context.delete(matcher.path())
}
}
return json
return jsonCopy
}
/**
@@ -86,23 +87,31 @@ class JsonToJsonPathsConverter {
* @param bodyMatcher
* @return JSON path that checks the regex for its last element
*/
static String convertJsonPathAndRegexToAJsonPath(BodyMatcher bodyMatcher) {
static String convertJsonPathAndRegexToAJsonPath(BodyMatcher bodyMatcher, def body = null) {
String path = bodyMatcher.path()
Object value = bodyMatcher.value()
if (!value) {
if (value == null && bodyMatcher.matchingType() != MatchingType.EQUALITY) {
return path
}
int lastIndexOfDot = path.lastIndexOf(".")
String toLastDot = path.substring(0, lastIndexOfDot)
String fromLastDot = path.substring(lastIndexOfDot + 1)
String comparison = createComparison(bodyMatcher, value)
String comparison = createComparison(bodyMatcher, value, body)
return "${toLastDot}[?(@.${fromLastDot} ${comparison})]"
}
private static String createComparison(BodyMatcher bodyMatcher, Object value) {
private static String createComparison(BodyMatcher bodyMatcher, Object value, def body) {
if (bodyMatcher.matchingType() == MatchingType.EQUALITY) {
String wrappedValue = value instanceof Number ? value : "'${value.toString()}'"
return "== ${wrappedValue}"
if (!body) {
throw new IllegalStateException("Body hasn't been passed")
}
try {
Object retrievedValue = JsonPath.parse(body).read(bodyMatcher.path())
String wrappedValue = retrievedValue instanceof Number ? retrievedValue : "'${retrievedValue.toString()}'"
return "== ${wrappedValue}"
} catch (PathNotFoundException e) {
throw new IllegalStateException("Value [${bodyMatcher.path()}] not found in JSON [${JsonOutput.toJson(body)}]", e)
}
} else {
return "=~ /(${value})/"
}

View File

@@ -51,9 +51,9 @@ class MockMvcMethodBodyBuilderWithMatchersSpec extends Specification implements
])
stubMatchers {
jsonPath('$.duck', byRegex("[0-9]{3}"))
jsonPath('$.duck', byValue(123))
jsonPath('$.duck', byEquality())
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
jsonPath('$.alpha', byValue("abc"))
jsonPath('$.alpha', byEquality())
jsonPath('$.number', byRegex(number()))
jsonPath('$.aBoolean', byRegex(anyBoolean()))
jsonPath('$.date', byDate())
@@ -90,10 +90,10 @@ class MockMvcMethodBodyBuilderWithMatchersSpec extends Specification implements
// asserts the jsonpath value against manual regex
jsonPath('$.duck', byRegex("[0-9]{3}"))
// asserts the jsonpath value against the provided value
jsonPath('$.duck', byValue(123))
jsonPath('$.duck', byEquality())
// asserts the jsonpath value against some default regex
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
jsonPath('$.alpha', byValue("abc"))
jsonPath('$.alpha', byEquality())
jsonPath('$.number', byRegex(number()))
jsonPath('$.aBoolean', byRegex(anyBoolean()))
// asserts vs inbuilt time related regex

View File

@@ -766,17 +766,19 @@ class JsonToJsonPathsConverterSpec extends Specification {
def "should convert a json path with value to a equality checking json path without quotes for numbers"() {
given:
String jsonPath = '$.a.b.c.d'
Integer value = 1234
and:
def body = [ a: [ b: [ c: [ d: 1234 ] ] ] ]
expect:
'$.a.b.c[?(@.d == 1234)]' == JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher(MatchingType.EQUALITY, jsonPath, value))
'$.a.b.c[?(@.d == 1234)]' == JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher(MatchingType.EQUALITY, jsonPath, null), body)
}
def "should convert a json path with value to a equality checking json path with quotes for strings"() {
given:
String jsonPath = '$.a.b.c.d'
String value = "foo"
and:
def body = [ a: [ b: [ c: [ d: "foo" ] ] ] ]
expect:
'$.a.b.c[?(@.d == \'foo\')]' == JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher(MatchingType.EQUALITY, jsonPath, value))
'$.a.b.c[?(@.d == \'foo\')]' == JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher(MatchingType.EQUALITY, jsonPath, null), body)
}
def "should return the path if no value is provided"() {
@@ -786,6 +788,30 @@ class JsonToJsonPathsConverterSpec extends Specification {
'$.a.b.c.d' == JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher(MatchingType.REGEX, jsonPath, null))
}
def "should throw an exception when null body is passed to check for equality"() {
given:
String jsonPath = '$.a.b.c.d'
and:
def body = null
when:
JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher(MatchingType.EQUALITY, jsonPath, null), body)
then:
IllegalStateException e = thrown(IllegalStateException)
e.message.contains("Body")
}
def "should throw an exception when nonexisting jsonpath is passed to check for equality"() {
given:
String jsonPath = '$.a.b.c.d'
and:
def body = [ foo: "bar" ]
when:
JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher(MatchingType.EQUALITY, jsonPath, null), body)
then:
IllegalStateException e = thrown(IllegalStateException)
e.message.contains("not found")
}
private BodyMatcher matcher(final MatchingType matchingType, final String jsonPath, final Object value) {
return new BodyMatcher() {
@Override