From 4e08f77996b60b9ee3d33b5454b749614818cc36 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 21 Mar 2018 11:35:29 +0100 Subject: [PATCH] escapes all generated strings by Xeger so that the code is java compatible related to gh-589 fixes gh-588 --- .../cloud/contract/spec/internal/Input.groovy | 5 +-- .../spec/internal/OutputMessage.groovy | 3 +- .../contract/spec/internal/Request.groovy | 4 +- .../contract/spec/internal/Response.groovy | 5 ++- .../contract/spec/util/RegexpUtils.groovy | 2 +- .../repackaged/nl/flotsam/xeger/Xeger.java | 10 +++++ .../builder/MethodBodyBuilderSpec.groovy | 37 +++++++++++++++++++ 7 files changed, 58 insertions(+), 8 deletions(-) diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Input.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Input.groovy index f919eaa356..465df6461d 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Input.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Input.groovy @@ -16,14 +16,13 @@ package org.springframework.cloud.contract.spec.internal +import java.util.regex.Pattern + import groovy.transform.CompileStatic import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import groovy.transform.TypeChecked import repackaged.nl.flotsam.xeger.Xeger - -import java.util.regex.Pattern - /** * Represents an input for messaging. The input can be a message or some * action inside the application. diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/OutputMessage.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/OutputMessage.groovy index b70634af7b..9f0baa32bf 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/OutputMessage.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/OutputMessage.groovy @@ -20,6 +20,7 @@ import groovy.transform.CompileStatic import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import groovy.transform.TypeChecked +import org.apache.commons.lang3.StringEscapeUtils import repackaged.nl.flotsam.xeger.Xeger import java.util.regex.Pattern @@ -74,7 +75,7 @@ class OutputMessage extends Common { DslProperty value(ServerDslProperty server) { Object value = server.clientValue if (server.clientValue instanceof Pattern) { - value = new Xeger(((Pattern)server.clientValue).pattern()).generate() + value = StringEscapeUtils.escapeJava(new Xeger(((Pattern)server.clientValue).pattern()).generate()) } return new DslProperty(value, server.serverValue) } diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Request.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Request.groovy index cd43e99818..ffdc9ba666 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Request.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Request.groovy @@ -20,6 +20,8 @@ import groovy.transform.CompileStatic import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import groovy.transform.TypeChecked +import org.apache.commons.lang3.StringEscapeUtils + import org.springframework.cloud.contract.spec.util.RegexpUtils import repackaged.nl.flotsam.xeger.Xeger @@ -186,7 +188,7 @@ class Request extends Common { DslProperty value(ClientDslProperty client) { Object clientValue = client.clientValue if (client.clientValue instanceof Pattern && client.isSingleValue()) { - clientValue = new Xeger(((Pattern)client.clientValue).pattern()).generate() + clientValue = StringEscapeUtils.escapeJava(new Xeger(((Pattern)client.clientValue).pattern()).generate()) } else if (client.clientValue instanceof Pattern && !client.isSingleValue()) { clientValue = client.serverValue } diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Response.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Response.groovy index 008f92043d..7fa887a448 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Response.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Response.groovy @@ -16,14 +16,15 @@ package org.springframework.cloud.contract.spec.internal +import java.util.regex.Pattern + import groovy.transform.CompileStatic import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import groovy.transform.TypeChecked -import org.springframework.cloud.contract.spec.util.RegexpUtils import repackaged.nl.flotsam.xeger.Xeger -import java.util.regex.Pattern +import org.springframework.cloud.contract.spec.util.RegexpUtils /** * Represents the response side of the HTTP communication * diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/util/RegexpUtils.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/util/RegexpUtils.groovy index f60ff554c6..9ca0e2f8e6 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/util/RegexpUtils.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/util/RegexpUtils.groovy @@ -21,7 +21,7 @@ import groovy.transform.CompileStatic import java.util.regex.Pattern /** - * Useful utility methods to work with regular expresisons + * Useful utility methods to work with regular expressions * * @since 1.0.2 */ diff --git a/spring-cloud-contract-spec/src/main/groovy/repackaged/nl/flotsam/xeger/Xeger.java b/spring-cloud-contract-spec/src/main/groovy/repackaged/nl/flotsam/xeger/Xeger.java index 81743c649f..d26abc3f7f 100644 --- a/spring-cloud-contract-spec/src/main/groovy/repackaged/nl/flotsam/xeger/Xeger.java +++ b/spring-cloud-contract-spec/src/main/groovy/repackaged/nl/flotsam/xeger/Xeger.java @@ -27,6 +27,7 @@ import dk.brics.automaton.Automaton; import dk.brics.automaton.RegExp; import dk.brics.automaton.State; import dk.brics.automaton.Transition; +import org.apache.commons.lang3.StringUtils; /** * An object that will generate text from a regular expression. In a way, it's the opposite of a regular expression @@ -59,6 +60,15 @@ public class Xeger { .replace("\\s", "[ \t\r\n]"); // Used s="White"Space this.automaton = new RegExp(pattern).toAutomaton(); this.random = random; + String generatedCharsSysProp = System + .getProperty("springCloudContractGeneratedCharsFromRegex"); + String generatedCharsEnvVar = System + .getenv("SPRING_CLOUD_CONTRACT_GENERATED_CHARS_FROM_REGEX"); + if (StringUtils.isNotEmpty(generatedCharsSysProp)) { + ITERATION_LIMIT = Integer.parseInt(generatedCharsSysProp); + } else if (StringUtils.isNotEmpty(generatedCharsEnvVar)) { + ITERATION_LIMIT = Integer.parseInt(generatedCharsEnvVar); + } } /** 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 dcb113d825..154f7c33de 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 @@ -141,6 +141,43 @@ DocumentContext parsedJson = JsonPath.parse(json); "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } } + @Issue('#521') + def "should always escape generated chars [#methodBuilderName]"() { + expect: + [1..200].each { + Contract contractDsl = Contract.make { + request { + method GET() + urlPath('/v1/users') { + queryParameters { + parameter 'userId': value(regex(nonBlank())) + } + } + } + response { + status 200 + body([ + ok: value(regex(nonBlank())) + ]) + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + + builder.appendTo(blockBuilder) + def test = blockBuilder.toString() + + assert !test.contains('REGEXP>>') + SyntaxChecker.tryToCompile(methodBuilderName, blockBuilder.toString()) + } + 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) } + } + @Issue('#269') def "should work with execute and keys with dots [#methodBuilderName]"() { given: