diff --git a/.gitignore b/.gitignore index c1785e61a0..5fd4a2d374 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ *.iml .idea/ +.gradle/ target/ +build/ hs_err_pid* diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy new file mode 100644 index 0000000000..cdd09eee72 --- /dev/null +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy @@ -0,0 +1,37 @@ +package io.codearte.accurest.wiremock +import groovy.json.JsonSlurper +import io.coderate.accurest.dsl.GroovyDsl + +class WiremockToDslConverter { + static GroovyDsl fromWiremockStub(String wiremockStringStub) { + Object wiremockStub = new JsonSlurper().parseText(wiremockStringStub) + def wiremockRequest = wiremockStub.request + def wiremockResponse = wiremockStub.response + return GroovyDsl.make { + 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 { + def assertion = it.value + String headerName = it.key as String + header(headerName)."$assertion.key"(assertion.value) + } + } : null + } + 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]) + } + } : null + } + } + } +} diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy new file mode 100644 index 0000000000..6a4534a20b --- /dev/null +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy @@ -0,0 +1,54 @@ +package io.codearte.accurest.wiremock + +import io.coderate.accurest.dsl.GroovyDsl +import spock.lang.Specification + +class WiremockToDslConverterSpec extends Specification { + + def 'should produce a Groovy DSL from Wiremock stub'() { + given: + String wiremockStub = ''' +{ + "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" + } + } +} +''' + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + 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 { + header 'Content-Type': 'text/plain' + } + } + } + when: + GroovyDsl groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + then: + groovyDsl == expectedGroovyDsl + } +} diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/BaseWiremockStubStrategy.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/BaseWiremockStubStrategy.groovy new file mode 100644 index 0000000000..3925ada9f3 --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/BaseWiremockStubStrategy.groovy @@ -0,0 +1,55 @@ +package io.coderate.accurest.dsl +import groovy.transform.CompileStatic +import io.coderate.accurest.dsl.internal.DslProperty +import io.coderate.accurest.dsl.internal.Headers +import io.coderate.accurest.dsl.internal.WithValuePattern + +@CompileStatic +abstract class BaseWiremockStubStrategy { + protected Map buildClientHeadersSection(Headers headers) { + if (!headers) { + return null + } + return withAssertionHeaders(headers) { + Map.Entry entry -> [(entry.key): buildClientHeaderFromValuePattern(entry.value)] + } << headers?.valueHeaders() + } + + protected Map buildServerHeadersSection(Headers headers) { + if (!headers) { + return null + } + return withAssertionHeaders(headers) { + Map.Entry entry -> [(entry.key): buildServerHeaderFromValuePattern(entry.value)] + } << headers.valueHeaders() + } + + private Map withAssertionHeaders(Headers headers, Closure closure) { + return headers?.assertionEntries()?.collectEntries(closure) + } + + private Map buildClientHeaderFromValuePattern(WithValuePattern valuePattern) { + return getValuePatternSection(valuePattern) + .findAll { it.value } + .collectEntries { [(it.key): it.value.clientValue] } + } + + private Map buildServerHeaderFromValuePattern(WithValuePattern valuePattern) { + return getValuePatternSection(valuePattern) + .findAll { it.value } + .collectEntries { [(it.key): it.value.serverValue] } + } + + private Map getValuePatternSection(WithValuePattern valuePattern) { + return [equalToJson : valuePattern.equalToJson, + equalToXml : valuePattern.equalToXml, + matchesXPath : valuePattern.matchesXPath, + jsonCompareMode: valuePattern.jsonCompareMode, + equalTo : valuePattern.equalTo, + contains : valuePattern.contains, + matches : valuePattern.matches, + doesNotMatch : valuePattern.doesNotMatch, + absent : valuePattern.absent, + matchesJsonPath: valuePattern.matchesJsonPath] + } +} diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/GroovyDsl.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/GroovyDsl.groovy index 35a2acbc7d..9ea3348dbd 100644 --- a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/GroovyDsl.groovy +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/GroovyDsl.groovy @@ -1,11 +1,18 @@ package io.coderate.accurest.dsl +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString import groovy.transform.TypeChecked +import io.coderate.accurest.dsl.internal.Request +import io.coderate.accurest.dsl.internal.Response @TypeChecked +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false) class GroovyDsl { Request request + Response response static GroovyDsl make(Closure closure) { GroovyDsl dsl = new GroovyDsl() @@ -15,9 +22,14 @@ class GroovyDsl { } void request(@DelegatesTo(Request) Closure closure) { - Request request = new Request() - this.request = request + this.request = new Request() closure.delegate = request closure() } + + void response(@DelegatesTo(Response) Closure closure) { + this.response = new Response() + closure.delegate = response + closure() + } } diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/Request.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/Request.groovy deleted file mode 100644 index d4f40ef6ba..0000000000 --- a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/Request.groovy +++ /dev/null @@ -1,172 +0,0 @@ -package io.coderate.accurest.dsl - -import groovy.transform.TypeChecked - -@TypeChecked -class Request { - String method - String url - StringCustomizableProperty urlPattern - String urlPath - Headers headers - - void method(String method) { - this.method = method - } - - void url(String url) { - this.url = url - } - - void urlPattern(@DelegatesTo(StringCustomizableProperty) Closure closure) { - StringCustomizableProperty urlPattern = new StringCustomizableProperty() - this.urlPattern = urlPattern - delegateToClosure(closure, urlPattern) - } - - void headers(@DelegatesTo(Headers) Closure closure) { - Headers headers = new Headers() - this.headers = headers - delegateToClosure(closure, headers) - } - - void urlPath(String urlPath) { - this.urlPath = urlPath - } - - //TODO: Can we have different types for Client/Server (client: pattern(String), server: value(Boolean/Int)) ? - class CustomizableProperty { - private T client - private V server - - void client(T client) { - this.client = client - } - - void server(V server) { - this.server = server - } - - T toClientSide() { - return client - } - - V toServerSide() { - return server - } - } - - private class StringCustomizableProperty extends CustomizableProperty { - @Override - String toClientSide() { - return super.toClientSide() as String - } - - @Override - String toServerSide() { - return super.toServerSide() as String - } - } - - private class NoOpCustomizableProperty extends CustomizableProperty { - NoOpCustomizableProperty(T value) { - client(value) - server(value) - } - - @Override - public String toString() { - return toClientSide() - } - } - - class Headers { - private Map headers = [:] - - WithValuePattern header(String headerName) { - WithValuePattern withValuePattern = new WithValuePattern() - headers[headerName] = withValuePattern - return withValuePattern - } - - Set> entries() { - return headers.entrySet() - } - } - -/** - * - * From Wiremock - private Map queryParamPatterns; - private List bodyPatterns; - - private String equalToJson; - private String equalToXml; - private String matchesXPath; - private JSONCompareMode jsonCompareMode; - private String equalTo; - private String contains; - private String matches; - private String doesNotMatch; - private Boolean absent; - private String matchesJsonPath; - - */ - class WithValuePattern { - NoOpCustomizableProperty equalToJson - NoOpCustomizableProperty equalToXml - StringCustomizableProperty matchesXPath - NoOpCustomizableProperty jsonCompareMode - NoOpCustomizableProperty equalTo - StringCustomizableProperty contains - StringCustomizableProperty matches - StringCustomizableProperty doesNotMatch - CustomizableProperty absent - StringCustomizableProperty matchesJsonPath - -// void equalToJson(String equalToJson) { -// this.equalToJson = equalToJson -// } -// - void equalTo(String equalTo) { - this.equalTo = new NoOpCustomizableProperty(equalTo) - } - -// void equalToXml(String equalToXml) { -// this.equalToXml = equalToXml -// } - - void matches(@DelegatesTo(StringCustomizableProperty) Closure closure) { - StringCustomizableProperty placeholderHaving = new StringCustomizableProperty() - this.matches = placeholderHaving - delegateToClosure(closure, placeholderHaving) - } - - void doesNotMatch(@DelegatesTo(StringCustomizableProperty) Closure closure) { - StringCustomizableProperty placeholderHaving = new StringCustomizableProperty() - this.doesNotMatch = placeholderHaving - delegateToClosure(closure, placeholderHaving) - } - - void contains(@DelegatesTo(StringCustomizableProperty) Closure closure) { - StringCustomizableProperty placeholderHaving = new StringCustomizableProperty() - this.contains = placeholderHaving - delegateToClosure(closure, placeholderHaving) - } - -// void jsonCompareMode(JSONCompareMode jsonCompareMode) { -// this.jsonCompareMode = jsonCompareMode -// } - - } - - enum JSONCompareMode { - STRICT, LENIENT, NON_EXTENSIBLE, STRICT_ORDER; - } - - private static void delegateToClosure(@DelegatesTo(T) Closure closure, T delegate) { - closure.delegate = delegate - closure() - } - -} \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/WiremockRequestStubStrategy.groovy new file mode 100644 index 0000000000..369b082cf8 --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -0,0 +1,42 @@ +package io.coderate.accurest.dsl + +import groovy.transform.CompileStatic +import groovy.transform.PackageScope +import io.coderate.accurest.dsl.internal.ClientRequest +import io.coderate.accurest.dsl.internal.Request +import io.coderate.accurest.dsl.internal.ServerRequest + +@CompileStatic +@PackageScope +class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { + + private final Request request + + WiremockRequestStubStrategy(GroovyDsl groovyDsl) { + this.request = groovyDsl.request + } + + @PackageScope Map buildClientRequestContent() { + return buildRequestContent(new ClientRequest(request)) + } + + @PackageScope Map buildServerRequestContent() { + return buildRequestContent(new ServerRequest(request)) + } + + private Map buildRequestContent(ClientRequest request) { + return [method : request?.method?.clientValue, + url : request?.url?.clientValue, + urlPattern: request?.urlPattern?.clientValue, + urlPath : request?.urlPath?.clientValue, + headers : buildClientHeadersSection(request.headers)].findAll { it.value } + } + + private Map buildRequestContent(ServerRequest request) { + return [method : request?.method?.serverValue, + url : request?.url?.serverValue, + urlPattern: request?.urlPattern?.serverValue, + urlPath : request?.urlPath?.serverValue, + headers : buildServerHeadersSection(request.headers)].findAll { it.value } + } +} diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/WiremockResponseStubStrategy.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/WiremockResponseStubStrategy.groovy new file mode 100644 index 0000000000..ab62663b51 --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/WiremockResponseStubStrategy.groovy @@ -0,0 +1,39 @@ +package io.coderate.accurest.dsl + +import groovy.transform.CompileStatic +import groovy.transform.PackageScope +import io.coderate.accurest.dsl.internal.ClientResponse +import io.coderate.accurest.dsl.internal.Response +import io.coderate.accurest.dsl.internal.ServerResponse + +@CompileStatic +@PackageScope +class WiremockResponseStubStrategy extends BaseWiremockStubStrategy { + + private final Response response + + WiremockResponseStubStrategy(GroovyDsl groovyDsl) { + this.response = groovyDsl.response + } + + @PackageScope Map buildClientResponseContent() { + return buildResponseContent(new ClientResponse(response)) + } + + @PackageScope Map buildServerResponseContent() { + return buildResponseContent(new ServerResponse(response)) + } + + private Map buildResponseContent(ClientResponse response) { + return [status : response?.status?.clientValue, + body : response?.body?.forClientSide(), + headers: buildClientHeadersSection(response.headers)].findAll { it.value } + } + + private Map buildResponseContent(ServerResponse response) { + return [status : response?.status?.serverValue, + body : response?.body?.forServerSide(), + headers: buildServerHeadersSection(response.headers)].findAll { it.value } + } + +} diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/WiremockStubStrategy.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/WiremockStubStrategy.groovy index ea975a0ca8..c4ced294d6 100644 --- a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/WiremockStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/WiremockStubStrategy.groovy @@ -6,81 +6,21 @@ import groovy.transform.CompileStatic @CompileStatic class WiremockStubStrategy { - private final Request request + private final WiremockRequestStubStrategy wiremockRequestStubStrategy + private final WiremockResponseStubStrategy wiremockResponseStubStrategy WiremockStubStrategy(GroovyDsl groovyDsl) { - this.request = groovyDsl.request + this.wiremockRequestStubStrategy = new WiremockRequestStubStrategy(groovyDsl) + this.wiremockResponseStubStrategy = new WiremockResponseStubStrategy(groovyDsl) } String toWiremockClientStub() { - return JsonOutput.toJson(buildClientRequest(request)) - + return JsonOutput.toJson([request: wiremockRequestStubStrategy.buildClientRequestContent(), + response: wiremockResponseStubStrategy.buildClientResponseContent()]) } String toWiremockServerStub() { - return JsonOutput.toJson(buildServerRequest(request)) + return JsonOutput.toJson([request: wiremockRequestStubStrategy.buildServerRequestContent(), + response: wiremockResponseStubStrategy.buildServerResponseContent()]) } - - private Map buildClientRequest(Request request) { - return getRequestSection(request, - { request.urlPattern?.toClientSide() }, - { buildClientHeadersSection(request.headers) }) - } - - private Map buildServerRequest(Request request) { - return getRequestSection(request, - { request.urlPattern?.toServerSide() }, - { buildServerHeadersSection(request.headers) }) - } - - private Map> getRequestSection(Request request, Closure buildUrlPattern, Closure buildHeaders) { - return [request: [method : request.method, - url : request.url, - urlPattern: buildUrlPattern(), - urlPath : request.urlPath, - headers : buildHeaders()].findAll { it.value }] - } - - private Map buildClientHeadersSection(Request.Headers headers) { - return createHeadersSection(headers) { - Map.Entry entry -> [(entry.key): buildClientHeaderFromValuePattern(entry.value)] - } - } - - private Map buildServerHeadersSection(Request.Headers headers) { - return createHeadersSection(headers) { - Map.Entry entry -> [(entry.key): buildServerHeaderFromValuePattern(entry.value)] - } - } - - private Map createHeadersSection(Request.Headers headers, Closure closure) { - return headers?.entries()?.collectEntries(closure) - } - - - private Map buildClientHeaderFromValuePattern(Request.WithValuePattern valuePattern) { - return getValuePatternSection(valuePattern) - .findAll { it.value } - .collectEntries { [(it.key): it.value.toClientSide()] } - } - - private Map buildServerHeaderFromValuePattern(Request.WithValuePattern valuePattern) { - return getValuePatternSection(valuePattern) - .findAll { it.value } - .collectEntries { [(it.key): it.value.toServerSide()] } - } - - private Map getValuePatternSection(Request.WithValuePattern valuePattern) { - return [equalToJson : valuePattern.equalToJson, - equalToXml : valuePattern.equalToXml, - matchesXPath : valuePattern.matchesXPath, - jsonCompareMode: valuePattern.jsonCompareMode, - equalTo : valuePattern.equalTo, - contains : valuePattern.contains, - matches : valuePattern.matches, - doesNotMatch : valuePattern.doesNotMatch, - absent : valuePattern.absent, - matchesJsonPath: valuePattern.matchesJsonPath] - } - } diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Body.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Body.groovy new file mode 100644 index 0000000000..e3b75c9eb4 --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Body.groovy @@ -0,0 +1,32 @@ +package io.coderate.accurest.dsl.internal +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +@CompileStatic +@ToString(includePackage = false, includeFields = true) +@EqualsAndHashCode(includeFields = true) +class Body { + + private final Map body + + Body() { + this.body = [:] + } + + Body(Map body) { + this.body = body + } + + Map forClientSide() { + return body.collectEntries { + Map.Entry entry -> [(entry.key) : entry.value.clientValue] + } as Map + } + + Map forServerSide() { + return body.collectEntries { + Map.Entry entry -> [(entry.key) : entry.value.serverValue] + } as Map + } +} diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/ClientDslProperty.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/ClientDslProperty.groovy new file mode 100644 index 0000000000..5551688e02 --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/ClientDslProperty.groovy @@ -0,0 +1,10 @@ +package io.coderate.accurest.dsl.internal +import groovy.transform.CompileStatic + +@CompileStatic +class ClientDslProperty extends DslProperty { + + ClientDslProperty(Object singleValue) { + super(singleValue) + } +} diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Common.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Common.groovy new file mode 100644 index 0000000000..fc5135af33 --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Common.groovy @@ -0,0 +1,52 @@ +package io.coderate.accurest.dsl.internal + +import groovy.transform.PackageScope +import groovy.transform.TypeChecked + +/** + * @TypeChecked instead of @CompileStatic due to usage of double dispatch. + * Double dispatch doesn't work if you're using @CompileStatic + */ +@TypeChecked +@PackageScope +class Common { + + Map convertObjectsToDslProperties(Map body) { + return body.collectEntries { + Map.Entry entry -> + [(entry.key): toDslProperty(entry.value)] + } as Map + } + + DslProperty toDslProperty(Object property) { + return new DslProperty(property) + } + + DslProperty toDslProperty(DslProperty property) { + return property + } + + DslProperty value(ClientDslProperty client, ServerDslProperty server) { + return new DslProperty(client.clientValue, server.serverValue) + } + + DslProperty value(ServerDslProperty server, ClientDslProperty client) { + return new DslProperty(client.clientValue, server.serverValue) + } + + DslProperty $(ClientDslProperty client, ServerDslProperty server) { + return value(client, server) + } + + DslProperty $(ServerDslProperty server, ClientDslProperty client) { + return value(server, client) + } + + ClientDslProperty client(Object clientValue) { + return new ClientDslProperty(clientValue) + } + + ServerDslProperty server(Object serverValue) { + return new ServerDslProperty(serverValue) + } +} diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/DslProperty.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/DslProperty.groovy new file mode 100644 index 0000000000..6710b90958 --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/DslProperty.groovy @@ -0,0 +1,24 @@ +package io.coderate.accurest.dsl.internal + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +@CompileStatic +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false) +class DslProperty { + + final T clientValue + final T serverValue + + DslProperty(T clientValue, T serverValue) { + this.clientValue = clientValue + this.serverValue = serverValue + } + + DslProperty(T singleValue) { + this.clientValue = singleValue + this.serverValue = singleValue + } +} diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Headers.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Headers.groovy new file mode 100644 index 0000000000..5487b10b53 --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Headers.groovy @@ -0,0 +1,31 @@ +package io.coderate.accurest.dsl.internal + +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false) +class Headers { + + private Map assertionHeaders = [:] + private Map valueHeaders = [:] + + WithValuePattern header(String headerName) { + WithValuePattern withValuePattern = new WithValuePattern() + assertionHeaders[headerName] = withValuePattern + return withValuePattern + } + + void header(Map singleHeader) { + Map.Entry first = singleHeader.entrySet().first() + valueHeaders[first?.key] = first?.value + } + + Map valueHeaders() { + return Collections.unmodifiableMap(valueHeaders) + } + + Set> assertionEntries() { + return Collections.unmodifiableSet(assertionHeaders.entrySet()) + } +} diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/JSONCompareMode.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/JSONCompareMode.groovy new file mode 100644 index 0000000000..7f07abb723 --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/JSONCompareMode.groovy @@ -0,0 +1,5 @@ +package io.coderate.accurest.dsl.internal + +enum JSONCompareMode { + STRICT, LENIENT, NON_EXTENSIBLE, STRICT_ORDER +} \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Request.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Request.groovy new file mode 100644 index 0000000000..1b12d8b296 --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Request.groovy @@ -0,0 +1,84 @@ +package io.coderate.accurest.dsl.internal +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import groovy.transform.TypeChecked + +@TypeChecked +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false) +class Request extends Common { + + DslProperty method + DslProperty url + DslProperty urlPattern + DslProperty urlPath + Headers headers + + Request() { + } + + Request(Request request) { + this.method = request.method + this.url = request.url + this.urlPattern = request.urlPattern + this.urlPath = request.urlPath + this.headers = request.headers + } + + void method(String method) { + this.method = toDslProperty(method) + } + + void method(DslProperty method) { + this.method = toDslProperty(method) + } + + void url(String url) { + this.url = toDslProperty(url) + } + + void url(DslProperty url) { + this.url = toDslProperty(url) + } + + void urlPattern(String urlPattern) { + this.urlPattern = toDslProperty(urlPattern) + } + + void urlPattern(DslProperty urlPattern) { + this.urlPattern = toDslProperty(urlPattern) + } + + void headers(@DelegatesTo(Headers) Closure closure) { + this.headers = new Headers() + closure.delegate = headers + closure() + } + + void urlPath(String urlPath) { + this.urlPath = toDslProperty(urlPath) + } + + void urlPath(DslProperty urlPath) { + this.urlPath = toDslProperty(urlPath) + } +} + +@CompileStatic +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false) +class ServerRequest extends Request { + ServerRequest(Request request) { + super(request) + } +} + +@CompileStatic +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false) +class ClientRequest extends Request { + ClientRequest(Request request) { + super(request) + } +} diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Response.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Response.groovy new file mode 100644 index 0000000000..660863c1b0 --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/Response.groovy @@ -0,0 +1,73 @@ +package io.coderate.accurest.dsl.internal + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import groovy.transform.TypeChecked + +@TypeChecked +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false, includeFields = true) +class Response extends Common { + + private DslProperty status + private Headers headers + private Body body = new Body() + + Response() { + } + + Response(Response response) { + this.status = response.status + this.headers = response.headers + this.body = response.body + } + + void status(int status) { + this.status = toDslProperty(status) + } + + void status(DslProperty status) { + this.status = toDslProperty(status) + } + + void headers(@DelegatesTo(Headers) Closure closure) { + this.headers = new Headers() + closure.delegate = headers + closure() + } + + void body(Map body) { + this.body = new Body(convertObjectsToDslProperties(body)) + } + + Body getBody() { + return body + } + + DslProperty getStatus() { + return status + } + + Headers getHeaders() { + return headers + } +} + +@CompileStatic +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false) +class ServerResponse extends Response { + ServerResponse(Response request) { + super(request) + } +} + +@CompileStatic +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false) +class ClientResponse extends Response { + ClientResponse(Response request) { + super(request) + } +} \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/ServerDslProperty.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/ServerDslProperty.groovy new file mode 100644 index 0000000000..dad9dec06d --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/ServerDslProperty.groovy @@ -0,0 +1,15 @@ +package io.coderate.accurest.dsl.internal + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +@CompileStatic +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false) +class ServerDslProperty extends DslProperty { + + ServerDslProperty(Object singleValue) { + super(singleValue) + } +} diff --git a/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/WithValuePattern.groovy b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/WithValuePattern.groovy new file mode 100644 index 0000000000..bad8bda88d --- /dev/null +++ b/accurest-core/src/main/groovy/io/coderate/accurest/dsl/internal/WithValuePattern.groovy @@ -0,0 +1,102 @@ +package io.coderate.accurest.dsl.internal + +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import groovy.transform.TypeChecked + +@TypeChecked +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false) +class WithValuePattern { + + DslProperty equalTo + DslProperty equalToJson + DslProperty equalToXml + DslProperty matchesXPath + DslProperty jsonCompareMode + DslProperty contains + DslProperty matches + DslProperty doesNotMatch + DslProperty absent + DslProperty matchesJsonPath + + void equalTo(String equalTo) { + this.equalTo = new DslProperty(equalTo) + } + + void equalTo(DslProperty equalTo) { + this.equalTo = equalTo + } + + void equalToJson(String equalToJson) { + this.equalToJson = new DslProperty(equalToJson) + } + + void equalToJson(DslProperty equalToJson) { + this.equalToJson = equalToJson + } + + void equalToXml(String equalToXml) { + this.equalToXml = new DslProperty(equalToXml) + } + + void equalToXml(DslProperty equalToXml) { + this.equalToXml = equalToXml + } + + void matchesXPath(String matchesXPath) { + this.matchesXPath = new DslProperty(matchesXPath) + } + + void matchesXPath(DslProperty matchesXPath) { + this.matchesXPath = matchesXPath + } + + void jsonCompareMode(JSONCompareMode jsonCompareMode) { + this.jsonCompareMode = new DslProperty(jsonCompareMode) + } + + void jsonCompareMode(DslProperty jsonCompareMode) { + this.jsonCompareMode = jsonCompareMode + } + + void contains(String contains) { + this.contains = new DslProperty(contains) + } + + void contains(DslProperty contains) { + this.contains = contains + } + + void matches(String matches) { + this.matches = new DslProperty(matches) + } + + void matches(DslProperty matches) { + this.matches = matches + } + + void doesNotMatch(String doesNotMatch) { + this.doesNotMatch = new DslProperty(doesNotMatch) + } + + void doesNotMatch(DslProperty doesNotMatch) { + this.doesNotMatch = doesNotMatch + } + + void absent(String absent) { + this.absent = new DslProperty(absent) + } + + void absent(DslProperty absent) { + this.absent = absent + } + + void matchesJsonPath(String matchesJsonPath) { + this.matchesJsonPath = new DslProperty(matchesJsonPath) + } + + void matchesJsonPath(DslProperty matchesJsonPath) { + this.matchesJsonPath = matchesJsonPath + } +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslResponseSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslResponseSpec.groovy new file mode 100644 index 0000000000..f8aa6a6890 --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslResponseSpec.groovy @@ -0,0 +1,100 @@ +package io.codearte.accurest.dsl + +import groovy.json.JsonSlurper +import io.coderate.accurest.dsl.GroovyDsl +import io.coderate.accurest.dsl.WiremockResponseStubStrategy +import spock.lang.Specification + +class WiremockGroovyDslResponseSpec extends Specification { + + def 'should generate response without body for #side side'() { + given: + GroovyDsl dsl = GroovyDsl.make { + response { + status 200 + } + } + expect: + new WiremockResponseStubStrategy(dsl)."build${side}ResponseContent"() == new JsonSlurper().parseText(expectedStub) + where: + side << ['Client', 'Server'] + expectedStub << [''' + { + "status": 200 + } + ''', + + ''' + { + "status": 200 + } + '''] + } + + def 'should generate headers for response for client side'() { + given: + GroovyDsl dsl = GroovyDsl.make { + response { + headers { + header('Content-Type').matches $(client('text/xml'), server('text/*')) + } + status 200 + } + } + expect: + new WiremockResponseStubStrategy(dsl).buildClientResponseContent() == new JsonSlurper().parseText(''' + { + "headers": { + "Content-Type": { + "matches": "text/xml" + }, + }, + "status": 200 + } + ''') + } + + def 'should generate headers for response for server side'() { + given: + GroovyDsl dsl = GroovyDsl.make { + response { + status 200 + headers { + header('Content-Type').matches $(client('text/xml'), server('text/*')) + } + } + } + expect: + new WiremockResponseStubStrategy(dsl).buildServerResponseContent() == new JsonSlurper().parseText(''' + { + "status": 200, + "headers": { + "Content-Type": { + "matches": "text/*" + } + } + } + ''') + } + + def 'should generate an exact header for response for both sides '() { + given: + GroovyDsl dsl = GroovyDsl.make { + response { + status 200 + headers { + header 'Content-Type': 'text/xml' + } + } + } + expect: + new WiremockResponseStubStrategy(dsl).buildServerResponseContent() == new JsonSlurper().parseText(''' + { + "status": 200, + "headers": { + "Content-Type": "text/xml" + } + } + ''') + } +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index f6feb8710e..3ee9cd7f11 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -2,41 +2,43 @@ package io.codearte.accurest.dsl import groovy.json.JsonSlurper import io.coderate.accurest.dsl.GroovyDsl +import io.coderate.accurest.dsl.WiremockRequestStubStrategy import io.coderate.accurest.dsl.WiremockStubStrategy import spock.lang.Ignore import spock.lang.Specification class WiremockGroovyDslSpec extends Specification { - // TODO: add alias instead of placeholder - @Ignore - def 'should convert groovy dsl stub to wiremock stub'() { + def 'should convert groovy dsl stub to wiremock stub for the client side'() { given: - GroovyDsl dsl = GroovyDsl.make { - request { - method('GET') - urlPattern { - client('/[0-9]{2}') - server('/12') + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method('GET') + urlPattern $(client('/[0-9]{2}'), server('/12')) + } + response { + status 200 + body ( + id : value( + client('123'), + server({ regex('[0-9]+') }) + ), + surname : $( + client('Kowalsky'), + server('Lewandowski') + ), + name: 'Jan', + created : $(client('2014-02-02 12:23:43'), server({ currentDate(it) })) + ) + headers { + header 'Content-Type': 'text/plain' + } } } - response { - status(200) - body { - withPlaceholder(client: '2015-01-14', server: '$anyInt($it)') - withTemplate(''' - { - "date" : "$placeholder0" - } - ''') - } - headers { - Content-Type('text/plain') - } - } - } - expect: - dsl.toWiremockClientStub() == ''' + when: + String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + then: + new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' { "request": { "method": "GET", @@ -44,13 +46,69 @@ class WiremockGroovyDslSpec extends Specification { }, "response": { "status": 200, - "body": "2015-01-14", + "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 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 : $( + client('Kowalsky'), + server('Lewandowski') + ), + name: 'Jan', + created : $(client('2014-02-02 12:23:43'), server('1999-01-01 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" : "1999-01-01 01:23:45" + }, + "headers": { + "Content-Type": "text/plain" + } + } +} +''') } def "should generate stub with GET"() { @@ -60,14 +118,10 @@ class WiremockGroovyDslSpec extends Specification { method("GET") } } - when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() - then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + expect: + new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' { - "request":{ - "method":"GET" - } + "method":"GET" } ''') } @@ -80,15 +134,11 @@ class WiremockGroovyDslSpec extends Specification { url("/sth") } } - when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() - then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + expect: + new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' { - "request":{ - "method":"GET", - "url":"/sth" - } + "method":"GET", + "url":"/sth" } ''') } @@ -97,19 +147,16 @@ class WiremockGroovyDslSpec extends Specification { given: GroovyDsl groovyDsl = GroovyDsl.make { request { - urlPattern { - client('/^[0-9]{2}$') - } + urlPattern $( + client('/^[0-9]{2}$'), + server('/12') + ) } } - when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() - then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + expect: + new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' { - "request":{ - "urlPattern":"/^[0-9]{2}$" - } + "urlPattern":"/^[0-9]{2}$" } ''') } @@ -118,20 +165,16 @@ class WiremockGroovyDslSpec extends Specification { given: GroovyDsl groovyDsl = GroovyDsl.make { request { - urlPattern { - client('/^[0-9]{2}$') - server('/12') - } + urlPattern $( + client('/[0-9]{2}'), + server('/12') + ) } } - when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockServerStub() - then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + expect: + new WiremockRequestStubStrategy(groovyDsl).buildServerRequestContent() == new JsonSlurper().parseText(''' { - "request":{ - "urlPattern":"/12" - } + "urlPattern":"/12" } ''') } @@ -143,14 +186,10 @@ class WiremockGroovyDslSpec extends Specification { urlPath ('/12') } } - when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() - then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + expect: + new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' { - "request":{ - "urlPath":"/12" - } + "urlPath":"/12" } ''') } @@ -162,14 +201,10 @@ class WiremockGroovyDslSpec extends Specification { urlPath ('/12') } } - when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockServerStub() - then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + expect: + new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' { - "request":{ - "urlPath":"/12" - } + "urlPath":"/12" } ''') } @@ -180,40 +215,36 @@ class WiremockGroovyDslSpec extends Specification { request { headers { header('Content-Type').equalTo('text/xml') - header('Accept').matches { - client('text/.*') + header('Accept').matches $( + client('text/.*'), server('text/plain') - } - header('etag').doesNotMatch { - client('abcd.*') + ) + header('etag').doesNotMatch $( + client('abcd.*'), server('abcdef') - } - header('X-Custom-Header').contains { - client('2134') + ) + header('X-Custom-Header').contains $( + client('2134'), server('121345') - } + ) } } } - when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() - then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + expect: + new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' { - "request":{ - "headers": { - "Content-Type": { - "equalTo": "text/xml" - }, - "Accept": { - "matches": "text/.*" - }, - "etag": { - "doesNotMatch": "abcd.*" - }, - "X-Custom-Header": { - "contains": "2134" - } + "headers": { + "Content-Type": { + "equalTo": "text/xml" + }, + "Accept": { + "matches": "text/.*" + }, + "etag": { + "doesNotMatch": "abcd.*" + }, + "X-Custom-Header": { + "contains": "2134" } } } @@ -226,45 +257,45 @@ class WiremockGroovyDslSpec extends Specification { request { headers { header('Content-Type').equalTo('text/xml') - header('Accept').matches { - client('text/.*') + header('Accept').matches $( + client('text/.*'), server('text/plain') - } - header('etag').doesNotMatch { - client('abcd.*') + ) + header('etag').doesNotMatch $( + client('abcd.*'), server('abcdef') - } - header('X-Custom-Header').contains { - client('2134') + ) + header('X-Custom-Header').contains $( + client('2134'), server('121345') - } + ) } } } - when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockServerStub() - then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + expect: + new WiremockRequestStubStrategy(groovyDsl).buildServerRequestContent() == new JsonSlurper().parseText(''' { - "request":{ - "headers": { - "Content-Type": { - "equalTo": "text/xml" - }, - "Accept": { - "matches": "text/plain" - }, - "etag": { - "doesNotMatch": "abcdef" - }, - "X-Custom-Header": { - "contains": "121345" - } + "headers": { + "Content-Type": { + "equalTo": "text/xml" + }, + "Accept": { + "matches": "text/plain" + }, + "etag": { + "doesNotMatch": "abcdef" + }, + "X-Custom-Header": { + "contains": "121345" } } } ''') } + @Ignore("Not implemented yet") + def "should generate stub with request body matching for server side"() {} + @Ignore("Not implemented yet") + def "should generate stub with request query parameter matching for server side"() {} } diff --git a/build.gradle b/build.gradle index 33ecf9a2db..a74ec2a59f 100644 --- a/build.gradle +++ b/build.gradle @@ -44,6 +44,12 @@ subprojects { project(':accurest-core') { } +project(':accurest-converters') { + dependencies { + compile project(':accurest-core') + } +} + project(':accurest-gradle-plugin') { dependencies { compile project(':accurest-core') diff --git a/settings.gradle b/settings.gradle index dff32d91dd..148580177b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,3 @@ -include "accurest-core", "accurest-gradle-plugin" -rootProject.name = "accurest" \ No newline at end of file +include "accurest-core", "accurest-gradle-plugin", 'accurest-converters' +rootProject.name = "accurest" +