Shouldn't always parse each object

without this change, when a body is parsed and it contains a JSON in a JSON, then we parse it. That's because our logic hasn't actually ever considered that one can have a valid JSON string in a JSON.

with this change we're introducing a parsing closure. When we know that the body is a Map and we know that we're parsing a JSON, then we don't want to parse any Strings inside that map. We assume that the users know what they do, so if an element in the map is a String, then we just pass it.

fixes gh-652
This commit is contained in:
Marcin Grzejszczak
2019-01-20 22:23:34 +01:00
parent 1eed5c606d
commit 0d0d2ea11c
8 changed files with 219 additions and 143 deletions

View File

@@ -1,22 +1,22 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2018 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
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://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.
*
* 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.cloud.contract.verifier.builder
import com.jayway.jsonpath.DocumentContext
import com.jayway.jsonpath.JsonPath
import com.jayway.jsonpath.PathNotFoundException
@@ -38,7 +38,6 @@ import org.springframework.cloud.contract.verifier.template.TemplateProcessor
import org.springframework.cloud.contract.verifier.util.JsonPaths
import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter
import org.springframework.cloud.contract.verifier.util.MapConverter
/**
* @author Marcin Grzejszczak
* @author Olga Maciaszek-Sharma
@@ -83,9 +82,11 @@ class JsonBodyVerificationBuilder implements BodyMethodGeneration, ClassVerifier
boolean shouldCommentOutBDDBlocks) {
appendJsonPath(bb, responseString)
DocumentContext parsedRequestBody
boolean dontParseStrings = convertedResponseBody instanceof Map
Closure parsingClosure = dontParseStrings ? Closure.IDENTITY : MapConverter.JSON_PARSING_CLOSURE
if (contract.request?.body) {
def testSideRequestBody = MapConverter
.getTestSideValues(contract.request.body)
.getTestSideValues(contract.request.body, parsingClosure)
parsedRequestBody = JsonPath.parse(testSideRequestBody)
if (convertedResponseBody instanceof String && !
textContainsJsonPathTemplate(convertedResponseBody)) {
@@ -100,9 +101,9 @@ class JsonBodyVerificationBuilder implements BodyMethodGeneration, ClassVerifier
TestSideRequestTemplateModel templateModel = contract.request?.body ?
TestSideRequestTemplateModel.from(contract.request) : null
convertedResponseBody = MapConverter.transformValues(convertedResponseBody,
returnReferencedEntries(templateModel))
returnReferencedEntries(templateModel), parsingClosure)
JsonPaths jsonPaths = new JsonToJsonPathsConverter(configProperties).
transformToJsonPathWithTestsSideValues(convertedResponseBody)
transformToJsonPathWithTestsSideValues(convertedResponseBody, parsingClosure)
jsonPaths.each {
String method = it.method()
method = processIfTemplateIsPresent(method, parsedRequestBody)

View File

@@ -1,17 +1,18 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2018 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
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://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.
*
* 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.cloud.contract.verifier.builder
@@ -116,7 +117,8 @@ abstract class MessagingMethodBodyBuilder extends MethodBodyBuilder {
}
protected String getBodyAsString() {
Object bodyValue = extractServerValueFromBody(inputMessage.messageBody.serverValue)
ContentType contentType = contentType()
Object bodyValue = extractServerValueFromBody(contentType, inputMessage.messageBody.serverValue)
if (bodyValue instanceof FromFileProperty) {
FromFileProperty fileProperty = (FromFileProperty) bodyValue
return fileProperty.isByte() ?

View File

@@ -1,17 +1,18 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2018 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
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://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.
*
* 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.cloud.contract.verifier.builder
@@ -30,6 +31,7 @@ import org.springframework.cloud.contract.spec.internal.DslProperty
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.FromFileProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.Headers
import org.springframework.cloud.contract.spec.internal.MatchingStrategy
import org.springframework.cloud.contract.spec.internal.NamedProperty
import org.springframework.cloud.contract.spec.internal.OptionalProperty
@@ -418,7 +420,9 @@ abstract class MethodBodyBuilder implements ClassVerifier {
convertedResponseBody = extractValue(convertedResponseBody as GString, contentType, { Object o -> o instanceof DslProperty ? o.serverValue : o })
}
if (TEXT != contentType && FORM != contentType) {
convertedResponseBody = MapConverter.getTestSideValues(convertedResponseBody)
boolean dontParseStrings = contentType == JSON && convertedResponseBody instanceof Map
Closure parsingClosure = dontParseStrings ? Closure.IDENTITY : MapConverter.JSON_PARSING_CLOSURE
convertedResponseBody = MapConverter.getTestSideValues(convertedResponseBody, parsingClosure)
} else {
convertedResponseBody = StringEscapeUtils.escapeJava(convertedResponseBody.toString())
}
@@ -510,15 +514,18 @@ abstract class MethodBodyBuilder implements ClassVerifier {
* Converts the passed body into ints server side representation. All {@link DslProperty}
* will return their server side values
*/
protected Object extractServerValueFromBody(bodyValue) {
protected Object extractServerValueFromBody(ContentType contentType, Object bodyValue) {
if (bodyValue instanceof GString) {
return extractValue(bodyValue, contentType(), GET_SERVER_VALUE)
return extractValue(bodyValue, contentType, GET_SERVER_VALUE)
}
return MapConverter.transformValues(bodyValue, GET_SERVER_VALUE)
boolean dontParseStrings = contentType == JSON && bodyValue instanceof Map
Closure parsingClosure = dontParseStrings ? Closure.IDENTITY : MapConverter.JSON_PARSING_CLOSURE
return MapConverter.transformValues(bodyValue, GET_SERVER_VALUE, parsingClosure)
}
protected ContentType contentType() {
return ContentUtils.recognizeContentTypeFromTestHeader(this.contract.request?.headers)
Headers headers = this.contract.request?.headers ?: this.contract.input?.messageHeaders
return ContentUtils.recognizeContentTypeFromTestHeader(headers)
}
/**

View File

@@ -1,17 +1,18 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2018 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
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://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.
*
* 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.cloud.contract.verifier.builder
@@ -133,7 +134,7 @@ abstract class RequestProcessingMethodBodyBuilder extends MethodBodyBuilder {
case ExecutionProperty:
case FromFileProperty:
body = request.body?.serverValue
break;
break
default:
body = getBodyAsString()
}
@@ -243,8 +244,9 @@ abstract class RequestProcessingMethodBodyBuilder extends MethodBodyBuilder {
@Override
protected String getBodyAsString() {
Object bodyValue = extractServerValueFromBody(request.body.serverValue)
if (contentType() == ContentType.FORM) {
ContentType contentType = contentType()
Object bodyValue = extractServerValueFromBody(contentType, request.body.serverValue)
if (contentType == ContentType.FORM) {
if (bodyValue instanceof Map) {
// [a:3, b:4] == "a=3&b=4"
return ((Map) bodyValue).collect {

View File

@@ -1,17 +1,18 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2018 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
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://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.
*
* 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.cloud.contract.verifier.util
@@ -178,7 +179,7 @@ class ContentUtils {
if (contentType == UNKNOWN) {
return getClientContentType(bodyAsValue)
}
return contentType;
return contentType
}
static ContentType getClientContentType(Map bodyAsValue) {
@@ -277,10 +278,11 @@ class ContentUtils {
return transformXMLStringValue(valueProvider(dslProperty), valueProvider)
}
protected static Object convertDslPropsToTemporaryRegexPatterns(parsedJson) {
protected static Object convertDslPropsToTemporaryRegexPatterns(Object parsedJson,
Closure parsingClosure = MapConverter.JSON_PARSING_CLOSURE) {
MapConverter.transformValues(parsedJson, { Object value ->
return transformJSONStringValue(value, GET_TEST_SIDE)
})
}, parsingClosure)
}
private static Object convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) {

View File

@@ -1,17 +1,18 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2018 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
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://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.
*
* 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.cloud.contract.verifier.util
@@ -23,7 +24,6 @@ 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.transform.CompileStatic
import groovy.util.logging.Commons
@@ -233,56 +233,62 @@ class JsonToJsonPathsConverter {
}
}
JsonPaths transformToJsonPathWithTestsSideValues(def json) {
return transformToJsonPathWithValues(json, SERVER_SIDE)
JsonPaths transformToJsonPathWithTestsSideValues(def json,
Closure parsingClosure = MapConverter.JSON_PARSING_CLOSURE) {
return transformToJsonPathWithValues(json, SERVER_SIDE, parsingClosure)
}
JsonPaths transformToJsonPathWithStubsSideValues(def json) {
return transformToJsonPathWithValues(json, CLIENT_SIDE)
JsonPaths transformToJsonPathWithStubsSideValues(def json,
Closure parsingClosure = MapConverter.JSON_PARSING_CLOSURE) {
return transformToJsonPathWithValues(json, CLIENT_SIDE, parsingClosure)
}
static JsonPaths transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(def json) {
static JsonPaths transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(def json,
Closure parsingClosure = MapConverter.JSON_PARSING_CLOSURE) {
return new JsonToJsonPathsConverter()
.transformToJsonPathWithValues(json, CLIENT_SIDE)
.transformToJsonPathWithValues(json, CLIENT_SIDE, parsingClosure)
}
private JsonPaths transformToJsonPathWithValues(def json, boolean clientSide) {
private JsonPaths transformToJsonPathWithValues(def json, boolean clientSide,
Closure parsingClosure = MapConverter.JSON_PARSING_CLOSURE) {
if(!json) {
return new JsonPaths()
}
JsonPaths pathsAndValues = [] as Set
Object convertedJson = MapConverter.getClientOrServerSideValues(json, clientSide)
Object jsonWithPatterns = ContentUtils.convertDslPropsToTemporaryRegexPatterns(convertedJson)
Object convertedJson = MapConverter.getClientOrServerSideValues(json, clientSide, parsingClosure)
Object jsonWithPatterns = ContentUtils.
convertDslPropsToTemporaryRegexPatterns(convertedJson, parsingClosure)
MethodBufferingJsonVerifiable methodBufferingJsonPathVerifiable =
new DelegatingJsonVerifiable(JsonAssertion.assertThat(JsonOutput.toJson(jsonWithPatterns))
.withoutThrowingException())
traverseRecursivelyForKey(jsonWithPatterns, methodBufferingJsonPathVerifiable)
traverseRecursivelyForKey(jsonWithPatterns, methodBufferingJsonPathVerifiable,
{ MethodBufferingJsonVerifiable key, Object value ->
if (value instanceof ExecutionProperty || !(key instanceof FinishedDelegatingJsonVerifiable)) {
return
}
pathsAndValues.add(key)
}
}, parsingClosure)
return pathsAndValues
}
protected def traverseRecursively(Class parentType, MethodBufferingJsonVerifiable key, def value, Closure closure) {
protected def traverseRecursively(Class parentType, MethodBufferingJsonVerifiable key, def value,
Closure closure, Closure parsingClosure = MapConverter.JSON_PARSING_CLOSURE) {
value = ContentUtils.returnParsedObject(value)
if (value instanceof String && value) {
try {
def json = new JsonSlurper().parseText(value)
def json = parsingClosure(value)
if (json instanceof Map) {
return convertWithKey(parentType, key, json, closure)
return convertWithKey(parentType, key, json, closure, parsingClosure)
}
} catch (Exception ignore) {
return runClosure(closure, key, value)
}
} else if (isAnEntryWithNonCollectionLikeValue(value)) {
return convertWithKey(List, key, value as Map, closure)
return convertWithKey(List, key, value as Map, closure, parsingClosure)
} else if (isAnEntryWithoutNestedStructures(value)) {
return convertWithKey(List, key, value as Map, closure)
return convertWithKey(List, key, value as Map, closure, parsingClosure)
} else if (value instanceof Map && !value.isEmpty()) {
return convertWithKey(Map, key, value as Map, closure)
return convertWithKey(Map, key, value as Map, closure, parsingClosure)
} else if (value instanceof Map && value.isEmpty()) {
return runClosure(closure, key.isEmpty(), value)
// JSON with a list of primitives ["a", "b", "c"] in root issue #266
@@ -290,28 +296,28 @@ class JsonToJsonPathsConverter {
addSizeVerificationForListWithPrimitives(key, closure, value)
value.each {
traverseRecursively(Object, key.arrayField().contains(ContentUtils.returnParsedObject(it)),
ContentUtils.returnParsedObject(it), closure)
ContentUtils.returnParsedObject(it), closure, parsingClosure)
}
// JSON containing list of primitives { "partners":[ { "role":"AGENT", "payment_methods":[ "BANK", "CASH" ] } ]
} else if (value instanceof List && listContainsOnlyPrimitives(value)) {
addSizeVerificationForListWithPrimitives(key, closure, value)
value.each {
traverseRecursively(Object, valueToAsserter(key.arrayField(), ContentUtils.returnParsedObject(it)),
ContentUtils.returnParsedObject(it), closure)
ContentUtils.returnParsedObject(it), closure, parsingClosure)
}
} else if (value instanceof List && !value.empty) {
MethodBufferingJsonVerifiable jsonPathVerifiable = createAsserterFromList(key, value)
addSizeVerificationForListWithPrimitives(key, closure, value)
value.each { def element ->
traverseRecursively(List, createAsserterFromListElement(jsonPathVerifiable, ContentUtils.returnParsedObject(element)),
ContentUtils.returnParsedObject(element), closure)
ContentUtils.returnParsedObject(element), closure, parsingClosure)
}
return value
} else if (value instanceof List && value.empty) {
return runClosure(closure, key, value)
} else if (key.isIteratingOverArray()) {
traverseRecursively(Object, key.arrayField().contains(ContentUtils.returnParsedObject(value)),
ContentUtils.returnParsedObject(value), closure)
ContentUtils.returnParsedObject(value), closure, parsingClosure)
}
try {
return runClosure(closure, key, value)
@@ -435,7 +441,8 @@ class JsonToJsonPathsConverter {
}
}
private Map convertWithKey(Class parentType, MethodBufferingJsonVerifiable parentKey, Map map, Closure closureToExecute) {
private Map convertWithKey(Class parentType, MethodBufferingJsonVerifiable parentKey, Map map,
Closure closureToExecute, Closure parsingClosure) {
return map.collectEntries {
Object entrykey, value ->
def convertedValue = ContentUtils.returnParsedObject(value)
@@ -443,7 +450,7 @@ class JsonToJsonPathsConverter {
convertedValue instanceof List ? list(convertedValue, entrykey, parentKey) :
convertedValue instanceof Map ? parentKey.field(new ShouldTraverse(entrykey)) :
valueToAsserter(parentKey.field(entrykey), convertedValue)
, convertedValue, closureToExecute)]
, convertedValue, closureToExecute, parsingClosure)]
}
}
@@ -456,8 +463,9 @@ class JsonToJsonPathsConverter {
parentKey.array(entrykey)
}
private void traverseRecursivelyForKey(def json, MethodBufferingJsonVerifiable rootKey, Closure closure) {
traverseRecursively(Map, rootKey, json, closure)
private void traverseRecursivelyForKey(def json, MethodBufferingJsonVerifiable rootKey,
Closure closure, Closure parsingClosure = MapConverter.JSON_PARSING_CLOSURE) {
traverseRecursively(Map, rootKey, json, closure, parsingClosure)
}
protected MethodBufferingJsonVerifiable valueToAsserter(MethodBufferingJsonVerifiable key, Object value) {

View File

@@ -1,26 +1,29 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2018 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
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://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.
*
* 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.cloud.contract.verifier.util
import groovy.json.JsonSlurper
import org.springframework.cloud.contract.spec.internal.DslProperty
import org.springframework.cloud.contract.spec.internal.FromFileProperty
import org.springframework.cloud.contract.verifier.template.HandlebarsTemplateProcessor
import org.springframework.cloud.contract.verifier.template.TemplateProcessor
/**
* Converts an object into either client or server side representation.
* Iterates over the structure of an object (depending on whether it's an
@@ -35,6 +38,9 @@ class MapConverter {
public static final boolean STUB_SIDE = true
public static final boolean TEST_SIDE = false
public static final Closure JSON_PARSING_CLOSURE = { String value ->
new JsonSlurper().parseText(value)
}
private final TemplateProcessor templateProcessor
@@ -61,35 +67,36 @@ class MapConverter {
*
* Returns the transformed structure
*/
static def transformValues(def value, Closure closure) {
static def transformValues(def value, Closure closure,
Closure parsingClosure = JSON_PARSING_CLOSURE) {
if (value instanceof String && value) {
try {
def json = new JsonSlurper().parseText(value)
def json = parsingClosure(value)
if (json instanceof Map) {
return convert(json, closure)
return convert(json, closure, parsingClosure)
} else if (json instanceof List) {
return transformValues(json, closure)
return transformValues(json, closure, parsingClosure)
}
} catch (Exception ignore) {
}
return extractValue(value, closure)
} else if (value instanceof Map) {
return convert(value as Map, closure)
return convert(value as Map, closure, parsingClosure)
} else if (value instanceof List) {
return value.collect({ transformValues(it, closure) })
return value.collect({ transformValues(it, closure, parsingClosure) })
}
return transformValue(closure, value)
return transformValue(closure, value, parsingClosure)
}
/**
* Transforms a value with the given closure. Needs to be protected, otherwise
* method access exception will occur at runtime.
*/
protected static Object transformValue(Closure closure, Object value) {
protected static Object transformValue(Closure closure, Object value, Closure parsingClosure) {
return extractValue(value, { Object val->
Object newValue = closure(val)
if (newValue instanceof Map || newValue instanceof List || newValue instanceof String && value) {
return transformValues(newValue, closure)
return transformValues(newValue, closure, parsingClosure)
}
return newValue
})
@@ -103,10 +110,10 @@ class MapConverter {
}
}
private static Map convert(Map map, Closure closure) {
private static Map convert(Map map, Closure closure, Closure parsingClosure) {
return map.collectEntries {
key, value ->
[key, transformValues(value, closure)]
[key, transformValues(value, closure, parsingClosure)]
}
}
@@ -114,12 +121,14 @@ class MapConverter {
* If {@code clientSide} is {@code true} returns the client side value for the
* provided object
*/
static Object getClientOrServerSideValues(json, boolean clientSide) {
return transformValues(json) {
static Object getClientOrServerSideValues(json, boolean clientSide,
Closure parsingClosure = JSON_PARSING_CLOSURE) {
return transformValues(json, {
if (it instanceof DslProperty) {
DslProperty dslProperty = ((DslProperty) it)
return clientSide ?
getClientOrServerSideValues(dslProperty.clientValue, clientSide) : getClientOrServerSideValues(dslProperty.serverValue, clientSide)
getClientOrServerSideValues(dslProperty.clientValue, clientSide, parsingClosure) :
getClientOrServerSideValues(dslProperty.serverValue, clientSide, parsingClosure)
} else if (it instanceof GString) {
ContentType type = new MapConverter().templateProcessor.containsJsonPathTemplateEntry(
ContentUtils.extractValueForGString(it, ContentUtils.GET_TEST_SIDE).toString()
@@ -127,7 +136,8 @@ class MapConverter {
return ContentUtils.extractValue(it , type, {
if (it instanceof DslProperty) {
return clientSide ?
getClientOrServerSideValues((it as DslProperty).clientValue, clientSide) : getClientOrServerSideValues((it as DslProperty).serverValue, clientSide)
getClientOrServerSideValues((it as DslProperty).clientValue, clientSide, parsingClosure) :
getClientOrServerSideValues((it as DslProperty).serverValue, clientSide, parsingClosure)
}
return it
})
@@ -135,14 +145,14 @@ class MapConverter {
return it.isByte() ? it.asBytes() : it.asString()
}
return it
}
}, parsingClosure)
}
static Object getStubSideValues(json) {
return getClientOrServerSideValues(json, STUB_SIDE)
static Object getStubSideValues(json, Closure parsingClosure = JSON_PARSING_CLOSURE) {
return getClientOrServerSideValues(json, STUB_SIDE, parsingClosure)
}
static Object getTestSideValues(json) {
return getClientOrServerSideValues(json, TEST_SIDE)
static Object getTestSideValues(json, Closure parsingClosure = JSON_PARSING_CLOSURE) {
return getClientOrServerSideValues(json, TEST_SIDE, parsingClosure)
}
}

View File

@@ -1,17 +1,18 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2018 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
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://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.
*
* 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.cloud.contract.verifier.builder
@@ -1130,4 +1131,47 @@ DocumentContext parsedJson = JsonPath.parse(json);
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties, classDataForMethod) }
}
@Issue("#652")
def "should not parse json in a json [#methodBuilderName]"() {
given:
Contract contractDsl = Contract.make {
name("insertSomething_ShouldReturnHttp200")
description("POST should do sth")
request {
method 'POST'
url "/foo"
body(
value: "{}"
)
headers {
contentType(applicationJson())
}
}
response {
status 200
headers { contentType(applicationJson()) }
body(
value: "{}"
)
}
}
MethodBodyBuilder builder = methodBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
String test = blockBuilder.toString()
SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test)
!test.contains(''':{}}''')
test.contains("""assertThatJson(parsedJson).field("['value']").isEqualTo("{}")""")
and:
stubMappingIsValidWireMockStub(contractDsl)
where:
methodBuilderName | methodBuilder
"MockMvcSpockMethodBuilder" | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties, classDataForMethod) }
"MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties, classDataForMethod) }
"JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties, classDataForMethod) }
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties, classDataForMethod) }
}
}