Merge pull request #17 from 4finance/fromDslToWiremock

From Wiremock to DSL
This commit is contained in:
Marcin Grzejszczak
2015-02-16 12:41:03 +01:00
10 changed files with 557 additions and 45 deletions

View File

@@ -1,37 +1,138 @@
package io.codearte.accurest.wiremock
import groovy.io.FileType
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import io.coderate.accurest.dsl.GroovyDsl
import groovy.xml.XmlUtil
import static org.apache.commons.lang3.StringEscapeUtils.escapeJava
class WiremockToDslConverter {
static GroovyDsl fromWiremockStub(String wiremockStringStub) {
static String fromWiremockStub(String wiremockStringStub) {
return new WiremockToDslConverter().convertFromWiremockStub(wiremockStringStub)
}
private String convertFromWiremockStub(String wiremockStringStub) {
Object wiremockStub = new JsonSlurper().parseText(wiremockStringStub)
def wiremockRequest = wiremockStub.request
def wiremockResponse = wiremockStub.response
return GroovyDsl.make {
def request = wiremockStub.request
def response = wiremockStub.response
return """\
request {
wiremockRequest.method ? method(wiremockRequest.method as String) : null
wiremockRequest.url ? url(wiremockRequest.url as String) : null
wiremockRequest.urlPattern ? urlPattern(wiremockRequest.urlPattern as String) : null
wiremockRequest.urlPath ? urlPath(wiremockRequest.urlPath as String) : null
wiremockRequest.headers ? headers {
wiremockRequest.headers.each {
${request.method ? "method \"\"\"$request.method\"\"\"" : ""}
${request.url ? "url \"\"\"$request.url\"\"\"" : ""}
${request.urlPattern ? "urlPattern \"\"\"${escapeJava(request.urlPattern)}\"\"\"" : ""}
${request.urlPath ? "urlPath \"\"\"$request.urlPath\"\"\"" : ""}
${request.headers ? """headers {
${request.headers.collect {
def assertion = it.value
String headerName = it.key as String
header(headerName)."$assertion.key"(assertion.value)
}
} : null
def entry = assertion.entrySet().first()
"""header(\"\"\"$headerName\"\"\").$entry.key(\"\"\"${escapeJava(entry.value)}\"\"\")\n"""
}.join('')
}
}
""" : ""}
}
response {
status wiremockResponse.status ? wiremockResponse.status as Integer : null
wiremockResponse.body ? body (
wiremockResponse.body as Map
) : null
wiremockResponse.headers ? headers {
wiremockResponse.headers.each {
header([(it.key) : it.value])
${response.status ? "status $response.status" : ""}
${response.body ? "body( ${buildBody(response.body)})" : ""}
${response.headers ? """headers {
${response.headers.collect { "header('$it.key': '${it.value}')\n" }.join('')}
}
} : null
""" : ""}
}
"""
}
private Object buildBody(Map responseBody) {
return responseBody.entrySet().collectAll(withQuotedMapStringElements()).inject([:], appendToIterable())
}
private Object buildBody(List responseBody) {
return responseBody.collectAll(withQuotedStringElements()).inject([], appendToIterable())
}
private Object buildBody(Integer responseBody) {
return responseBody
}
private Object buildBody(String responseBody) {
try {
def json = new JsonSlurper().parseText(responseBody)
return wrapWithMultilineGString(JsonOutput.prettyPrint(responseBody))
} catch (Exception jsonException) {
try {
def xml = new XmlSlurper().parseText(responseBody)
return wrapWithMultilineGString(XmlUtil.serialize(responseBody))
} catch (Exception xmlException) {
return wrapWithMultilineGString(responseBody)
}
}
}
private String wrapWithMultilineGString(String string) {
return """\"\"\"$string\"\"\""""
}
private Closure withQuotedMapStringElements() {
return {
[(it.key): convert(it.value)]
}
}
private Closure withQuotedStringElements() {
return {
convert(it)
}
}
private Closure appendToIterable() {
return {
acc, el -> acc << el
}
}
private Object convert(Object element) {
return element
}
private Object convert(String element) {
return quoteString(element)
}
private String quoteString(String element) {
if (element =~ /^".*"$/) {
return element
}
return """\"\"\"${escapeJava(element)}\"\"\""""
}
private Object convert(List element) {
return element.collect {
convert(it)
}
}
private Object convert(Map element) {
return element.collectEntries {
[(it.key) : convert(it.value)]
}
}
static void main(String[] args) {
String rootOfFolderWithStubs = args[0]
new File(rootOfFolderWithStubs).eachFileRecurse(FileType.FILES) {
try {
if(!it.name.endsWith('json')) {
return
}
String wiremockStub = fromWiremockStub(it.text)
File newGroovyFile = new File(it.parent, it.name.replaceAll('json', 'groovy'))
println("Creating new groovy file [$newGroovyFile.path]")
newGroovyFile.text = wiremockStub
} catch (Exception e) {
System.err.println(e)
}
}
}
}

