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:
@@ -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)
|
||||
|
||||
@@ -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() ?
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user