groovy: how to catch exceptions on AsyncHttpBuilder - json

i'm trying to make an async call to a domain. The following code works well if i specify a valid address with json response, but when the address is not valid, i want to be able to catch any possible exceptions.
How can i catch the returned exception?
Here an extract from stacktrace:
Message: Invalid JSON String
...
http.AsyncHTTPBuilder - Exception thrown from response delegate:
groovyx.net.http.HTTPBuilder$RequestConfigDelegate#420db81e
Here the code:
def http = new AsyncHTTPBuilder( poolSize : 1,
contentType : ContentType.JSON )
def futureResult
futureResult = http.request( "http://www.notexistingdomainxyzwq.com/",
Method.GET,
ContentType.JSON ) {
response.success = { resp, json ->
log.info("SUCCESS")
}
response.failure = { resp, json ->
log.info("ERROR")
}
}
log.info("Call started");
try {
while (!futureResult.done) {
log.info('waiting...')
log.info("DONE: ${futureResult.done}")
Thread.sleep(1000)
}
} catch(ex) {
log.error("EXCE ${ex}")
}
log.info("Call completed")

If you call futureResult.get() to block and wait for the result, this will throw the exception which you can catch:
try {
def result = futureResult.get()
log.info( "Done: $result" )
} catch(ex) {
log.error("EXCE ${ex}")
}

Related

Post Request in Kotlin

I'm trying to do a post request in Android Studio written in Kotlin
I'm posting a JSON object to our server and then the server is returning a JSON object back. But what I'm doing here is decoding the response body as a string and then converting it into the data structure we need. I'm sure there is a better and simpler way to do what I need done.
My current code works but the major issue I'm having is formatting the string if our objects have nested objects which is why I want to figure out a better way to turn the response body into a json object.
I'm not too familiar with many request libraries for kotlin but I have looked into okhttp3 but I'm not sure how to post a json object, attach headers and decode the response body into a json object.
I know for okhttp3 I need to convert the json object to a string to post other than that I'm lost.
Breakdown of what's needed:
Post JSON Object To Server
Send Headers With Post Request
Decode Response Body into JSON Object/ Kotlin Equivalent
Simplify What I'm Trying to Do if Possible
This is the current code I have
private fun postRequestToGetDashboardData() {
val r = JSONObject()
r.put("uid", muid)
r.put("token", mtoken)
SendJsonDataToServer().execute(r.toString());
}
inner class SendJsonDataToServer :
AsyncTask<String?, String?, String?>() {
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
if (result.equals(null)) {
val t = Toast.makeText(this#Home, "No devices to display", Toast.LENGTH_LONG)
t.setGravity(Gravity.CENTER, 0, 0)
t.show()
} else {
intentForUnique.putExtra("FirstEndpointData", result)
var list = handleJson(result)
adapter.submitList(list)
dashboardItem_list.adapter = adapter
adapter.notifyDataSetChanged();
dashboardItem_list.smoothScrollToPosition(0);
}
}
override fun doInBackground(vararg params: String?): String? {
val JsonDATA = params[0]!!
var urlConnection: HttpURLConnection? = null
var reader: BufferedReader? = null
try {
val url = URL("URL");
urlConnection = url.openConnection() as HttpURLConnection;
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Authorization", mtoken);
urlConnection.setRequestProperty("Accept", "application/json");
val writer: Writer =
BufferedWriter(OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8"));
writer.write(JsonDATA);
writer.close();
val inputStream: InputStream = urlConnection.getInputStream();
if (inputStream == null) {
return null;
}
reader = BufferedReader(InputStreamReader(inputStream))
var inputLine: String? = reader.readLine()
if (inputLine.equals("null")) {
return null
} else {
return inputLine
}
} catch (ex: Exception) {
Log.e(TAG, "Connection Failed", ex);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (ex: Exception) {
Log.e(TAG, "Error closing stream", ex);
}
}
}
return null
}
}
private fun handleJson(jsonString: String?): ArrayList<SensorData> {
val jsonArray = JSONArray(jsonString)
val list = ArrayList<SensorData>()
var x = 0
while (x < jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(x)
list.add(
SensorData(
jsonObject.getInt("deviceId"),
// jsonObject.getString("deviceName"),
jsonObject.getInt("battery"),
jsonObject.getString("dateTime"),
jsonObject.getInt("airValue"),
jsonObject.getInt("waterValue"),
jsonObject.getInt("soilMoistureValue"),
jsonObject.getInt("soilMoisturePercent")
)
)
x++
}
return list
}
So the json data being returned back is an array of this structure (our backend is written in Go)
type Device struct {
DeviceID int `bson:"deviceId" json:"deviceId"`
Battery int `bson:"battery" json:"battery"`
DateTime time.Time `bson:"dateTime" json:"dateTime"`
AirValue int `bson:"airValue" json:"airValue"`
WaterValue int `bson:"waterValue" json:"waterValue"`
SoilMoistureValue int `bson:"soilMoistureValue" json:"soilMoistureValue"`
SoilMoisturePercent int `bson:"soilMoisturePercent" json:"soilMoisturePercent"`
}