View File

@@ -7,22 +7,92 @@ class WiremockToDslConverterSpec extends Specification {
def 'should produce a Groovy DSL from Wiremock stub'() {
given:
String wiremockStub = '''
String wiremockStub = '''\
{
"request": {
"method": "GET",
"urlPattern": "/[0-9]{2}"
"urlPattern": "/[0-9]{2}",
"headers" : {
"Accept": {
"matches": "text/.*"
},
"etag": {
"doesNotMatch": "abcd.*"
},
"X-Custom-Header": {
"contains": "2134"
}
}
},
"response": {
"status": 200,
"body": {
"id": "123",
"id": {
"value": "132"
},
"surname": "Kowalsky",
"name": "Jan",
"created" : "2014-02-02 12:23:43"
},
"headers": {
"Content-Type": "text/plain"
"Content-Type": "text/plain",
}
}
}
'''
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'GET'
urlPattern '/[0-9]{2}'
headers {
header('Accept').matches('text/.*')
header('etag').doesNotMatch('abcd.*')
header('X-Custom-Header').contains('2134')
}
}
response {
status 200
body (
id : [value: '132'],
surname : 'Kowalsky',
name: 'Jan',
created : '2014-02-02 12:23:43'
)
headers {
header 'Content-Type': 'text/plain'
}
}
}
when:
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.coderate.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert Wiremock stub with body containing simple JSON'() {
given:
String wiremockStub = '''\
{
"request": {
"method": "DELETE",
"urlPattern": "/credit-card-verification-data/[0-9]+",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.mymoid-adapter.v2+json; charset=UTF-8"
}
}
},
"response": {
"status": 200,
"body": "{\\"status\\": \\"OK\\"}",
"headers": {
"Content-Type": "application/json"
}
}
}
@@ -30,25 +100,200 @@ class WiremockToDslConverterSpec extends Specification {
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'GET'
urlPattern '/[0-9]{2}'
method 'DELETE'
urlPattern '/credit-card-verification-data/[0-9]+'
headers {
header('Content-Type').equalTo('application/vnd.mymoid-adapter.v2+json; charset=UTF-8')
}
}
response {
status 200
body (
id : '123',
surname : 'Kowalsky',
name: 'Jan',
created : '2014-02-02 12:23:43'
)
body ("""{
"status": "OK"
}""")
headers {
header 'Content-Type': 'text/plain'
header 'Content-Type': 'application/json'
}
}
}
when:
GroovyDsl groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
groovyDsl == expectedGroovyDsl
new GroovyShell(this.class.classLoader).evaluate(
""" io.coderate.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert Wiremock stub with body containing integer'() {
given:
String wiremockStub = '''\
{
"request": {
"method": "POST",
"url": "/charge/count",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.creditcard-reporter.v1+json"
}
}
},
"response": {
"status": 200,
"body": 200,
"headers": {
"Content-Type": "application/json"
}
}
}
'''
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/charge/count'
headers {
header('Content-Type').equalTo('application/vnd.creditcard-reporter.v1+json')
}
}
response {
status 200
body ( 200 )
headers {
header 'Content-Type': 'application/json'
}
}
}
when:
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.coderate.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert Wiremock stub with body as a list'() {
given:
String wiremockStub = '''\
{
"request": {
"method": "POST",
"url": "/charge/count",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.creditcard-reporter.v1+json"
}
}
},
"response": {
"status": 200,
"body": [
{"a":1, "c":"3"},
"b",
"a"
],
"headers": {
"Content-Type": "application/json"
}
}
}
'''
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/charge/count'
headers {
header('Content-Type').equalTo('application/vnd.creditcard-reporter.v1+json')
}
}
response {
status 200
body ([
[a: 1, c: '3'],
'b',
'a'
])
headers {
header 'Content-Type': 'application/json'
}
}
}
when:
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.coderate.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert Wiremock stub with body containing a nested list'() {
given:
String wiremockStub = '''\
{
"request": {
"method": "POST",
"url": "/charge/search?pageNumber=0&size=2147483647",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.creditcard-reporter.v1+json"
}
}
},
"response": {
"status": 200,
"body":"[{\\"amount\\":1.01,\\"name\\":\\"Name\\",\\"info\\":{\\"title\\":\\"title1\\",\\"payload\\":null},\\"booleanvalue\\":true,\\"user\\":null},{\\"amount\\":2.01,\\"name\\":\\"Name2\\",\\"info\\":{\\"title\\":\\"title2\\",\\"payload\\":null},\\"booleanvalue\\":true,\\"user\\":null}]"
}
}
'''
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/charge/search?pageNumber=0&size=2147483647'
headers {
header('Content-Type').equalTo('application/vnd.creditcard-reporter.v1+json')
}
}
response {
status 200
body ("""[
{
"amount": 1.01,
"name": "Name",
"info": {
"title": "title1",
"payload": null
},
"booleanvalue": true,
"user": null
},
{
"amount": 2.01,
"name": "Name2",
"info": {
"title": "title2",
"payload": null
},
"booleanvalue": true,
"user": null
}
]""")
}
}
when:
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.coderate.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
}

View File

@@ -12,7 +12,7 @@ abstract class BaseWiremockStubStrategy {
}
return withAssertionHeaders(headers) {
Map.Entry<String, WithValuePattern> entry -> [(entry.key): buildClientHeaderFromValuePattern(entry.value)]
} << headers?.valueHeaders()
} << headers.valueHeaders()
}
protected Map buildServerHeadersSection(Headers headers) {

View File

@@ -8,7 +8,7 @@ import io.coderate.accurest.dsl.internal.Response
@TypeChecked
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
@ToString(includeFields = true, includePackage = false)
class GroovyDsl {
Request request
@@ -32,4 +32,5 @@ class GroovyDsl {
closure.delegate = response
closure()
}
}

View File

@@ -1,14 +1,17 @@
package io.coderate.accurest.dsl.internal
import groovy.transform.CompileStatic
import groovy.json.JsonSlurper
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import org.codehaus.groovy.runtime.GStringImpl
@CompileStatic
@ToString(includePackage = false, includeFields = true)
@EqualsAndHashCode(includeFields = true)
class Body {
private final Map<String, DslProperty> body
private Map<String, DslProperty> body
private DslProperty bodyAsValue
private List<DslProperty> bodyAsList
Body() {
this.body = [:]
@@ -18,13 +21,51 @@ class Body {
this.body = body
}
Map<String, Object> forClientSide() {
Body(List bodyAsList) {
this.bodyAsList = bodyAsList
}
Body(Object bodyAsValue) {
this.bodyAsValue = new DslProperty(bodyAsValue)
}
Body(GString bodyAsValue) {
this.bodyAsValue = new DslProperty(getClientValue(bodyAsValue), getServerValue(bodyAsValue))
}
private Map getClientValue(GString bodyAsValue) {
GString clientGString = new GStringImpl(bodyAsValue.values.clone(), bodyAsValue.strings.clone())
Object[] clientValues = bodyAsValue.values.collect { it instanceof DslProperty ? it.clientValue : it } as Object[]
return new JsonSlurper().parseText(new GStringImpl(clientValues, clientGString.strings).toString())
}
private Map getServerValue(GString bodyAsValue) {
GString clientGString = new GStringImpl(bodyAsValue.values.clone(), bodyAsValue.strings.clone())
Object[] serverValues = bodyAsValue.values.collect { it instanceof DslProperty ? it.serverValue: it } as Object[]
return new JsonSlurper().parseText(new GStringImpl(serverValues, clientGString.strings).toString())
}
Body(DslProperty bodyAsValue) {
this.bodyAsValue = bodyAsValue
}
Object forClientSide() {
if(bodyAsValue) {
return bodyAsValue.clientValue
} else if(bodyAsList) {
bodyAsList.collect { it.clientValue }
}
return body.collectEntries {
Map.Entry<String, DslProperty> entry -> [(entry.key) : entry.value.clientValue]
} as Map<String, Object>
}
Map<String, Object> forServerSide() {
Object forServerSide() {
if(bodyAsValue) {
return bodyAsValue.serverValue
} else if(bodyAsList) {
bodyAsList.collect { it.serverValue }
}
return body.collectEntries {
Map.Entry<String, DslProperty> entry -> [(entry.key) : entry.value.serverValue]
} as Map<String, Object>

View File

@@ -18,10 +18,28 @@ class Common {
} as Map<String, DslProperty>
}
List convertObjectsToDslProperties(List body) {
return body.collect {
Object element -> toDslProperty(element)
} as List
}
DslProperty toDslProperty(Object property) {
return new DslProperty(property)
}
DslProperty toDslProperty(Map property) {
return new DslProperty(property.collectEntries {
[(it.key) : toDslProperty(it.value)]
})
}
DslProperty toDslProperty(List property) {
return new DslProperty(property.collect {
toDslProperty(it)
})
}
DslProperty toDslProperty(DslProperty property) {
return property
}

View File

@@ -4,7 +4,7 @@ import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true)
class Headers {
private Map<String, WithValuePattern> assertionHeaders = [:]
@@ -28,4 +28,5 @@ class Headers {
Set<Map.Entry<String, WithValuePattern>> assertionEntries() {
return Collections.unmodifiableSet(assertionHeaders.entrySet())
}
}

View File

@@ -41,6 +41,14 @@ class Response extends Common {
this.body = new Body(convertObjectsToDslProperties(body))
}
void body(List body) {
this.body = new Body(convertObjectsToDslProperties(body))
}
void body(Object bodyAsValue) {
this.body = new Body(bodyAsValue)
}
Body getBody() {
return body
}

View File

@@ -60,6 +60,102 @@ class WiremockGroovyDslSpec extends Specification {
''')
}
def 'should convert groovy dsl stub with Body as String to wiremock stub for the client side'() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
method('GET')
urlPattern $(client('/[0-9]{2}'), server('/12'))
}
response {
status 200
body ("""\
{
"id": "${value(client('123'),server('321'))}",
"surname": "${value(client('Kowalsky'),server('Lewandowski'))}",
"name": "Jan",
"created" : "${$(client('2014-02-02 12:23:43'), server('2999-09-09 01:23:45'))}"
}
"""
)
headers {
header 'Content-Type': 'text/plain'
}
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request": {
"method": "GET",
"urlPattern": "/[0-9]{2}"
},
"response": {
"status": 200,
"body": {
"id": "123",
"surname": "Kowalsky",
"name": "Jan",
"created" : "2014-02-02 12:23:43"
},
"headers": {
"Content-Type": "text/plain"
}
}
}
''')
}
def 'should convert groovy dsl stub with Body as String to wiremock stub for the server side'() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
method('GET')
urlPattern $(client('/[0-9]{2}'), server('/12'))
}
response {
status 200
body ("""\
{
"id": "${value(client('123'),server('321'))}",
"surname": "${value(client('Kowalsky'),server('Lewandowski'))}",
"name": "Jan",
"created" : "${$(client('2014-02-02 12:23:43'), server('2999-09-09 01:23:45'))}"
}
"""
)
headers {
header 'Content-Type': 'text/plain'
}
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockServerStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request": {
"method": "GET",
"urlPattern": "/12"
},
"response": {
"status": 200,
"body": {
"id": "321",
"surname": "Lewandowski",
"name": "Jan",
"created" : "2999-09-09 01:23:45"
},
"headers": {
"Content-Type": "text/plain"
}
}
}
''')
}
def 'should convert groovy dsl stub to wiremock stub for the server side'() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {

View File

@@ -47,6 +47,7 @@ project(':accurest-core') {
project(':accurest-converters') {
dependencies {
compile project(':accurest-core')
compile 'org.apache.commons:commons-lang3:3.3.2'
}
}