From 4a8f847a5b4b82482bb26722ccbfe98af9b6df31 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 18 Apr 2019 17:46:32 +0400 Subject: [PATCH] Not parsing the header or cookie values fixes gh-1034 --- .../builder/JUnitMethodBodyBuilder.groovy | 4 +- .../JaxRsClientJUnitMethodBodyBuilder.groovy | 4 +- .../verifier/builder/MethodBodyBuilder.groovy | 13 +++++++ ...kMethodRequestProcessingBodyBuilder.groovy | 4 +- .../verifier/util/MapConverter.groovy | 39 ++++++++++++------- .../builder/MethodBodyBuilderSpec.groovy | 36 +++++++++++++++++ 6 files changed, 80 insertions(+), 20 deletions(-) diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy index 83b8b24a69..e9769c1906 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy @@ -145,12 +145,12 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder @Override protected String getHeaderString(Header header) { - return ".header(${getTestSideValue(header.name)}, ${getTestSideValue(header.serverValue)})" + return ".header(${getTestSideForNonBodyValue(header.name)}, ${getTestSideForNonBodyValue(header.serverValue)})" } @Override protected String getCookieString(Cookie cookie) { - return ".cookie(${getTestSideValue(cookie.key)}, ${getTestSideValue(cookie.serverValue)})" + return ".cookie(${getTestSideForNonBodyValue(cookie.key)}, ${getTestSideForNonBodyValue(cookie.serverValue)})" } @Override diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientJUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientJUnitMethodBodyBuilder.groovy index d859d92986..41a77a3aa6 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientJUnitMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientJUnitMethodBodyBuilder.groovy @@ -123,7 +123,7 @@ class JaxRsClientJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder { return } if (header.name == 'Content-Type' || header.name == 'Accept') return - bb.addLine(".header(\"${header.name}\", \"${header.serverValue}\")") + bb.addLine(".header(\"${header.name}\", ${quotedAndEscaped(header.serverValue as String)})") } } @@ -133,7 +133,7 @@ class JaxRsClientJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder { return } - bb.addLine(".cookie(\"${cookie.key}\", \"${cookie.serverValue}\")") + bb.addLine(".cookie(\"${cookie.key}\", ${quotedAndEscaped(cookie.serverValue as String)})") } } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy index 6b469e3cd6..3b8481cb81 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy @@ -735,6 +735,19 @@ abstract class MethodBodyBuilder { return '"' + MapConverter.getTestSideValues(object).toString() + '"' } + /** + * Depending on the object type extracts the test side values and + * combines them into a String representation. Unlike the body transformation + * done via {@link MethodBodyBuilder#getTestSideValue(java.lang.Object)} will + * not try to guess the type of the value of the header (e.g. if it's a JSON). + */ + protected String getTestSideForNonBodyValue(Object object) { + if (object instanceof ExecutionProperty) { + return getTestSideValue((ExecutionProperty) object) + } + return quotedAndEscaped(MapConverter.getTestSideValuesForNonBody(object).toString()) + } + /** * Extracts the executable test side values and * returns the code of the executable diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy index cb2c443060..9bd492b610 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy @@ -120,12 +120,12 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing @Override protected String getHeaderString(Header header) { - return ".header(${getTestSideValue(header.name)}, ${getTestSideValue(header.serverValue)})" + return ".header(${getTestSideForNonBodyValue(header.name)}, ${getTestSideForNonBodyValue(header.serverValue)})" } @Override protected String getCookieString(Cookie cookie) { - return ".cookie(${getTestSideValue(cookie.key)}, ${getTestSideValue(cookie.serverValue)})" + return ".cookie(${getTestSideForNonBodyValue(cookie.key)}, ${getTestSideForNonBodyValue(cookie.serverValue)})" } @Override diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MapConverter.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MapConverter.groovy index 4c6140d0eb..89f58cb024 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MapConverter.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MapConverter.groovy @@ -54,32 +54,35 @@ class MapConverter { } } + protected static final Closure JSON_TEXT_TRANSFORMATION = { Object input -> new JsonSlurper().parseText(input)} + /** - * Iterates over the structure of the object and executes the closure + * Iterates over the structure of the object and executes the objectTransformation * on each element of that structure. * * Returns the transformed structure */ - static def transformValues(def value, Closure closure) { + static def transformValues(def value, Closure objectTransformation, Closure textParser = JSON_TEXT_TRANSFORMATION) { if (value instanceof String && value) { try { - def json = new JsonSlurper().parseText(value) - if (json instanceof Map) { - return convert(json, closure) - } else if (json instanceof List) { - return transformValues(json, closure) + def parsed = textParser(value) + if (parsed instanceof Map) { + return convert(parsed, objectTransformation) + } else if (parsed instanceof List) { + return transformValues(parsed, objectTransformation) } } catch (Exception ignore) { } - return extractValue(value, closure) + return extractValue(value, objectTransformation) } else if (value instanceof Map) { - return convert(value as Map, closure) + return convert(value as Map, objectTransformation) } else if (value instanceof List) { - return value.collect({ transformValues(it, closure) }) + return value.collect({ transformValues(it, objectTransformation) }) } - return transformValue(closure, value) + return transformValue(objectTransformation, value) } + /** * Transforms a value with the given closure. Needs to be protected, otherwise * method access exception will occur at runtime. @@ -113,8 +116,8 @@ 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 textTansformation = JSON_TEXT_TRANSFORMATION) { + return transformValues(json, { if (it instanceof DslProperty) { DslProperty dslProperty = ((DslProperty) it) return clientSide ? @@ -132,7 +135,7 @@ class MapConverter { }) } return it - } + }, textTansformation) } static Object getStubSideValues(json) { @@ -142,4 +145,12 @@ class MapConverter { static Object getTestSideValues(json) { return getClientOrServerSideValues(json, TEST_SIDE) } + + static Object getStubSideValuesForNonBody(object) { + return getClientOrServerSideValues(object, STUB_SIDE, Closure.IDENTITY) + } + + static Object getTestSideValuesForNonBody(object) { + return getClientOrServerSideValues(object, TEST_SIDE, Closure.IDENTITY) + } } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy index e3f8dc90f2..7f722df638 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy @@ -1026,4 +1026,40 @@ DocumentContext parsedJson = JsonPath.parse(json); "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } } + @Issue("#1034") + def "should not escape headers as jsons [#methodBuilderName]"() { + given: + Contract contractDsl = Contract.make { + name 'my name' + request { + method POST() + urlPath '/my-url' + headers { + contentType(applicationJson()) + accept(applicationJson()) + header('my-json-header', ''' { "value": "123" } ''') + } + } + response { + status OK() + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + String test = blockBuilder.toString() + SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test) + !test.contains('''[value:123]''') + and: + stubMappingIsValidWireMockStub(contractDsl) + where: + methodBuilderName | methodBuilder + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } + } + }