GOJI stands for: Groovy-oriented and JSON-implying.
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.
- Intro
- Changelog
- Usage
- Request examples
- Handling responses
- Logging
- Future work
- Known bugs
- Contact
- Disclaimer
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
- (feat) logging
- (chore) migrate to org.wiremock & httclient5
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
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.
GET
, POST
, PUT
and DELETE
are supported:
http.get()
http.post()
http.put()
http.delete()
http.trace()
http.patch()
http.options()
http.get(url: 'http://pizza-delivery.org/margheritas')
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')
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
orpath
contains a query already,query
parameter is ignored.
http.put(
path: '/put',
headers: [
Accept: 'application/json',
'Content-Type': 'application/json'])
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'])
Any non-string body is serialized as JSON.
String
:
http.delete(
path: '/delete',
body: '<xml></xml>')
Map
:
http.put(
path: '/put',
body: [key: 'value'])
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)
def response = http.get(path: '/get')
assert response.statusCode == ResponseCode.OK
def response = http.get(path: '/get')
assert response.headers == [
'Content-Type': [
'application/json',
'application/vnd.tomtom+json'],
Connection: 'keep-alive']
By default, the response body is a String:
Response<String> response = http.get(path: '/get')
assert response.body == 'A string'
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
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
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.
- Verify HTTPS certificates
- Document URL encoding behavior
None yet!
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
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.