Merge pull request #93 from dstepanov/jsonMatching
Generate JSON regexp match as a single regexp. Fixes #92
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user