Easy way to render JSON with HTTP status code in Grails - grails

Is there a shorthand way to do this without explicit "text/json" designation?
def remoteError = {
render( status: 500, contentType: "text/json"){
error( exception: "a remote exception occurred")
}
}
I tried using as JSON...no content is returned but the status code is correct...
render( status: 500, exception: params.exception) as JSON

If you use a converter parameter to render then you cannot specify any other parameter such as status like you normally would when using gsp views. You can however set the response status prior to calling render:
response.status = 500
render([error: 'an error occurred'] as JSON)

render(status:500,text:(errors as JSON).toString(),contentType: 'application/json')

Related

Problem when displaying errors in friendly way in Zapier

I'm trying to display errors in a friendly way, but I'm always getting the errors stack trace with console logs that I want to get rid of.
The idea is to create a Lead in our platform using any source, for example, Google Sheets.
When an invalid email is provided in the lead and posted to our API, I'm getting the expected message I want to display followed by the stack trace.
My custom error message is
INVALID FORMAT for email. Object didn't pass validation for format email: as1#mail.
But this is what I'm getting:
INVALID FORMAT for email. Object didn't pass validation for format email: as1#mail. What happened: Starting POST request to https://cosmo-charon-production.herokuapp.com/v1/lead/vehicle Received 500 code from https://cosmo-charon-production.herokuapp.com/v1/lead/vehicle?api-key=gIBp04HVdTgsHShJj6bXKwjbcxXTogsh after 62ms Received content "{"code":"SCHEMA_VALIDATION_FAILED","message":"Request validation failed: Parameter (lead) failed sch" INVALID FORMAT for email. Object didn't pass validation for format email: as1#mail. Console logs:
Image showing error displayed in Zapier
I've added a middleware for ErrorHandling into afterResponse, just as one of the examples provided in Zapier docs.
The function analyzeAndParse() receives an error object from the API and returns a string with the error message translated in a friendly way
const checkForErrors = (response, z) => {
// If we get a bad status code, throw an error, using the ErrorTranslator
if (response.status >= 300) {
throw new Error(analyzeAndParse(response.json))
}
// If no errors just return original response
return response
}
This is the code that creates a Lead in our platform, making a request to our API.
function createLead (z, bundle) {
const industry = bundle.inputData.industry
// add product to request based on the inputFields
leadType[industry].addProductFields(bundle.inputData)
const requestOptions = {
url: `${baseUrl}lead/${_.kebabCase(industry)}`,
method: 'POST',
body: JSON.stringify(checkSourceForCreate(bundle.inputData)),
headers: {
'content-type': 'application/json'
}
}
return z.request(requestOptions).then((response) => {
if (response.status >= 300) {
throw new Error(analyzeAndParse(response.content))
}
const content = JSON.parse(response.content)
if (content && content.leads) {
// get only the last lead from the list of leads
content.lead = content.leads[0]
delete content.leads
}
return content
})
}
Any ideas?
Thanks!

ActionCable - Respond With Error

With ActionCable, how can I respond with an error after receiving data from a client?
For example, when the client fails to authenticate, ActionCable throws UnauthorizedError which responds with a 404. I want to respond with a 422, for example, when the data that the client sent is invalid.
ActionCable.server.broadcast "your_channel", message: {data: data, code: 422}
Then in your.coffee file:
received: (res) ->
switch res.code
when 200
# your success code
# use res.data to access your data message
when 422
# your error handler
From what I could gather there's no "Rails way" to do this, the answer given by #Viktor seems correct. In summary: ensure all messages are broadcasted with both data and code, then switch by code in the client.
For a more modern and ES6 example see below:
In rails:
require 'json/add/exception'
def do_work
// Do work or raise
CampaignChannel.broadcast_to(#campaign, data: #campaign.as_json, code: 200)
rescue StandardError => e
CampaignChannel.broadcast_to(#campaign, data: e.to_json, code: 422) # Actually transmitting the entire stacktrace is a bad idea!
end
In ES6:
campaignChannel = this.cable.subscriptions.create({ channel: "CampaignChannel", id: campaignId }, {
received: (response) => {
const { code, data } = response
switch (code) {
case 200:
console.log(data)
break
default:
console.error(data)
}
}
})

Grails Integration Test Controller can't be re-used?

In a grails integration test, I have code that resembles this:
def ctrlA = new MyController()
... make some request that returns 'ok' ...
assert ctrlA.response.json.status == 'ok'
def ctrlB = new MyController()
... make some request that returns 'error' ...
assert ctrlB.response.json.status == 'error' // fails; status still equals 'ok'
Problem: Even when ctrlB actually does return a json response that looks like { status: 'error' }, I'm actually seeing { status: 'ok' }, the value that was in ctrlA.response.json!! My logs in the controller indicate that 'error' is most definitely being returned.
Why is this?
Ah. Don't need the separate ctrlA and ctrlB at all. Just call ctrl.response.reset() in between.

Parsing JSON data in RAILS 3.0.6 received from HTTP POST with content-type application/x-www-form-urlencoded

I am receiving an HTTP POST from a 3rd party with content-type as 'application/x-www-form-urlencoded' in my RAILS 3.0.6 controller
I tried parsing the request both as -
job_status = params['job']['status']
as well as
recd_json = JSON.parse(params)
job_status = recd_json['job']['status']
But in both cases the rails controller is throwing the error 'NoMethodError (You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.'
I am not sure how else can JSON (encoded using this content type) can be parsed..Any help is appreciated :)
Heres the json being posted:
{
"name": "JSON Parser",
"url": "job/test/",
"job": {
"status": "SUCCESS",
"url": "job/356/"
}
}
Heres the params as returned by the RAILS controller using the Rails.Logger:
{"{ \"name\": \"JSON Parser\", \"url\": \"job/test/\", \"job\": { \"status\": \"SUCCESS\", \"url\": \"job/356/\" }}"=>nil, "action"=>"receive_jobs_updates", "controller"=>"jobs"}
The Error trace:
NoMethodError (You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]):
app/controllers/jobs_controller.rb:32:in `receive_jobs_updates'
The controller code:
def receive_job_updates
Rails.logger.info params
job_name = params['name']
job_status = params['job']['status']
render :text=> "All ok" , :status => :ok
end
You need to create a Job object: job_status = Job.new(params[:job]).status

Grails Content Negotiation - handling unsupported type

We are using content negotiation in our service using the Accept header and the withFormat method....the issue we are facing is that we want to return a 406 http status if the Accept header has a type which is not supported by our service....can anybody give us some ideas on how would we go about doing this?
return withFormat {
html {
render(view: "itWorked", model: data)
}
json {
render(data as JSON)
}
xml {
render(data as XML)
}
}
render(status: 406, text: 'ERROR')

Resources