How to render a custom error message in active_model_serializers JSON API - active-model-serializers

As AMS docs say, we could use ActiveModel::Serializer::ErrorSerializer to generate JSON API errors response. It seems like it calls errors and suppose to have an ActiveModel object.
So the below code works and send the error response message but not the code (ite responds with 500 code), and it does not use ErrorSerializer either.
def user_not_authorized
render jsonapi: errors_response, status: :unathorized
end
def errors_response
{
errors:
[
{ message: 'You are not authorized to perform this action.' }
],
title: 'Invalid Attribute'
}
end
Moreover, it displays in the console:
Rendered ActiveModel::Serializer::Null with Hash
When trying to use ErrorSerializer as follows:
def user_not_authorized
render jsonapi: errors_response, status: :unathorized, serializer: ActiveModel::Serializer::ErrorSerializer
end
raises the error:
NoMethodError (undefined method `errors' for #<Hash:0x00007fcf12d84d78>)
There was an issue opened at AMS repo about that and sems like it is not implemented.
Any idea on what is right way to do that in Rails API app?

Related

Unable to update the custom_attribute field using the node.js intercom-client

I am using the official Intercom node.js 'intercom-client' library (latest version -- 4.0, and node version 16.18) to try to add data to the custom_attribute field for an existing conversation, but keep getting an 'attribute does not exist' error.
According to the Intercom API documentation, it is possible to modify the custom_attribute field of a conversation: https://github.com/intercom/intercom-node#update-a-conversation
https://developers.intercom.com/intercom-api-sreference/reference/update-a-conversation
Using the node.js 'intercom-client' library to interact with the API in javascript, I attempted the following:
Passing the following object: { test: 'hope' }; to the client.conversations.update() method using the following code:
const customAttributes = { test: 'hope' };
client.conversations.update({
id: '68',
customAttributes,
});
I get the following error:
errors: [
{
code: 'parameter_invalid',
message: "Conversation attribute 'test' does not exist"
}
]
I would have expected it to modify the conversation object successfully.
Per the official documentation on the git-hub repo, the following should work:
const response = await client.conversations.update({
id,
markRead: true,
customAttributes: {
anything: 'you want',
},
});
but I get the same error, when I use this code exactly as it is written. I have tried to use snake case for the 'customAttributes' field, i.e. tried 'custom_attributes' without success. I have also tried to pass this object to the conversation at time it is created (using client.conversation.create()), given that the error is suggesting that the object should have been passed earlier, but without success (nothing happens...).
Thank you for your help!
Dan

Passing custom errors back to AWS API Gateway from AWS Lambda using node.js

I'd really like to be able to pass custom errors back out of my Lambda function via the API Gateway.
I'm generating the errors this way:
if (error) { // failure
APIGatewayResult = { // set error object
statusCode: 608,
message: 'File upload to buffer failed.',
error: error
};
done();
};
I'm fairly certain the format above is either parsed incorrectly or I can't pass it back out like this:
done = (err, res) => callback(null, {
statusCode: err ? APIGatewayResult.statusCode : APIGatewayResult.statusCode,
body: err ? JSON.stringify(APIGatewayResult) : JSON.stringify(APIGatewayResult),
headers: {
'Content-Type': 'application/json',
},
});
I'd like the response I get from API gateway to look like this:
{
"statusCode": 608,
"message": "File upload to buffer failed.",
"error": {}
}
Instead of this:
{
"message": "Internal server error"
}
There are two parts to your question:
1) How to handle Lambda error messages in API Gateway:
There are numerous guides available which explain how to parse Lambda error responses in API Gateway to return them in the desired format to consumers. For example: Error Handling Patterns in Amazon API Gateway and AWS Lambda.
Important to note that you're parsing null to the first parameter of the callback function from your lambda. The first parameter is the error response message, so by providing it as null the Lambda will be returning a successful 200 response back to API Gateway.
2) How to override the generic unhandled exception message in API Gateway:
This is required because as mentioned in the comments, the error you're receiving appears to have been thrown due to an unhandled exception in your application. You'll need to review the logs to identify the source of the issue.
But to change the format of the default error response object you'll need to add a custom Gateway response in API Gateway.
It's difficult to offer a more concrete example given the limited information provided but this should be enough to point you in the right direction to find what you're looking for.

axios.post() error about circular structure

Please guide me in case I'm not in proper use of axios. This simple piece of code can directly run:
const axios = require('axios')
axios.post('https://exp.host/--/api/v2/push/send', {"to":["ExponentPushToken[xxxxxxx]"],"title":"test title","body":"test body."})
.then(responseExpo => {
console.log("expo replied normally: " + JSON.stringify(responseExpo));
})
.catch(error => {
console.log("expo replied with error: " + JSON.stringify(error,null,4));
});
The result is:
Promise { <pending> }
expo replied with error: {}
"axios": "^0.19.2"
I tried to post with api tools and see a response with normal 200 status code:
{
"data":[
{
"status": "error",
"message": "\"ExponentPushToken[xxxxxxx]\" is not a registered push notification recipient",
"details":{
"error": "DeviceNotRegistered"
}
}
]
}
(you may ignore the "error": "DeviceNotRegistered" inside this json cos it's expected because I have put an invalid xxxxx input value when calling the api. Even putting a valid input value the result is still returning to the catch block with empty error)
I'm expecting it to return to the then block cos the server actually response with 200 with well formatted json result.
Have I done something wrong so that the call returns to the catch block? Cos the error is empty I have no idea what went wrong.
===============================
after jfriend's reminder I changed to directly disply the error.
console.log("expo replied with error: " + error);
it is show like this now:
Promise { <pending> }
expo replied with error: TypeError: Converting circular structure to JSON
--> starting at object with constructor 'ClientRequest'
| property 'socket' -> object with constructor 'TLSSocket'
--- property '_httpMessage' closes the circle
Anyone can let me know what exactly it means and guide me how to correct my usage?
(problem resolved). the response (responseExpo in the question) is neither a plain data JSON nor a plain string. it is an object with (see github.com/axios/axios#response-schema) some attributes. The real response content is inside "response.data". I was wrongly treating the response to be a plain json object or the http response content.
I had a similar problem and as solution, I used HttpService from nestjs which returns Observable<AxiosResponse<T>>. I fixed the problem by piping and plucking the request like this:
http.put<T>(url, data, config).pipe(pluck('data'))
I had a similar problem with HttpService from nestjs which returns Observable<AxiosResponse<any>>. I resolve with:
this.httpService.post(this.legacyAccessTokenEndpoint, form, { headers: form.getHeaders() }).pipe(map(x => x?.data))

How to accept plain text in a route handler

I'm using FilePond to upload files to a hapi.js 17.9 API. FilePond allows automatic uploads after a user selects a file, and has a UI for deleting/reverting/undoing an uploaded file. Per FilePond's documentation...
FilePond sends DELETE request with 12345 as body by tapping the undo button
Where 12345 is an ID supplied by the server when the file was uploaded. Note this is not JSON, it's a plain text body.
I have a hapi.js route that's set up to handle DELETE methods, with the default validation settings. When FilePond sends it's request, Hapi responds with a 400 error before my handler code executes, and prints this message in the console:
Validation-failAction:error ValidationError: "value" must be an object
at Object.exports.process (/home/lamont/projects/rooster/api/node_modules/hapi/node_modules/joi/lib/errors.js:203:19)
at internals.Object._validateWithOptions (/home/lamont/projects/rooster/api/node_modules/hapi/node_modules/joi/lib/types/any/index.js:764:31)
at module.exports.internals.Any.root.validate (/home/lamont/projects/rooster/api/node_modules/hapi/node_modules/joi/lib/index.js:147:23)
at Object.internals.input (/home/lamont/projects/rooster/api/node_modules/hapi/lib/validation.js:83:63)
at exports.payload (/home/lamont/projects/rooster/api/node_modules/hapi/lib/validation.js:50:22)
at Request._lifecycle (/home/lamont/projects/rooster/api/node_modules/hapi/lib/request.js:263:62)
at process._tickCallback (internal/process/next_tick.js:68:7)
The hapi docs strongly imply the default payload validation options don't do any payload validation, so I'm a little surprised that this scenario is even a problem.
I've tried the following:
options.payload= {parse: false}
options.payload= {allow: "text\*"}
options.validate= { payload: async (v, o) => { return v } }
options.validate= { payload: true }
options.validate= { payload: false }
options.validate= undefined
options.validate= null
Edit:
Based on the suggestion of one of the guys on my team, I've also tried
options.validate : { payload: (() => { return Joi.string(); })() }
and defining my route options with no validate property at all (which ought to be functionally equivalent to an explicit undefined but who knows).
In all these cases, I still get the above validation error. Is it just not possible to write a hapi route that accepts plain, non-json text?

send error message back to browser in nodejs

I have a node API that is working fine when tested using postman.
But when I use this API in my angular project there occurs an error and browser don't get any response there it keep waiting for a response. When I go to console I see the error message.
How I can make that error message to be sent back to the browser with full stack trace
In general, you will need to catch that error, then populate http response object with it just the same as if you were sending successful response data back to the requestor.
Synchronous processing:
try {
// do my requested stuff
res.status(200).json({something:"returned"});
} catch(ex) {
res.status(500).json(ex);
};
Promises:
Promise.resolve()
.then(() => {
// do my requested stuff
// return my results stuff to the client
res.status(200).json({something:"returned"});
})
.catch((ex) => {
// return 500 error and exception data to the client
res.status(500).json(ex);
});
Also, as standard practice you should catch all errors, and at the very least, you should return a 500 to the browser res.status(500) so you don't leave it hanging when unexpected issues arise.
And, of course you can return html rather than json, and/or more info in the response.
Good luck.

Resources