diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/GenericHttpBodyThen.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/GenericHttpBodyThen.java index 7520a5febc..e3eceed9a8 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/GenericHttpBodyThen.java +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/GenericHttpBodyThen.java @@ -58,12 +58,8 @@ class GenericHttpBodyThen implements Then, BodyMethodVisitor { endBodyBlock(this.blockBuilder); this.blockBuilder.addEmptyLine(); startBodyBlock(this.blockBuilder, "and:"); - Request request = metadata.getContract().getRequest(); this.thens.stream().filter(then -> then.accept(metadata)) .forEach(then -> then.apply(metadata)); - String newBody = this.templateProcessor.transform(request, - this.blockBuilder.toString()); - this.blockBuilder.updateContents(newBody); return this; } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JsonBodyVerificationBuilder.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JsonBodyVerificationBuilder.java index b36653b30a..3adedf6000 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JsonBodyVerificationBuilder.java +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JsonBodyVerificationBuilder.java @@ -188,16 +188,19 @@ class JsonBodyVerificationBuilder implements BodyMethodGeneration, ClassVerifier } private String className(Object retrievedValue) { - return retrievedValue.getClass().getName().startsWith("java.lang") ? - retrievedValue.getClass().getSimpleName() : retrievedValue.getClass().getName(); + return retrievedValue.getClass().getName().startsWith("java.lang") + ? retrievedValue.getClass().getSimpleName() + : retrievedValue.getClass().getName(); } private String objectToString(Object value) { if (value instanceof Long) { return String.valueOf(value).concat("L"); - } else if (value instanceof Double) { + } + else if (value instanceof Double) { return String.valueOf(value).concat("D"); - } else if (value instanceof BigDecimal) { + } + else if (value instanceof BigDecimal) { return quotedAndEscaped(value.toString()); } return String.valueOf(value); diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodPostProcessor.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodPostProcessor.java new file mode 100644 index 0000000000..d4b823f394 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodPostProcessor.java @@ -0,0 +1,21 @@ +/* + * Copyright 2013-2019 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 + * + * https://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. + */ + +package org.springframework.cloud.contract.verifier.builder; + +interface MethodPostProcessor extends MethodVisitor { + +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java index aed394cd6e..3494d58454 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java @@ -37,6 +37,8 @@ class SingleMethodBuilder { private List methodMetadata = new LinkedList<>(); + private List methodPostProcessors = new LinkedList<>(); + private List givens = new LinkedList<>(); private List whens = new LinkedList<>(); @@ -93,7 +95,9 @@ class SingleMethodBuilder { .then(new JavaRestAssuredThen(this.blockBuilder, this.generatedClassMetaData)) .then(new SpockRestAssuredThen(this.blockBuilder, - this.generatedClassMetaData)); + this.generatedClassMetaData)) + .methodPostProcessor(new TemplateUpdatingMethodPostProcessor( + this.blockBuilder)); } SingleMethodBuilder jaxRs() { @@ -101,7 +105,9 @@ class SingleMethodBuilder { .when(new JavaJaxRsWhen(this.blockBuilder, this.generatedClassMetaData)) .when(new SpockJaxRsWhen(this.blockBuilder, this.generatedClassMetaData)) .then(new JavaJaxRsThen(this.blockBuilder, this.generatedClassMetaData)) - .then(new SpockJaxRsThen(this.blockBuilder, this.generatedClassMetaData)); + .then(new SpockJaxRsThen(this.blockBuilder, this.generatedClassMetaData)) + .methodPostProcessor( + new TemplateUpdatingMethodPostProcessor(this.blockBuilder)); } SingleMethodBuilder messaging() { @@ -114,7 +120,8 @@ class SingleMethodBuilder { .then(new SpockMessagingWithBodyThen(this.blockBuilder, this.generatedClassMetaData)) .then(new SpockMessagingEmptyThen(this.blockBuilder, - this.generatedClassMetaData)); + this.generatedClassMetaData)) + .methodPostProcessor(new TemplateUpdatingMethodPostProcessor(this.blockBuilder)); // @formatter:on } @@ -133,6 +140,11 @@ class SingleMethodBuilder { return this; } + SingleMethodBuilder methodPostProcessor(MethodPostProcessor methodPostProcessor) { + this.methodPostProcessors.add(methodPostProcessor); + return this; + } + /** * Mutates the {@link BlockBuilder} to generate a methodBuilder * @return block builder with contents of a single methodBuilder @@ -165,6 +177,10 @@ class SingleMethodBuilder { visit(this.thens, metaData); }); this.blockBuilder.addEmptyLine(); + this.methodPostProcessors + .stream() + .filter(m -> m.accept(metaData)) + .forEach(m -> m.apply(metaData)); // } }); // @formatter:on diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/TemplateUpdatingMethodPostProcessor.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/TemplateUpdatingMethodPostProcessor.java new file mode 100644 index 0000000000..e6d8484c3a --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/TemplateUpdatingMethodPostProcessor.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2019 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 + * + * https://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. + */ + +package org.springframework.cloud.contract.verifier.builder; + +import org.springframework.cloud.contract.spec.internal.Request; +import org.springframework.cloud.contract.verifier.file.SingleContractMetadata; +import org.springframework.cloud.contract.verifier.template.HandlebarsTemplateProcessor; +import org.springframework.cloud.contract.verifier.template.TemplateProcessor; + +class TemplateUpdatingMethodPostProcessor implements MethodPostProcessor { + + private final BlockBuilder blockBuilder; + + private final TemplateProcessor templateProcessor; + + TemplateUpdatingMethodPostProcessor(BlockBuilder blockBuilder) { + this.blockBuilder = blockBuilder; + this.templateProcessor = new HandlebarsTemplateProcessor(); + } + + @Override + public MethodVisitor apply(SingleContractMetadata metadata) { + Request request = metadata.getContract().getRequest(); + String newBody = this.templateProcessor.transform(request, + this.blockBuilder.toString()); + this.blockBuilder.updateContents(newBody); + return this; + } + + @Override + public boolean accept(SingleContractMetadata metadata) { + return this.templateProcessor.containsTemplateEntry(this.blockBuilder.toString()); + } + +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy index 25239502f4..b1bd7a46ad 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy @@ -29,6 +29,7 @@ import org.springframework.cloud.contract.spec.internal.DslProperty import org.springframework.cloud.contract.spec.internal.Headers import org.springframework.cloud.contract.verifier.util.ContentType import org.springframework.cloud.contract.verifier.util.ContentUtils +import org.springframework.cloud.contract.verifier.util.MapConverter import org.springframework.cloud.contract.verifier.util.NamesUtil import org.springframework.util.Assert diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy index bc7057ef7e..ef18833794 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy @@ -3012,4 +3012,46 @@ DocumentContext parsedJson = JsonPath.parse(json); } "webclient" | { properties.testMode = TestMode.WEBTESTCLIENT } } + + @Issue('#1163') + def 'should resolve from request evaluation even if there is no response body [#methodBuilderName]'() { + given: + Contract contractDsl = Contract.make { + request { + method PUT() + url '/frauds/name' + body([ + name: $(anyAlphaUnicode()) + ]) + headers { + contentType("application/json") + } + } + response { + status OK() + headers { + header(contentType(), "${fromRequest().header(contentType())}") + } + } + } + methodBuilder() + when: + String test = singleTestGenerator(contractDsl) + then: + SyntaxChecker.tryToCompile(methodBuilderName, test) + and: + !test.contains('''{{{request.headers.Content-Type.[0]}}}''') + where: + methodBuilderName | methodBuilder + "spock" | { properties.testFramework = TestFramework.SPOCK } + "testng" | { properties.testFramework = TestFramework.TESTNG } + "mockmvc" | { properties.testMode = TestMode.MOCKMVC } + "jaxrs-spock" | { + properties.testFramework = TestFramework.SPOCK; properties.testMode = TestMode.JAXRSCLIENT + } + "jaxrs" | { + properties.testFramework = TestFramework.JUNIT; properties.testMode = TestMode.JAXRSCLIENT + } + "webclient" | { properties.testMode = TestMode.WEBTESTCLIENT } + } }