Skip to content

Commit

Permalink
✨ query parameter support
Browse files Browse the repository at this point in the history
  • Loading branch information
artamonovkirill committed Jun 13, 2022
1 parent b586a4e commit 44a6764
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 29 deletions.
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ limitations under the License.
* [Supported HTTP methods](#http-methods)
* [A request to an arbitrary url](#url)
* [Base url](#base-url)
* [Query](#query)
* [Request headers](#request-headers)
* [Request body](#request-body)
* [File upload](#file-upload)
Expand Down Expand Up @@ -87,6 +88,8 @@ Full Java API reference is available [here](doc/JAVA.md)

<a id='changelog'></a>
## Changelog
**[3.1.0](https://search.maven.org/artifact/com.tomtom.http/goji-http-client/3.1.0/jar)**: (feat) query parameter support

**[3.0.0](https://search.maven.org/artifact/com.tomtom.http/goji-http-client/3.0.0/jar)**: (chore) Groovy 4 and other dependency updates

**[2.0.0](https://search.maven.org/artifact/com.tomtom.http/goji-http-client/2.0.0/jar)**: (feature) Java-friendly API
Expand Down Expand Up @@ -117,7 +120,7 @@ GOJI HTTP uses the [semantic versioning](http://semver.org/) strategy: MAJOR.MIN
<dependency>
<groupId>com.tomtom.http</groupId>
<artifactId>goji-http-client</artifactId>
<version>3.0.0</version>
<version>3.1.0</version>
</dependency>
```

Expand Down Expand Up @@ -155,6 +158,21 @@ def http = new HttpClient(baseUrl: 'http://water-melon.com')
http.get(path: '/slice')
```

<a id='query'></a>
### Query:

You can either specify a query in `url` or `path`, or via `query` parameter:

```groovy
http.put(path: '/put?some=query&other=one&other=two')
```

```groovy
http.put(path: '/put', query: [some: 'query', other: ['one', 'two']])
```

> _NB!_ if `url` or `path` contains a query already, `query` parameter is ignored.
<a id='request-headers'></a>
### Request headers:

Expand Down
14 changes: 14 additions & 0 deletions doc/JAVA.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ HttpClient http = new HttpClient("http://water-melon.com");
http.get().path("/slice").execute();
```

### Query:

You can either specify a query in `url` or `path`, or via `query` parameter:

```groovy
http.put().path("/put?some=query&some=more")
```

```groovy
http.put().path("/put").query("some", "query").query("some", "more")
```

> _NB!_ if `url` or `path` contains a query already, `query` parameter is ignored.
### Request headers:

```groovy
Expand Down
22 changes: 21 additions & 1 deletion src/main/groovy/com/tomtom/http/RequestBuilder.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class RequestBuilder {
HttpRequestBase request(Map properties) {
def method = properties['method']
def url = urlFrom properties

def query = properties['query'] as Map
if (query) url = addQuery url, query

def request = requestFor method, url

def headers = properties['headers'] as Map
Expand All @@ -53,14 +57,21 @@ class RequestBuilder {
request
}

private urlFrom(Map properties) {
private String urlFrom(Map properties) {
def url = properties['url'] as String
if (url) return url
def path = properties['path']
if (baseUrl && path) return "$baseUrl$path"
throw new NoUrl()
}

private static addQuery(String url, Map query) {
if (url.contains('?')) return url
url + '?' + query.collect { k, v ->
v instanceof Collection ? v.collect { "$k=$it" }.join('&') : "$k=$v"
}.join('&')
}

private static addHeaders(request, Map headers) {
headers.collect { new BasicHeader(it.key as String, it.value as String) }
.forEach { request.addHeader it }
Expand Down Expand Up @@ -133,6 +144,15 @@ class RequestBuilder {
private Function<Map, Response<T>> method
private Map parameters

ParametersBuilder<T> query(String name, String value) {
parameters.query = parameters.query ?: [:]
if (parameters.query[name])
parameters.query[name] << value
else
parameters.query[name] = [value]
this
}

ParametersBuilder<T> header(String name, String value) {
parameters.headers = (parameters.headers ?: [:]) + [(name): value]
this
Expand Down
91 changes: 91 additions & 0 deletions src/test/groovy/com/tomtom/http/GroovyAPISpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -301,4 +301,95 @@ class GroovyAPISpec extends HttpClientSpec {
'OPTIONS' | WireMock.&options | http.&options
}

@Unroll
def 'executes a #name with query map'() {
given:
mock.givenThat(mockMethod(urlEqualTo('/freezer?foo=bar')).willReturn(ok()))

when:
def response = clientMethod(path: '/freezer', query: [foo: 'bar'])

then:
response.statusCode == OK

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'HEAD' | WireMock.&head | http.&head
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
'DELETE' | WireMock.&delete | http.&delete
'TRACE' | WireMock.&trace | http.&trace
'PATCH' | WireMock.&patch | http.&patch
'OPTIONS' | WireMock.&options | http.&options
}

@Unroll
def 'supports multiple query parameters'() {
given:
mock.givenThat(mockMethod(urlEqualTo('/freezer?foo=bar&foo=baz')).willReturn(ok()))

when:
def response = clientMethod(path: '/freezer', query: [foo: ['bar', 'baz']])

then:
response.statusCode == OK

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'HEAD' | WireMock.&head | http.&head
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
'DELETE' | WireMock.&delete | http.&delete
'TRACE' | WireMock.&trace | http.&trace
'PATCH' | WireMock.&patch | http.&patch
'OPTIONS' | WireMock.&options | http.&options
}

@Unroll
def 'ignores query param if path contains a query'() {
given:
mock.givenThat(mockMethod(urlEqualTo('/freezer?a=b')).willReturn(ok()))

when:
def response = clientMethod(path: '/freezer?a=b', query: [c: 'd'])

then:
response.statusCode == OK

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'HEAD' | WireMock.&head | http.&head
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
'DELETE' | WireMock.&delete | http.&delete
'TRACE' | WireMock.&trace | http.&trace
'PATCH' | WireMock.&patch | http.&patch
'OPTIONS' | WireMock.&options | http.&options
}

@Unroll
def 'ignores query param if URL contains a query'() {
given:
mock.givenThat(mockMethod(urlEqualTo('/freezer?a=b')).willReturn(ok()))

when:
def response = clientMethod(url: "http://localhost:${mock.port()}/freezer?a=b", query: [c: 'd'])

then:
response.statusCode == OK

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'HEAD' | WireMock.&head | http.&head
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
'DELETE' | WireMock.&delete | http.&delete
'TRACE' | WireMock.&trace | http.&trace
'PATCH' | WireMock.&patch | http.&patch
'OPTIONS' | WireMock.&options | http.&options
}
}
81 changes: 54 additions & 27 deletions src/test/groovy/com/tomtom/http/JavaAPISpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ class JavaAPISpec extends HttpClientSpec {
response.statusCode == OK

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'HEAD' | WireMock.&head | http.&head
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
Expand All @@ -77,8 +77,8 @@ class JavaAPISpec extends HttpClientSpec {
response.statusCode == OK

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'HEAD' | WireMock.&head | http.&head
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
Expand All @@ -102,8 +102,8 @@ class JavaAPISpec extends HttpClientSpec {
response.statusCode == OK

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'HEAD' | WireMock.&head | http.&head
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
Expand All @@ -127,8 +127,8 @@ class JavaAPISpec extends HttpClientSpec {
response.statusCode == OK

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'HEAD' | WireMock.&head | http.&head
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
Expand All @@ -147,9 +147,9 @@ class JavaAPISpec extends HttpClientSpec {

when:
def response = clientMethod()
.path('/freezer')
.body('ice-cream')
.execute()
.path('/freezer')
.body('ice-cream')
.execute()

then:
response.statusCode == OK
Expand All @@ -171,9 +171,9 @@ class JavaAPISpec extends HttpClientSpec {

when:
def response = clientMethod()
.path('/freezer')
.body(file)
.execute()
.path('/freezer')
.body(file)
.execute()

then:
response.statusCode == OK
Expand All @@ -194,9 +194,9 @@ class JavaAPISpec extends HttpClientSpec {

when:
def response = clientMethod()
.path('/freezer')
.body([type: 'ice-cream'])
.execute()
.path('/freezer')
.body([type: 'ice-cream'])
.execute()

then:
response.statusCode == OK
Expand Down Expand Up @@ -227,8 +227,8 @@ class JavaAPISpec extends HttpClientSpec {
response.statusCode == OK

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'HEAD' | WireMock.&head | http.&head
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
Expand All @@ -254,8 +254,8 @@ class JavaAPISpec extends HttpClientSpec {
response.body == 'ice-cream'

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
'DELETE' | WireMock.&delete | http.&delete
Expand All @@ -281,8 +281,8 @@ class JavaAPISpec extends HttpClientSpec {
response.body == [contents: ['ice-cream']]

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
'DELETE' | WireMock.&delete | http.&delete
Expand All @@ -309,8 +309,8 @@ class JavaAPISpec extends HttpClientSpec {
response.body == [[type: 'ice-cream']]

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
'DELETE' | WireMock.&delete | http.&delete
Expand Down Expand Up @@ -348,8 +348,8 @@ class JavaAPISpec extends HttpClientSpec {

when:
def response = clientMethod()
.path('/freezer')
.execute()
.path('/freezer')
.execute()

then:
response.statusCode == OK
Expand All @@ -370,4 +370,31 @@ class JavaAPISpec extends HttpClientSpec {
'OPTIONS' | WireMock.&options | http.&options
}

@Unroll
def 'executes a #name with query map'() {
given:
mock.givenThat(mockMethod(urlEqualTo('/freezer?foo=bar&foo=baz')).willReturn(ok()))

when:
def response = clientMethod()
.path('/freezer')
.query('foo', 'bar')
.query('foo', 'baz')
.execute()

then:
response.statusCode == OK

where:
name | mockMethod | clientMethod
'GET' | WireMock.&get | http.&get
'HEAD' | WireMock.&head | http.&head
'POST' | WireMock.&post | http.&post
'PUT' | WireMock.&put | http.&put
'DELETE' | WireMock.&delete | http.&delete
'TRACE' | WireMock.&trace | http.&trace
'PATCH' | WireMock.&patch | http.&patch
'OPTIONS' | WireMock.&options | http.&options
}

}

0 comments on commit 44a6764

Please sign in to comment.