Create parent entity and children and redirect request in fluent vapor

I´m trying to create an entity Task and one children using the same request object
func create(_ req: Request) throws -> Future<Response> {
return try req.content.decode(Task.TaskForm.self).flatMap { taskForm in
let user = try req.requireAuthenticated(User.self)
let task = Task(name: taskForm.name, userId: user.id!)
return task.save(on: req).map { t in
let interval = try Interval(taskId: t.requireID())
let t = interval.save(on: req)
return t.save(on: req).map { _ in
return req.redirect(to: "/dashboard")
}
}
}
}
The error that I'm getting is this one:
Cannot convert return expression of type 'EventLoopFuture' to return type 'Response'.
Any ideas on what's the problem?
This code should work
func create(_ req: Request) throws -> Future<Response> {
return try req.content.decode(Task.TaskForm.self).flatMap { taskForm in
let user = try req.requireAuthenticated(User.self)
let task = Task(name: taskForm.name, userId: user.id!)
return task.create(on: req).flatMap { t in
let interval = try Interval(taskId: t.requireID())
return interval.create(on: req).flatMap { _ in
return t.create(on: req).transform(to: req.redirect(to: "/dashboard"))
// or your previous variant
// return t.create(on: req).map { _ in
// return req.redirect(to: "/dashboard")
// }
}
}
}
}
There are a few things you could learn
use map when you have to return non-future result
use flatMap you have to return Future<> result
use create instead of save when you are creating object in the db
don't leave future calls without handling them like you do on line #7

Get values from a JSON response in Groovy

I have a groovy script, it does an easy API call, and I am getting as response a JSON body.
How can I get, from the JSON body, a single value, and use it as a variable?
newRelease.request(GET) { req ->
requestContentType = ContentType.JSON
headers.'X-Octopus-ApiKey' = 'API-xxx'
response.success = { resp, JSON ->
return JSON
}
response.failure = { resp ->
return "Request failed with status ${resp.status}"
}
}
and this is the response
[DeploymentProcessId:deploymentprocess-Projects-370, LastReleaseVersion:null, NextVersionIncrement:0.0.41, VersioningPackageStepName:null, Packages:[], Links:[Self:/api/Spaces-1/deploymentprocesses/deploymentprocess-Projects-370/template]]
So what I am trying to extract is the NextVersionIncrement.
Any idea?

HTTPBuilder set request contenttype

I am using the following code to execute a HTTP POST towards an external system. The problem is that the external system always gets a 'null' content type when using the code below. Is there a way to set the contenttype when using HTTPBuilder.
I tried other tools that execute the same request but then the remote system gets a good contentType ('application/json').
def execute(String baseUrl, String path, Map requestHeaders=[:], Map query=[:], method = Method.POST) {
try {
def http = new HTTPBuilder(baseUrl)
def result = null
// perform a ${method} request, expecting TEXT response
http.request(method, ContentType.JSON) {
uri.path = path
uri.query = query
// add possible headers
requestHeaders.each { key, value ->
headers."${key}" = "${value}"
}
// response handler for a success response code
response.success = { resp, reader ->
result = reader.getText()
}
}
return result
} catch (groovyx.net.http.HttpResponseException ex) {
ex.printStackTrace()
return null
} catch (java.net.ConnectException ex) {
ex.printStackTrace()
return null
}
}
Adding a specific header to the request seems to solve my problem.
def execute(String baseUrl, String path, Map requestHeaders=[:], Map query=[:], method = Method.POST) {
try {
def http = new HTTPBuilder(baseUrl)
def result = null
// perform a ${method} request, expecting TEXT response
http.request(method, ContentType.JSON) {
uri.path = path
uri.query = query
headers.'Content-Type' = 'application/json'
// add possible headers
requestHeaders.each { key, value ->
headers."${key}" = "${value}"
}
// response handler for a success response code
response.success = { resp, reader ->
result = reader.getText()
}
}
return result
} catch (groovyx.net.http.HttpResponseException ex) {
ex.printStackTrace()
return null
} catch (java.net.ConnectException ex) {
ex.printStackTrace()
return null
}
}
Try setting the requestContentType in the body of your request block...
http.request(method, ContentType.JSON) {
uri.path = path
uri.query = query
requestContentType = groovyx.net.http.ContentType.URLENC
.......
}

Get html body from response in groovy

I'm trying to see if a specific string exists in an html page but I can't seem to find an easy way to get the string that represents the body.
I've attempted:
http.request(Method.GET, { req ->
uri.path = '/x/app/main'
response.success = { resp, reader ->
assert resp.status == 200
println reader.text.startsWith('denied')
}
response.failure = { resp ->
fail("Failure reported: ${resp.statusLine}")
}
})
but reader.text is a NodeChildren object.
How do I get the html (or more specifically, the contexts of the body) as a string?
You can get an input stream directly off of the response. Try this:
http.request(Method.GET, { req ->
uri.path = '/x/app/main'
response.success = { resp ->
assert resp.status == 200
println resp.entity.content.text.startsWith('denied')
}
response.failure = { resp ->
fail("Failure reported: ${resp.statusLine}")
}
})