diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index 49df619015..dbd0663ac9 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -6,7 +6,7 @@ import io.codearte.accurest.dsl.GroovyDsl import io.codearte.accurest.dsl.internal.* import io.codearte.accurest.util.ContentType import io.codearte.accurest.util.MapConverter -import io.codearte.accurest.util.JsonPathJsonConverter +import io.codearte.accurest.util.JsonToJsonPathsConverter import io.codearte.accurest.util.JsonPaths import static io.codearte.accurest.util.ContentUtils.* @@ -86,7 +86,7 @@ abstract class SpockMethodBodyBuilder { } if (contentType == ContentType.JSON) { appendJsonPath(bb, responseAsString) - JsonPaths jsonPaths = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(responseBody) + JsonPaths jsonPaths = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(responseBody) jsonPaths.each { it.buildJsonPathComparison('parsedJson').each { bb.addLine(it) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy index 923b614f7a..c87a1cebac 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy @@ -9,14 +9,14 @@ import groovy.transform.TypeCheckingMode import io.codearte.accurest.dsl.internal.* import io.codearte.accurest.util.ContentType import io.codearte.accurest.util.ContentUtils -import io.codearte.accurest.util.JsonPathJsonConverter +import io.codearte.accurest.util.JsonToJsonPathsConverter import io.codearte.accurest.util.JsonPaths import io.codearte.accurest.util.MapConverter import java.util.regex.Pattern import static io.codearte.accurest.util.ContentUtils.* -import static io.codearte.accurest.util.RegexpBuilders.buildGStringRegexpMatch +import static io.codearte.accurest.util.RegexpBuilders.buildGStringRegexpForStubSide import static io.codearte.accurest.util.RegexpBuilders.buildJSONRegexpMatch @TypeChecked @@ -53,7 +53,7 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { } ContentType contentType = tryToGetContentType() if (contentType == ContentType.JSON) { - JsonPaths values = JsonPathJsonConverter.transformToJsonPathWithStubsSideValues(getMatchingStrategyFromBody(request.body)?.clientValue) + JsonPaths values = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValues(getMatchingStrategyFromBody(request.body)?.clientValue) if (values.empty) { requestPattern.bodyPatterns = [new ValuePattern(jsonCompareMode: org.skyscreamer.jsonassert.JSONCompareMode.LENIENT, equalToJson: JsonOutput.toJson(getMatchingStrategy(request.body.clientValue).clientValue) ) ] @@ -183,7 +183,7 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { case ContentType.JSON: return new MatchingStrategy(buildJSONRegexpMatch(value), MatchingStrategy.Type.MATCHING) case ContentType.UNKNOWN: - return new MatchingStrategy(buildGStringRegexpMatch(value), MatchingStrategy.Type.MATCHING) + return new MatchingStrategy(buildGStringRegexpForStubSide(value), MatchingStrategy.Type.MATCHING) case ContentType.XML: throw new IllegalStateException("XML pattern matching is not implemented yet") } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy index 210668a2a9..432dee266d 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy @@ -9,7 +9,7 @@ class RegexPatterns { private static final Pattern IP_ADDRESS = Pattern.compile('([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])'); private static final Pattern HOSTNAME_PATTERN = Pattern.compile('((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?'); - private static final Pattern EMAIL = Pattern.compile('[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}'); + private static final Pattern EMAIL = Pattern.compile('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}'); private static final Pattern URL = Pattern.compile('((www\\.|(http|https|ftp|news|file)+\\:\\/\\/)[_.a-z0-9-]+\\.[a-z0-9\\/_:@=.+?,##%&~-]*[^.|\\\'|\\# |!|\\(|?|,| |>|<|;|\\)])'); String ipAddress() { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathJsonConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy similarity index 90% rename from accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathJsonConverter.groovy rename to accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy index 28625a1cbf..6a749346a7 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathJsonConverter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy @@ -8,7 +8,7 @@ import java.util.regex.Pattern /** * @author Marcin Grzejszczak */ -class JsonPathJsonConverter { +class JsonToJsonPathsConverter { private static final Boolean SERVER_SIDE = false private static final Boolean CLIENT_SIDE = true @@ -42,11 +42,18 @@ class JsonPathJsonConverter { private static Object getClientOrServerSideValues(json, boolean clientSide) { return MapConverter.transformValues(json) { - boolean dslProp = it instanceof DslProperty - if (dslProp) { + if (it instanceof DslProperty) { DslProperty dslProperty = ((DslProperty) it) return clientSide ? getClientOrServerSideValues(dslProperty.clientValue, clientSide) : getClientOrServerSideValues(dslProperty.serverValue, clientSide) + } else if (it instanceof GString) { + return ContentUtils.extractValue(it , null, { + if (it instanceof DslProperty) { + return clientSide ? + getClientOrServerSideValues((it as DslProperty).clientValue, clientSide) : getClientOrServerSideValues((it as DslProperty).serverValue, clientSide) + } + return it + }) } return it } @@ -142,6 +149,8 @@ class JsonPathJsonConverter { protected static String compareWith(Object value) { if (value instanceof Pattern) { return """=~ /${(value as Pattern).pattern()}/""" + } else if (value instanceof GString) { + return """=~ /${RegexpBuilders.buildGStringRegexpForTestSide(value)}/""" } return """== ${potentiallyWrappedWithQuotesValue(value)}""" } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy index 47cb595220..6c8b703fca 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy @@ -10,29 +10,48 @@ import static org.apache.commons.lang3.StringEscapeUtils.escapeJson public class RegexpBuilders { - public static String buildGStringRegexpMatch(GString gString) { + public static String buildGStringRegexpForStubSide(GString gString) { new GStringImpl( - gString.values.collect(this.&buildGStringRegexpMatch) as Object[], + gString.values.collect(this.&buildGStringRegexpForStubSide) as Object[], gString.strings.collect(this.&escapeSpecialRegexChars) as String[] ) } - public static String buildGStringRegexpMatch(Pattern pattern) { + public static String buildGStringRegexpForStubSide(Pattern pattern) { return pattern.pattern() } - public static String buildGStringRegexpMatch(DslProperty dslProperty) { - return buildGStringRegexpMatch(dslProperty.clientValue) + public static String buildGStringRegexpForStubSide(DslProperty dslProperty) { + return buildGStringRegexpForStubSide(dslProperty.clientValue) } - public static String buildGStringRegexpMatch(Object o) { + public static String buildGStringRegexpForStubSide(Object o) { return escapeSpecialRegexChars(o.toString()) } + public static String buildGStringRegexpForTestSide(GString gString) { + new GStringImpl( + gString.values.collect(this.&buildGStringRegexpForTestSide) as Object[], + gString.strings.collect(this.&escapeSpecialRegexChars) as String[] + ) + } + + public static String buildGStringRegexpForTestSide(Pattern pattern) { + return pattern.pattern() + } + + public static String buildGStringRegexpForTestSide(DslProperty dslProperty) { + return buildGStringRegexpForTestSide(dslProperty.clientValue) + } + + public static String buildGStringRegexpForTestSide(Object o) { + return o.toString().replaceAll('\\\\', '\\\\\\\\') + } + private final static Pattern SPECIAL_REGEX_CHARS = Pattern.compile('[{}()\\[\\].+*?^$\\\\|]') private static String escapeSpecialRegexChars(String str) { - return SPECIAL_REGEX_CHARS.matcher(str).replaceAll('\\\\$0') + return SPECIAL_REGEX_CHARS.matcher(str).replaceAll('\\\\\\\\$0') } private final static String WS = /\s*/ diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index dcbf988168..5d25d94874 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -429,4 +429,39 @@ class MockMvcSpockMethodBuilderSpec extends Specification { spockTest.contains('''$.errors[*][?(@.property == 'bank_account_number')]''') spockTest.contains('''$.errors[*][?(@.message == 'incorrect_format')]''') } + + def "should resolve properties in GString with regular expression"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + priority 1 + request { + method 'POST' + url '/users/password' + headers { + header 'Content-Type': 'application/json' + } + body( + email: $(client(regex(email())), server('not.existing@user.com')), + callback_url: $(client(regex(hostname())), server('http://partners.com')) + ) + } + response { + status 404 + headers { + header 'Content-Type': 'application/json' + } + body( + code: 4, + message: "User not found by email = [${value(server(regex(email())), client('not.existing@user.com'))}]" + ) + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains('''$[?(@.message =~ /User not found by email = \\\\[[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,4}\\\\]/)]''') + } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonPathJsonConverterSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonToJsonPathsConverterSpec.groovy similarity index 89% rename from accurest-core/src/test/groovy/io/codearte/accurest/util/JsonPathJsonConverterSpec.groovy rename to accurest-core/src/test/groovy/io/codearte/accurest/util/JsonToJsonPathsConverterSpec.groovy index a7e9f621d7..5348c77fd5 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonPathJsonConverterSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonToJsonPathsConverterSpec.groovy @@ -11,12 +11,12 @@ import spock.lang.Unroll import java.util.regex.Pattern -class JsonPathJsonConverterSpec extends Specification { +class JsonToJsonPathsConverterSpec extends Specification { @Unroll def 'should convert a json with list as root to a map of path to value'() { when: - JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) then: pathAndValues['''$[*].some.nested[?(@.json == 'with value')]'''] == 'with value' pathAndValues['''$[*].some.nested[?(@.anothervalue == 4)]'''] == 4 @@ -94,7 +94,7 @@ class JsonPathJsonConverterSpec extends Specification { } ''' when: - JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) then: pathAndValues['''$.some.nested[?(@.json == 'with value')]'''] == 'with value' pathAndValues['''$.some.nested[?(@.anothervalue == 4)]'''] == 4 @@ -112,7 +112,7 @@ class JsonPathJsonConverterSpec extends Specification { } ''' when: - JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) then: pathAndValues['''$.items[?(@ == 'HOP')]'''] == 'HOP' and: @@ -131,7 +131,7 @@ class JsonPathJsonConverterSpec extends Specification { } ''' when: - JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) then: pathAndValues['''$.errors[*][?(@.property == 'email')]'''] == 'email' pathAndValues['''$.errors[*][?(@.message == 'inconsistent value')]'''] == 'inconsistent value' @@ -175,7 +175,7 @@ class JsonPathJsonConverterSpec extends Specification { ] ] when: - JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(json) + JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(json) then: pathAndValues['''$[*].some.nested[?(@.json == 'with value')]'''] == 'with value' pathAndValues['''$[*].some.nested[?(@.anothervalue == 4)]'''] == 4 diff --git a/build.gradle b/build.gradle index 7f78c6ad8a..c1739aa58c 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,8 @@ scmVersion { } allprojects { - project.version = scmVersion.version + //project.version = scmVersion.version + project.version = '0.9.1-SNAPSHOT' } apply plugin: 'io.codearte.nexus-staging'