Skip to content

Latest commit

 

History

History
336 lines (247 loc) · 9.67 KB

README.md

File metadata and controls

336 lines (247 loc) · 9.67 KB

GOJI HTTP Client Maven

GOJI stands for: Groovy-oriented and JSON-implying.

License

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Table of contents

Intro

This client wraps around Apache HttpClient and Jackson Databind libraries providing lean Groovy syntax:

given:
def http = new HttpClient(baseUrl: 'http://ice-cream-service.be')
    
when:
def response = http.post(
    path: '/ice-cream?banana=true',
    headers: ['Content-Type': 'application/json'],
    body: [sprinkles: true],
    expecting: BananaIceCream)
    
then:
response.statusCode == ResponseCode.OK
response.body == new BananaIceCream(sprinkles: true)

The library is initially intended for writing easily readable unit-tests but can also but used in other Groovy scripts.

There's also a Java-friendly API available:

class IceCreamTest {
    HttpClient http = new HttpClient("http://ice-cream-service.be");

    @Test
    public void returnsAnIceCream() {
        //when:
        Response<BananaIceCream> response = http
            .post()
            .path("/ice-cream?banana=true")
            .header("Content-Type", "application/json")
            .body(Map.of("sprinkles", true))
            .expecting(BananaIceCream.class);
     
        //then:
        response.getStatusCode() == ResponseCode.OK;
        response.getBody().equals(new BananaIceCream(true));
    }
}

Full Java API reference is available here

Changelog

3.3.0:

3.2.0: (feat) default headers

3.1.1: (security) remove vulnerable dependencies

3.1.0: (feat) query parameter support

3.0.0: (chore) Groovy 4 and other dependency updates

2.0.0: (feature) Java-friendly API

1.4.0: (chore) updated dependencies, including Groovy v2 -> v3 and Jackson (addressing CVE-2019-17531)

1.3.1: (chore) updated dependencies, including jackson-databind version with vulnerabilities

1.3.0: (feat) file upload

1.2.3: (chore) updated dependencies, including jackson-databind version with vulnerabilities

1.2.0: (feature) support for TRACE, OPTIONS and PATCH methods

1.1.0: (doc) maven usage and javadocs

1.0.0: initial release

Usage

GOJI HTTP uses the semantic versioning strategy: MAJOR.MINOR.PATCH.

Check Maven central repository for snippets to add the client as a dependency for your build system.

Request examples

Supported HTTP methods

GET, POST, PUT and DELETE are supported:

http.get()
http.post()
http.put()
http.delete()
http.trace()
http.patch()
http.options()

A request to an arbitrary url

http.get(url: 'http://pizza-delivery.org/margheritas')

Base url

If you want to make a number of requests to a given service, you can specify the baseUrl constructor parameter:

def http = new HttpClient(baseUrl: 'http://water-melon.com')
    
http.get(path: '/slice')

Query

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

http.put(path: '/put?some=query&other=one&other=two')
http.put(path: '/put', query: [some: 'query', other: ['one', 'two']])

NB! if url or path contains a query already, query parameter is ignored.

Request headers

http.put(
    path: '/put',
    headers: [
        Accept: 'application/json',
        'Content-Type': 'application/json'])

Default headers

Default headers are sent with each request. It is also possible to override default headers per request:

def http = new HttpClient(defaultHeaders: [
        Accept: 'application/json',
        'Content-Type': 'application/json']) 
http.put(path: '/put', headers: ['Content-Type': 'application/xml'])

Request body

Any non-string body is serialized as JSON.

String:

http.delete(
    path: '/delete',
    body: '<xml></xml>')

Map:

http.put(
    path: '/put',
    body: [key: 'value']) 

Uploading a file

If an instance of java.io.File is provided as body argument, it will be wrapped into a MultipartFile:

http.put(
    path: '/post',
    body: '/tmp/input.json' as File) 

Handling responses

Response status code

def response = http.get(path: '/get')
    
assert response.statusCode == ResponseCode.OK

Response headers

def response = http.get(path: '/get')
    
assert response.headers == [
    'Content-Type': [
        'application/json',
        'application/vnd.tomtom+json'],
    Connection: 'keep-alive'] 

Response body

By default, the response body is a String:

Response<String> response = http.get(path: '/get')
    
assert response.body == 'A string'

Deserializing JSON responses to Java objects

A valid JSON response body can be deserialized into a Java object.

Response<Map> response = http.get(
    path: '/get',
    expecting: Map)
    
assert response.body == [key: 'value']
Response<BananaIceCream> response = http.get(
    path: '/ice-cream?banana=true',
    expecting: BananaIceCream)
    
assert response.body instanceof BananaIceCream

Deserializing JSON responses to Java generics

Response<List<Map>> response = http.get(
    path: '/get',
    expecting: List, of: Map)
    
assert response.body == [
    [key: 'value'],
    ['another-key': 'another value']]

See more use-cases in tests

Logging

The client uses log4j v2, sample output:

2023-09-23 14:54:20 INFO POST http://localhost:53611/freezer
2023-09-23 14:54:20 INFO     headers: [shelve: top]
2023-09-23 14:54:20 INFO     body: ice-cream
2023-09-23 14:54:20 INFO => response: 200
2023-09-23 14:54:20 INFO     headers: [Content-Type: application/json]
2023-09-23 14:54:20 INFO     body: {"contents": ["ice-cream"]}

Example configuration can be found here.

Future work

  • Verify HTTPS certificates
  • Document URL encoding behavior

Known bugs

None yet!

Creating a release and deploying to Maven Central

Creating a release is usually done from main. Before the release:

  • Make sure the version is updated.
  • Make sure the Changelog (above) is updated.

And then deploy it to Maven Central (not auto-released):

mvn clean deploy -Prelease

And release (when successful):

mvn nexus-staging:release

Contact

artamonov.kirill@gmail.com

Disclaimer

Our primary use-case of this http client is testing our REST services. The client has not been tested for any production use though we don't expect big issues there.