Using Kong Unirest With Groovy
One of the benefits of the Groovy scripting language is that it’s built on top of Java. As a result, you can typically use all sorts of great libraries from the Maven Repository via Grape. This is something I’ve leveraged before with both JSoup and msal4j. What tends to be a pain, however, is knowing what to use for the corresponding import
statement(s). While some packages do a great job of including this in their documentation, others assume you're writing full-blown Java with an IDE and that there's no need to include that in documentation or examples because it's essentially boilerplate if the IDE is handling it for you.
This was the exact situation I found myself in recently with Kong’s Unirest library. In the same project where I was using the Post Test Server V2, I was essentially pasting Groovy code into the web interface for a SaaS application to create background services in it. As the SaaS app runs Java under the hood, Groovy is the language they chose for custom scripting. One of the challenges in this scenario is that how you make a REST connection is going to depend on the version of the JVM everything is running on; that’s information which typically isn’t readily available in these scenarios. For example, the way I would make an HTTP call in Java 8 is different from how I would do it in Java 11 because there are different classes available, but without any information from the platform I can only make a simple attempt and see if it fails or not.
To abstract this problem away, the SaaS platform in question this time automatically imported the Unirest library. This was great, and my code worked fine when I tested pasting it into the web UI. However, that’s not an efficient way to develop something, so I wanted to work on the code locally and then only paste in the finished product. While it took no time at all to find the library on the Maven Repository, what to import was another matter entirely. My initial searches online turned up the following statements:
import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.JsonNode; import com.mashape.unirest.http.Unirest; import com.mashape.unirest.http.exceptions.UnirestException;
These, however, immediately resulted in errors with Groovy having absolutely no idea what those classes were. A few things tipped me off that this information might be dated. First was simply that none of the posts referencing these imports had been made in the past 5 years. Second, the path matched up with the artifact name of an old version of the library which hadn’t been updated since 2016:
By contrast, the newer version I was using lists a different artifact name of: com.konghq
You may notice that the icon for the new library matches the one on the official documentation. Unfortunately, just replacing com.mashape
with com.konghq
as follows didn't give me any success, either:
import com.konghq.unirest.http.HttpResponse; import com.konghq.unirest.http.JsonNode; import com.konghq.unirest.http.Unirest; import com.konghq.unirest.http.exceptions.UnirestException;
After more searching than I care to think about over the span of several days, I finally stumbled across some relatively recent sample code that only used kong
rather than com.konghq
. The working imports looked as follows:
import kong.unirest.HttpResponse; import kong.unirest.JsonNode; import kong.unirest.Unirest; import kong.unirest.UnirestException; import kong.unirest.UnirestParsingException;
Excellent! With that part finally out of the way, I just had to get familiar with how to use it to make calls and parse the data I got back. I started off with the following:
def response = Unirest.get("https://api.github.com/repos/mastodon/mastodon/commits") .header('Content-Type', 'application/json') .header('accept', 'application/json') .basicAuth('username', 'token') .asJson()
You may recognize the URL I’m testing with as the one I used for my Git Commit Metrics script. This worked swimmingly, and I could print the JSON results. However, the results were a JsonNode object. Luckily, the documentation showed me there’s a .getArray()
method that can be used to get a JSONArray object. That object is something I can iterate through with a closure and access the subsequent content via normal map mechanics. I ultimately ended up handling the response via the following code as a test, which prints only the commit message:
if( response.isSuccess() ) { def responseArray = response.getBody().getArray() responseArray.each { singleResponse -> println singleResponse.commit.message } }
One additional thing worth mentioning is that it took me a bit longer than I’m alluding to here to hit upon the JsonObject and JSONArray documentation above. Prior to finding that, I leveraged the following code to get more specific information regarding the parsing errors I was seeing, as in my original request I was testing things like replacing .asJson()
with .asObject(Map)
and .asObject(List)
.
UnirestParsingException ex = response.getParsingError().get(); println ex.getOriginalBody() println ex.getMessage() println ex.getCause()
This combined with prodigious use of things like response.getClass().getName()
finally got me in the right place.
Originally published at https://borked.sh on May 4, 2022.