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 d6854255d9..aa0b0f6bbc 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 @@ -2,6 +2,9 @@ package io.codearte.accurest.dsl import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.internal.ClientRequest +import io.codearte.accurest.dsl.internal.MatchingStrategy +import io.codearte.accurest.dsl.internal.QueryParameter +import io.codearte.accurest.dsl.internal.QueryParameters import io.codearte.accurest.dsl.internal.Request import java.util.regex.Pattern @@ -27,12 +30,47 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { private Map buildRequestContent(ClientRequest request) { return ([method : request?.method?.clientValue, headers : buildClientRequestHeadersSection(request.headers) - ] << appendUrl(request) << appendBody(request)).findAll { it.value } + ] << appendUrl(request) << appendQueryParameters(request) << appendBody(request)).findAll { it.value } } private Map appendUrl(ClientRequest clientRequest) { - Object url = clientRequest?.url?.clientValue - return url instanceof Pattern ? [urlPattern: ((Pattern)url).pattern()] : [url: url] + def urlPath = clientRequest?.urlPath?.clientValue + if (urlPath) { + return [urlPath: urlPath] + } + def url = clientRequest?.url?.clientValue + return url instanceof Pattern ? [urlPattern: url.pattern()] : [url: url] + } + + private Map appendQueryParameters(ClientRequest clientRequest) { + def queryParameters = clientRequest?.urlPath?.queryParameters ?: clientRequest?.url?.queryParameters + return queryParameters && !queryParameters.parameters.isEmpty() ? + [queryParameters: buildUrlPathQueryParameters(queryParameters)] : [:] + } + + private Map buildUrlPathQueryParameters(QueryParameters queryParameters) { + return queryParameters.parameters.collectEntries { QueryParameter param -> + parseQueryParameter(param.name, param.clientValue) + } + } + + protected Map parseQueryParameter(String name, MatchingStrategy matchingStrategy) { + return buildQueryParameter(name, matchingStrategy.clientValue, matchingStrategy.type) + } + + protected Map parseQueryParameter(String name, Object value) { + return buildQueryParameter(name, value, MatchingStrategy.Type.EQUAL_TO) + } + + protected Map parseQueryParameter(String name, Pattern pattern) { + return buildQueryParameter(name, pattern.pattern(), MatchingStrategy.Type.MATCHING) + } + + private Map buildQueryParameter(String name, Object value, MatchingStrategy.Type type) { + if (value instanceof Pattern) { + value = value.pattern() + } + return [(name): [(type.name) : value]] } private Map appendBody(ClientRequest clientRequest) { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy new file mode 100644 index 0000000000..2be3567482 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy @@ -0,0 +1,34 @@ +package io.codearte.accurest.dsl.internal + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString; + +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true) +@CompileStatic +public class MatchingStrategy extends DslProperty { + + Type type + + MatchingStrategy(Object value, Type type) { + super(value) + this.type = type + } + + MatchingStrategy(DslProperty value, Type type) { + super(value.clientValue, value.serverValue) + this.type = type + } + + enum Type { + EQUAL_TO("equalTo"), CONTAINS("contains"), MATCHING("matches"), NOT_MATCHING("doesNotMatch") + + final String name + + Type(name) { + this.name = name + } + } + +} \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy new file mode 100644 index 0000000000..2e5a606c29 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy @@ -0,0 +1,29 @@ +package io.codearte.accurest.dsl.internal; + +import groovy.transform.CompileStatic; +import groovy.transform.EqualsAndHashCode; +import groovy.transform.ToString; + +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true) +@CompileStatic +public class QueryParameter extends DslProperty { + + String name + + QueryParameter(String name, DslProperty dslProperty) { + super(dslProperty.clientValue, dslProperty.serverValue) + this.name = name + } + + QueryParameter(String name, MatchingStrategy matchingStrategy) { + super(matchingStrategy) + this.name = name + } + + QueryParameter(String name, Object value) { + super(value) + this.name = name + } + +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy new file mode 100644 index 0000000000..c233681652 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy @@ -0,0 +1,45 @@ +package io.codearte.accurest.dsl.internal + +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import groovy.transform.TypeChecked + +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true) +@TypeChecked +public class QueryParameters { + + List parameters = [] + + public void parameter(Map singleParameter) { + Map.Entry first = singleParameter.entrySet().first() + parameters << new QueryParameter(first?.key, first?.value) + } + + public void parameter(String parameterName, Object parameterValue) { + parameters << new QueryParameter(parameterName, parameterValue) + } + + def equalTo(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO) + } + + def containing(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.CONTAINS) + } + + def matching(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.MATCHING) + } + + def notMatching(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.NOT_MATCHING) + } + + void collect(Closure closure) { + parameters?.each { + parameter -> closure(parameter) + } + } + +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy index 50bd42910c..dab8f873f2 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy @@ -11,6 +11,7 @@ class Request extends Common { DslProperty method Url url + UrlPath urlPath Headers headers Body body @@ -20,6 +21,7 @@ class Request extends Common { Request(Request request) { this.method = request.method this.url = request.url + this.urlPath = request.urlPath this.headers = request.headers this.body = request.body } @@ -32,7 +34,7 @@ class Request extends Common { this.method = toDslProperty(method) } - void url(String url) { + void url(Object url) { this.url = new Url(url) } @@ -40,6 +42,38 @@ class Request extends Common { this.url = new Url(url) } + void url(Object url, @DelegatesTo(UrlPath) Closure closure) { + this.url = new Url(url) + closure.delegate = this.url + closure() + } + + void url(DslProperty url, @DelegatesTo(UrlPath) Closure closure) { + this.url = new Url(url) + closure.delegate = this.url + closure() + } + + void urlPath(String path) { + this.urlPath = new UrlPath(path) + } + + void urlPath(DslProperty path) { + this.urlPath = new UrlPath(path) + } + + void urlPath(String path, @DelegatesTo(UrlPath) Closure closure) { + this.urlPath = new UrlPath(path) + closure.delegate = urlPath + closure() + } + + void urlPath(DslProperty path, @DelegatesTo(UrlPath) Closure closure) { + this.urlPath = new UrlPath(path) + closure.delegate = urlPath + closure() + } + void headers(@DelegatesTo(Headers) Closure closure) { this.headers = new Headers() closure.delegate = headers diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy index da91b4a248..42a26eb26c 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy @@ -1,18 +1,28 @@ package io.codearte.accurest.dsl.internal +import groovy.transform.CompileStatic import groovy.transform.EqualsAndHashCode import groovy.transform.ToString @ToString(includePackage = false, includeFields = true, includeNames = true) @EqualsAndHashCode(includeFields = true) +@CompileStatic class Url extends DslProperty { - Url(DslProperty bodyAsValue) { - super(bodyAsValue.clientValue, bodyAsValue.serverValue) + QueryParameters queryParameters + + Url(DslProperty prop) { + super(prop.clientValue, prop.serverValue) } - Url(String bodyAsValue) { - super(bodyAsValue) + Url(Object url) { + super(url) } - + + void queryParameters(@DelegatesTo(QueryParameters) Closure closure) { + this.queryParameters = new QueryParameters() + closure.delegate = queryParameters + closure() + } + } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/UrlPath.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/UrlPath.groovy new file mode 100644 index 0000000000..ee1d3eed98 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/UrlPath.groovy @@ -0,0 +1,20 @@ +package io.codearte.accurest.dsl.internal + +import groovy.transform.CompileStatic; +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString; + +@ToString(includePackage = false, includeFields = true, includeNames = true) +@EqualsAndHashCode(includeFields = true) +@CompileStatic +class UrlPath extends Url { + + UrlPath(String path) { + super(path) + } + + UrlPath(DslProperty path) { + super(path) + } + +} 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 f5b61625e3..ba6211223d 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -1,4 +1,6 @@ package io.codearte.accurest.dsl + +import groovy.json.JsonBuilder import groovy.json.JsonSlurper class WiremockGroovyDslSpec extends WiremockSpec { @@ -313,6 +315,110 @@ class WiremockGroovyDslSpec extends WiremockSpec { ''') } + def "should generate request with urlPath and queryParameters for client side"() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + urlPath($(client("users"), server("items"))) { + queryParameters { + parameter 'limit': $(client(equalTo("20")), server(containing("10"))) + parameter 'offset': containing("10") + parameter 'filter': "email" + parameter 'sort': ~/^[0-9]{10}$/ + parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server(containing("10"))) + parameter 'age': notMatching("^\\w*\$") + parameter 'name': matching("Denis.*") + } + } + } + response { + status 200 + } + } + when: + def json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "urlPath":"users", + "queryParameters": { + "offset": { + "contains": "10" + }, + "limit": { + "equalTo": "20" + }, + "filter": { + "equalTo": "email" + }, + "sort": { + "matches": "^[0-9]{10}$" + }, + "search": { + "doesNotMatch": "^/[0-9]{2}$" + }, + "age": { + "doesNotMatch": "^\\\\w*$" + }, + "name": { + "matches": "Denis.*" + } + } + }, + "response": { + "status": 200, + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + + def "should generate request with url and queryParameters for client side"() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url(regex(/users\/[0-9]*/)) { + queryParameters { + parameter 'age': notMatching("^\\w*\$") + parameter 'name': matching("Denis.*") + } + } + } + response { + status 200 + } + } + when: + def json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "urlPattern": "users/[0-9]*", + "queryParameters": { + "age": { + "doesNotMatch": "^\\\\w*$" + }, + "name": { + "matches": "Denis.*" + } + } + }, + "response": { + "status": 200, + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + def "should generate stub with some headers section for client side"() { given: GroovyDsl groovyDsl = GroovyDsl.make { @@ -347,4 +453,16 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') } + + def toJsonString(value) { + new JsonBuilder(value).toPrettyString() + } + + def parseJson(json) { + new JsonSlurper().parseText(json) + } + + def toWiremockClientJsonStub(groovyDsl) { + new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + } }