Merge pull request #93 from dstepanov/jsonMatching

Generate JSON regexp match as a single regexp. Fixes #92
This commit is contained in:
Marcin Grzejszczak
2015-06-25 10:21:35 +02:00
3 changed files with 183 additions and 51 deletions

View File

@@ -1,5 +1,4 @@
package io.codearte.accurest.dsl
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import io.codearte.accurest.dsl.internal.Body
@@ -13,11 +12,12 @@ import io.codearte.accurest.util.ContentType
import java.util.regex.Pattern
import static io.codearte.accurest.util.ContentUtils.extractValue
import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader
import static io.codearte.accurest.util.ContentUtils.getEqualsTypeFromContentType
import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent
import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader
import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromMatchingStrategy
import static io.codearte.accurest.util.RegexpBuilders.buildGStringRegexpMatch
import static io.codearte.accurest.util.RegexpBuilders.buildJSONRegexpMatch
@TypeChecked
@PackageScope
@@ -93,58 +93,73 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy {
return [appendBodyPattern(matchingStrategy)]
}
private List<Map<String, Object>> appendBodyPatterns(List<MatchingStrategy> matchingStrategies) {
return matchingStrategies.collect { appendBodyPattern(it) }
}
private List<Map<String, Object>> appendBodyPatterns(GString gString) {
if (containsPattern(gString)) {
Object value = extractValue(gString, { DslProperty dslProperty -> dslProperty.clientValue })
return appendBodyPatterns(extractReqexpMatching(value))
}
return appendBodyPatterns(new MatchingStrategy(gString, getEqualsTypeFromContentTypeHeader()))
}
private List<Map<String, Object>> appendBodyPatterns(Object bodyValue) {
return appendBodyPatterns(new MatchingStrategy(bodyValue, MatchingStrategy.Type.EQUAL_TO))
return appendBodyPatterns(new MatchingStrategy(bodyValue, getEqualsTypeFromContentTypeHeader()))
}
private Map<String, Object> appendBodyPattern(MatchingStrategy matchingStrategy) {
MatchingStrategy.Type type = matchingStrategy.type
Object value= matchingStrategy.clientValue
Object value = matchingStrategy.clientValue
ContentType contentType = recognizeContentTypeFromMatchingStrategy(type)
if (contentType == ContentType.UNKNOWN && type == MatchingStrategy.Type.EQUAL_TO) {
contentType = recognizeContentTypeFromContent(value)
type = getEqualsTypeFromContentType(contentType)
}
Map<String, ? extends Object> result = [(type.name): parseBody(value, contentType)]
if (type == MatchingStrategy.Type.EQUAL_TO_JSON && matchingStrategy.jsonCompareMode) {
if (containsPattern(value)) {
return appendBodyRegexpMatchPattern(value, contentType)
}
return buildMatchPattern(new MatchingStrategy(parseBody(value, contentType), type))
}
private Map<String, Object> appendBodyRegexpMatchPattern(Object value, ContentType contentType) {
switch (contentType) {
case ContentType.JSON:
return buildMatchPattern(new MatchingStrategy(buildJSONRegexpMatch(value), MatchingStrategy.Type.MATCHING))
case ContentType.UNKNOWN:
return buildMatchPattern(new MatchingStrategy(buildGStringRegexpMatch(value), MatchingStrategy.Type.MATCHING))
case ContentType.XML:
throw new IllegalStateException("XML pattern matching is not implemented yet")
}
}
private Map<String, Object> buildMatchPattern(MatchingStrategy matchingStrategy) {
Map<String, ? extends Object> result = [(matchingStrategy.type.name): matchingStrategy.clientValue.toString()]
if (matchingStrategy.type == MatchingStrategy.Type.EQUAL_TO_JSON && matchingStrategy.jsonCompareMode) {
return result << [jsonCompareMode : (matchingStrategy.jsonCompareMode.toString())]
}
return result
}
private boolean containsPattern(GString bodyAsValue) {
return bodyAsValue.values.collect { it instanceof DslProperty ? it.clientValue : it }
.find { it instanceof Pattern }
return containsPattern(bodyAsValue.values)
}
private List<MatchingStrategy> extractReqexpMatching(Object responseBodyObject) {
def matchingStrategies = new ArrayList<MatchingStrategy>()
if (responseBodyObject instanceof GString) {
return [new MatchingStrategy(responseBodyObject, MatchingStrategy.Type.MATCHING)]
} else if (responseBodyObject instanceof Map) {
responseBodyObject.each { k, v ->
if (v instanceof List) {
v.each {
matchingStrategies.addAll(extractReqexpMatching((Map<String, Object>)it))
}
} else {
matchingStrategies.add(new MatchingStrategy(/.*${k}":.?"?${v}"?.*/, MatchingStrategy.Type.MATCHING))
}
}
}
return matchingStrategies
private boolean containsPattern(Map map) {
return containsPattern(map.entrySet())
}
private boolean containsPattern(Collection collection) {
return collection.collect(this.&containsPattern).inject { a, b -> a || b }
}
private boolean containsPattern(Object[] objects) {
return containsPattern(objects.toList())
}
private boolean containsPattern(Map.Entry entry) {
return containsPattern(entry.value)
}
private boolean containsPattern(DslProperty dslProperty) {
return containsPattern(dslProperty.clientValue)
}
private boolean containsPattern(Pattern pattern) {
return true
}
private boolean containsPattern(Object o) {
return false
}
private MatchingStrategy.Type getEqualsTypeFromContentTypeHeader() {

View File

@@ -0,0 +1,72 @@
package io.codearte.accurest.util
import io.codearte.accurest.dsl.internal.DslProperty
import org.codehaus.groovy.runtime.GStringImpl
import java.util.regex.Pattern
import static io.codearte.accurest.util.ContentUtils.extractValue
import static org.apache.commons.lang3.StringEscapeUtils.escapeJson
public class RegexpBuilders {
public static String buildGStringRegexpMatch(GString gString) {
new GStringImpl(
gString.values.collect(this.&buildGStringRegexpMatch) as Object[],
gString.strings.collect(this.&escapeSpecialRegexChars) as String[]
)
}
public static String buildGStringRegexpMatch(Pattern pattern) {
return pattern.pattern()
}
public static String buildGStringRegexpMatch(DslProperty dslProperty) {
return buildGStringRegexpMatch(dslProperty.clientValue)
}
public static String buildGStringRegexpMatch(Object o) {
return escapeSpecialRegexChars(o.toString())
}
private final static Pattern SPECIAL_REGEX_CHARS = Pattern.compile('[{}()\\[\\].+*?^$\\\\|]')
private static String escapeSpecialRegexChars(String str) {
return SPECIAL_REGEX_CHARS.matcher(str).replaceAll('\\\\$0')
}
private final static String WS = /\s*/
public static String buildJSONRegexpMatch(GString gString) {
return buildJSONRegexpMatch(extractValue(gString, ContentType.JSON, { DslProperty dslProperty -> dslProperty.clientValue }))
}
public static String buildJSONRegexpMatch(Map jsonMap) {
return WS + "\\{" + jsonMap.collect(this.&buildJSONRegexpMatch).join(",") + "\\}" + WS
}
public static String buildJSONRegexpMatch(List jsonList) {
return WS + "\\[" + jsonList.collect(this.&buildJSONRegexpMatch).join(",") + "\\]" + WS
}
public static String buildJSONRegexpMatch(Map.Entry<String, Object> entry) {
return buildJSONRegexpMatchString(escapeJson(entry.key)) + ":" + buildJSONRegexpMatch(entry.value)
}
public static String buildJSONRegexpMatch(Object value) {
return buildJSONRegexpMatchStringOptionalQuotes(escapeJson(value.toString()))
}
public static String buildJSONRegexpMatch(Pattern pattern) {
return buildJSONRegexpMatchStringOptionalQuotes(pattern.pattern())
}
public static String buildJSONRegexpMatchString(String value) {
return WS + '"' + value + '"' + WS
}
public static String buildJSONRegexpMatchStringOptionalQuotes(String value) {
return WS + '"?' + value + '"?' + WS
}
}

View File

@@ -553,7 +553,7 @@ class WiremockGroovyDslSpec extends WiremockSpec {
"method": "GET",
"urlPattern": "/[0-9]{2}",
"bodyPatterns": [
{"matches": ".*personalId\\":.?\\"?^[0-9]{11}$\\"?.*"}
{"matches": "\\\\s*\\\\{\\\\s*\\\"personalId\\\"\\\\s*:\\\\s*\\\"?^[0-9]{11}$\\\"?\\\\s*\\\\}\\\\s*"}
]
},
"response": {
@@ -613,8 +613,7 @@ class WiremockGroovyDslSpec extends WiremockSpec {
},
"url": "/fraudcheck",
"bodyPatterns": [
{"matches": ".*clientPesel\\":.?\\"?[0-9]{10}\\"?.*"},
{"matches": ".*loanAmount\\":.?\\"?123.123\\"?.*"}
{"matches": "\\\\s*\\\\{\\\\s*\\"clientPesel\\"\\\\s*:\\\\s*\\"?[0-9]{10}\\"?\\\\s*,\\\\s*\\"loanAmount\\"\\\\s*:\\\\s*\\"?123.123\\"?\\\\s*\\\\}\\\\s*"}
]
},
"response": {
@@ -986,17 +985,11 @@ class WiremockGroovyDslSpec extends WiremockSpec {
"request": {
"method": "GET",
"urlPattern": "/[0-9]{2}",
"bodyPatterns": [
{"matches": ".*birthDate\\":.?\\"?[0-9]{4}-[0-9]{2}-[0-9]{2}\\"?.*"},
{"matches": ".*propertyName\\":.?\\"?[0-9]{2}\\"?.*"},
{"matches": ".*providerValue\\":.?\\"?Test\\"?.*"},
{"matches": ".*propertyName\\":.?\\"?[0-9]{2}\\"?.*"},
{"matches": ".*providerValue\\":.?\\"?Test\\"?.*"},
{"matches": ".*firstName\\":.?\\"?.*\\"?.*"},
{"matches": ".*lastName\\":.?\\"?.*\\"?.*"},
{"matches": ".*personalId\\":.?\\"?[0-9]{11}\\"?.*"}
]
},
"bodyPatterns": [
{
"matches": "\\\\s*\\\\{\\\\s*\\"birthDate\\"\\\\s*:\\\\s*\\"?[0-9]{4}-[0-9]{2}-[0-9]{2}\\"?\\\\s*,\\\\s*\\"errors\\"\\\\s*:\\\\s*\\\\[\\\\s*\\\\{\\\\s*\\"propertyName\\"\\\\s*:\\\\s*\\"?[0-9]{2}\\"?\\\\s*,\\\\s*\\"providerValue\\"\\\\s*:\\\\s*\\"?Test\\"?\\\\s*\\\\}\\\\s*,\\\\s*\\\\{\\\\s*\\"propertyName\\"\\\\s*:\\\\s*\\"?[0-9]{2}\\"?\\\\s*,\\\\s*\\"providerValue\\"\\\\s*:\\\\s*\\"?Test\\"?\\\\s*\\\\}\\\\s*\\\\]\\\\s*,\\\\s*\\"firstName\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"lastName\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"personalId\\"\\\\s*:\\\\s*\\"?[0-9]{11}\\"?\\\\s*\\\\}\\\\s*"
}
] },
"response": {
"status": 200,
"body": "{\\"name\\":\\"Jan\\"}",
@@ -1008,6 +1001,58 @@ class WiremockGroovyDslSpec extends WiremockSpec {
''')
}
def 'should use regexp matches when request body match is defined using a map with a pattern'() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/reissue-payment-order'
body(
loanNumber: "999997001",
amount: value(client(regex('[0-9.]+')), server('100.00')),
currency: "DKK",
applicationName: value(client(regex('.*')), server("Auto-Repayments")),
username: value(client(regex('.*')), server("scheduler")),
cardId: 1
)
}
response {
status 200
body '''
{
"status": "OK"
}
'''
headers {
header 'Content-Type': 'application/json'
}
}
}
when:
def json = toWiremockClientJsonStub(groovyDsl)
then:
parseJson(json) == parseJson('''
{
"request": {
"method": "POST",
"url": "/reissue-payment-order",
"bodyPatterns": [
{
"matches": "\\\\s*\\\\{\\\\s*\\"loanNumber\\"\\\\s*:\\\\s*\\"?999997001\\"?\\\\s*,\\\\s*\\"amount\\"\\\\s*:\\\\s*\\"?[0-9.]+\\"?\\\\s*,\\\\s*\\"currency\\"\\\\s*:\\\\s*\\"?DKK\\"?\\\\s*,\\\\s*\\"applicationName\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"username\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"cardId\\"\\\\s*:\\\\s*\\"?1\\"?\\\\s*\\\\}\\\\s*"
}
]
},
"response": {
"status": 200,
"body": "{\\"status\\":\\"OK\\"}",
"headers": {
"Content-Type": "application/json"
}
}
}
''')
}
String toJsonString(value) {
new JsonBuilder(value).toPrettyString()
}