From b143d16f32d1f6bb9b8f381c855db457f5b85572 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 30 Aug 2018 12:21:14 +0200 Subject: [PATCH 1/2] Chaned the helper's bounds --- .../builder/handlebars/HandlebarsJsonPathHelper.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy index dffc6c9f72..efa9a6af10 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy @@ -16,13 +16,13 @@ import org.springframework.cloud.contract.verifier.builder.TestSideRequestTempla * @since 1.1.0 */ @CompileStatic -class HandlebarsJsonPathHelper implements Helper> { +class HandlebarsJsonPathHelper implements Helper { public static final String NAME = "jsonpath" public static final String REQUEST_MODEL_NAME = "request" @Override - Object apply(Map context, Options options) throws IOException { + Object apply(Object context, Options options) throws IOException { if (context instanceof Map) { // legacy Map oldContext = (Map) context From d23684d1c4f378bf5ad8ca2d6e224282286781d9 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 31 Aug 2018 12:05:22 +0200 Subject: [PATCH 2/2] Fixed backward compatibility of native WireMock helpers --- .../cloud/contract/spec/internal/Body.groovy | 4 ++ .../build.gradle | 8 +-- .../verifier/builder/MethodBodyBuilder.groovy | 21 ++++--- .../HandlebarsJsonPathHelper.groovy | 18 +++++- .../verifier/util/MapConverter.groovy | 2 + .../MockMvcMethodBodyBuilderSpec.groovy | 56 ++++++++++++++++++- 6 files changed, 95 insertions(+), 14 deletions(-) diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy index 100210ca99..407a37500f 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy @@ -53,6 +53,10 @@ class Body extends DslProperty { super(bodyAsValue) } + Body(String bodyAsValue) { + super(bodyAsValue, bodyAsValue) + } + Body(GString bodyAsValue) { super(bodyAsValue, bodyAsValue) } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle index d289534e48..7790251c72 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle @@ -23,7 +23,7 @@ ext { ] } -project.version = findProperty('verifierVersion') ?: '2.0.0.BUILD-SNAPSHOT' +project.version = findProperty('verifierVersion') apply plugin: 'groovy' apply from: "$rootDir/gradle/release.gradle" apply plugin: 'eclipse' @@ -32,8 +32,8 @@ apply plugin: 'checkstyle' group = 'org.springframework.cloud' -sourceCompatibility = 1.7 -targetCompatibility = 1.7 +sourceCompatibility = 1.8 +targetCompatibility = 1.8 repositories { mavenLocal() @@ -75,7 +75,7 @@ dependencies { } testCompile 'info.solidsoft.spock:spock-global-unroll:0.5.0' testCompile gradleTestKit() - checkstyle 'org.springframework.cloud:spring-cloud-build:1.0.2.RELEASE' + checkstyle 'org.springframework.cloud:spring-cloud-build:2.0.3.RELEASE' } 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 439fda17e0..7c26e8626a 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 @@ -367,6 +367,14 @@ abstract class MethodBodyBuilder { private void addJsonResponseBodyCheck(BlockBuilder bb, convertedResponseBody, BodyMatchers bodyMatchers) { appendJsonPath(bb, getResponseAsString()) + DocumentContext parsedRequestBody + if (contract.request?.body) { + def testSideRequestBody = MapConverter.getTestSideValues(contract.request.body) + parsedRequestBody = JsonPath.parse(testSideRequestBody) + if (convertedResponseBody instanceof String && !textContainsJsonPathTemplate(convertedResponseBody)) { + convertedResponseBody = templateProcessor.transform(contract.request, convertedResponseBody.toString()) + } + } Object copiedBody = cloneBody(convertedResponseBody) convertedResponseBody = JsonToJsonPathsConverter.removeMatchingJsonPaths(convertedResponseBody, bodyMatchers) // remove quotes from fromRequest objects before picking json paths @@ -374,11 +382,6 @@ abstract class MethodBodyBuilder { TestSideRequestTemplateModel.from(contract.request) : null convertedResponseBody = MapConverter.transformValues(convertedResponseBody, returnReferencedEntries(templateModel)) JsonPaths jsonPaths = new JsonToJsonPathsConverter(configProperties).transformToJsonPathWithTestsSideValues(convertedResponseBody) - DocumentContext parsedRequestBody - if (contract.request?.body) { - def requestBody = MapConverter.getTestSideValues(contract.request.body) - parsedRequestBody = JsonPath.parse(requestBody) - } jsonPaths.each { String method = it.method() method = processIfTemplateIsPresent(method, parsedRequestBody) @@ -444,8 +447,7 @@ abstract class MethodBodyBuilder { } protected String processIfTemplateIsPresent(String method, DocumentContext parsedRequestBody) { - if (templateProcessor.containsTemplateEntry(method) && - templateProcessor.containsJsonPathTemplateEntry(method) && contract.request?.body) { + if (textContainsJsonPathTemplate(method) && contract.request?.body) { // Unquoting the values of non strings String jsonPathEntry = templateProcessor.jsonPathFromTemplateEntry(method) Object object = parsedRequestBody.read(jsonPathEntry) @@ -458,6 +460,11 @@ abstract class MethodBodyBuilder { return method } + protected boolean textContainsJsonPathTemplate(String method) { + return templateProcessor.containsTemplateEntry(method) && + templateProcessor.containsJsonPathTemplateEntry(method) + } + protected void methodForEqualityCheck(BodyMatcher bodyMatcher, BlockBuilder bb, Object copiedBody) { String path = quotedAndEscaped(bodyMatcher.path()) Object retrievedValue = value(copiedBody, bodyMatcher) diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy index efa9a6af10..0a53494c6f 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/handlebars/HandlebarsJsonPathHelper.groovy @@ -1,6 +1,8 @@ package org.springframework.cloud.contract.verifier.builder.handlebars import com.github.tomakehurst.wiremock.extension.responsetemplating.helpers.WireMockHelpers +import org.apache.commons.logging.Log +import org.apache.commons.logging.LogFactory import wiremock.com.github.jknack.handlebars.Helper import wiremock.com.github.jknack.handlebars.Options import com.github.tomakehurst.wiremock.extension.responsetemplating.RequestTemplateModel @@ -18,12 +20,17 @@ import org.springframework.cloud.contract.verifier.builder.TestSideRequestTempla @CompileStatic class HandlebarsJsonPathHelper implements Helper { + private static final Log log = LogFactory.getLog(HandlebarsJsonPathHelper) + public static final String NAME = "jsonpath" public static final String REQUEST_MODEL_NAME = "request" @Override Object apply(Object context, Options options) throws IOException { if (context instanceof Map) { + if (log.isTraceEnabled()) { + log.trace("Will apply the legacy [jsonpath this] handlebars function") + } // legacy Map oldContext = (Map) context String jsonPath = options.param(0) @@ -35,7 +42,10 @@ class HandlebarsJsonPathHelper implements Helper { } throw new IllegalArgumentException("Unsupported model") } else if (context instanceof String) { - Object value = WireMockHelpers.jsonPath.apply(context, options) + if (log.isTraceEnabled()) { + log.trace("Will apply the native WireMock [jsonPath request.body] handlebars function") + } + Object value = WireMockHelpers.jsonPath.apply(prepareForJsonPathCheck(context), options) if (testSideModel(options)) { return processTestResponseValue(value) } @@ -59,12 +69,16 @@ class HandlebarsJsonPathHelper implements Helper { } private Object returnObjectForTest(TestSideRequestTemplateModel model, String jsonPath) { - String body = removeSurroundingQuotes(model.rawBody).replace('\\"', '"') + String body = prepareForJsonPathCheck(model.rawBody) DocumentContext documentContext = JsonPath.parse(body) Object value = documentContext.read(jsonPath) return processTestResponseValue(value) } + protected String prepareForJsonPathCheck(String body) { + return removeSurroundingQuotes(body).replace('\\"', '"') + } + private Object processTestResponseValue(Object value) { if (value instanceof Long) { return String.valueOf(value) + "L" 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 e5565a8f81..1bea23558a 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 @@ -69,6 +69,8 @@ class MapConverter { def json = new JsonSlurper().parseText(value) if (json instanceof Map) { return convert(json, closure) + } else if (json instanceof List) { + return transformValues(json, closure) } } catch (Exception ignore) { } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy index 1b96f457be..d324989607 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy @@ -447,7 +447,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } } - def "should generate assertions for array inside response body element with #methodBuilderName"() { + def "should generate assertions for array inside response body element with #methodBuilderName"() { given: Contract contractDsl = Contract.make { request { @@ -2628,6 +2628,60 @@ DocumentContext parsedJson = JsonPath.parse(json); "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | { String body -> body.contains('assertThat(response.getHeaderString("Authorization")).isEqualTo("foo secret bar");') } } + @Issue("#230") + def "should manage to reference request in response via WireMock native entries [#methodBuilderName]"() { + given: + //tag::template_contract[] + Contract contractDsl = Contract.make { + request { + method 'GET' + url('/api/v1/xxxx') { + queryParameters { + parameter("foo", "bar") + parameter("foo", "bar2") + } + } + headers { + header(authorization(), "secret") + header(authorization(), "secret2") + } + body(foo: "bar", baz: 5) + } + response { + status OK() + headers { + contentType(applicationJson()) + } + body(''' + { + "responseFoo": "{{{ jsonPath request.body '$.foo' }}}", + "responseBaz": {{{ jsonPath request.body '$.baz' }}}, + "responseBaz2": "Bla bla {{{ jsonPath request.body '$.foo' }}} bla bla" + } + '''.toString()) + } + } + //end::template_contract[] + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + and: + builder.appendTo(blockBuilder) + String test = blockBuilder.toString() + when: + SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test) + then: + !test.contains('''DslProperty''') + test.contains('''assertThatJson(parsedJson).field("['responseFoo']").isEqualTo("bar")''') + test.contains('''assertThatJson(parsedJson).field("['responseBaz']").isEqualTo(5)''') + test.contains('''assertThatJson(parsedJson).field("['responseBaz2']").isEqualTo("Bla bla bar bla bla")''') + 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) } + } + def "should generate JUnit assertions with cookies"() { given: MethodBodyBuilder builder = new MockMvcJUnitMethodBodyBuilder(contractDslWithCookiesValue, properties)