Currently the Namespace parser validates the request arguments and throws error like
{
"errors": {
"file": "Missing required parameter in an uploaded file"
},
"message": "Input payload validation failed"
}
From the flask-app i want to intercept or handle these exceptions and send a customised response for consistency like
{
"errors": {
"file": "Missing required parameter in an uploaded file"
},
"message": "Input payload validation failed",
"id" : "some customer id "
}
Is it possible to handle this exception from app level instead of doing it for every api
According to this issue https://github.com/noirbizarre/flask-restplus/issues/530 there is a workaround to have a personalized message.
That said, it is possible to implement this with the definition of the BadRequest error handler and modify the data attribute:
#api.errorhandler(BadRequest)
def bad_request(self):
self.data.update({'id': 'some customer id'})
return {}, 400
Though, there is no clean way to avoid the empty dictionary return, as it is discarded.
Related
I have a chat lambda that stores messages into dynamodb. If the user is not authorized to add the message, I need to return a custom error exception with unique errorType and message to my client.
I have setup my custom error using the documentation at https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-lambda.html
i.e.:
export default class ForbiddenError extends Error {
/**
* ForbiddenError constructor
* #param {string} name
* #param {string} message
*/
constructor(name = null, message = null) {
message = message || 'You are forbidden from performing this action.'
super(message)
this.name = name || 'Forbidden'
}
}
and then I throw the error inside my app via:
throw new ForbiddenError()
When I test my lambda locally, everything works nicely, the code encounters an error. I catch it and call
context.fail(error)
Even when I test my lambda using a testing lambda in the AWS console, I get a beautiful response containing both the message and the error type:
class ForbiddenError extends Error {
constructor(message) {
super(message)
this.name = 'ForbiddenError'
}
}
exports.handler = (event, context, callback) => {
throw new ForbiddenError('You are forbidden from performing this action.')
return context.fail('test');
};
and the error:
{
"errorType": "ForbiddenError",
"errorMessage": "You are forbidden from performing this action.",
"trace": [
"ForbiddenError: You are forbidden from performing this action.",
" at Runtime.exports.handler (/var/task/index.js:9:21)",
" at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
]
}
but when I call my lambda using appsync, suddenly only the message is passed into the error, but the errorType is always the same: Lambda:Unhandled i.e.:
{
"graphQLErrors": [
{
"path": [
"storeStreamChatMessage"
],
"data": null,
"errorType": "Lambda:Unhandled",
"errorInfo": null,
"locations": [
{
"line": 2,
"column": 3,
"sourceName": null
}
],
"message": "You are forbidden from performing this action."
}
],
"networkError": null,
"message": "GraphQL error: You are forbidden from performing this action."
}
The response mapping template is the same is in the documentation:
ResponseMappingTemplate: |
#if($ctx.error)
$util.error($ctx.error.message, $ctx.error.type)
#end
$util.toJson($context.result)
I was trying to change the $ctx.error.type to $ctx.error.errorType, but then the errorType is returned as "Custom lambda error" and not "Forbidden".
I have tried using both the context.fail() and callback methods from the lambda exports.handler, both work nicely in the console but both only return the message when called from the appsync resolver.
I have tested that the context.fail(error) is indeed called in the lambda and that the exception is caught via .catch() statement before finally calling the context.fail(error)
Even cloudwatch displays the error with the message and errorType when the main lambda is called, so I'm suspecting an error in exepction and context.fail() returning
In the end, I stringified the error and passed it to my clients via the message column. Unfortunately, when I managed to pass the proper errorType via the resolver response mapping template, the returned error started returning "Custom Error" as the return errorType.
I am working with AppSync and it seems that validation errors don't trigger the error catch in the response mapping template.
The attribute values contain an AWSPhone input.
If I input a wrong format for AWSPhone, the error (as expected) is:
{
"data": null,
"errors": [
{
"path": null,
"locations": [
{
"line": 2,
"column": 17,
"sourceName": null
}
],
"message": "Validation error of type WrongType: argument 'input.company.phoneNumber' with value 'StringValue{value='+1-541-754-300'}' is not a valid 'AWSPhone' # 'createProfile'"
}
]
}
My request mapping template is like so:
{
"version": "2018-05-29",
"operation": "PutItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.input.client),
},
"attributeValues": $util.dynamodb.toMapValuesJson($ctx.args.input),
}
My response mapping template:
#if($ctx.error)
$util.error("Custom error response")
#end
$util.toJson($ctx.result)
It's clear that an error does indeed happen but it doesn't trigger the case in my response template.
How do I go about returning a custom message for a validation error?
Is it even possible in this scenario?
Based on what you provided, it looks like your PutItem operation did succeed and returned the item that you put on the $ctx.result field. Then, when AppSync tried to coerce one of the fields on the output of your response mapping template into a AWSPhone it failed with the validation error you mentioned.
The #if($ctx.error) $util.error("Custom error response") will catch only DynamoDB errors. The GraphQL coercion from your result to the field output type happens after the template has evaluated.
One way to catch this would be to add validation into your request mapping template before saving it in DynamoDB or changing the value coming back from DynamoDB into the proper AWSPhone scalar format.
The issue
Using the Integration Response feature of AWS API Gateway, I can successfully handle errors thrown by Lambda. However, I'm having trouble mapping the Javascript error object to the mapping template. The error object looks like this.
{
"errorMessage": "Error: ABC123",
"errorType": "Error",
"stackTrace": [
"exports.handler (/var/task/index.js:9:11)"
]
}
Here's my Integration Response mapping for application/json
#set($errorMessage = $input.path('$.errorMessage'))
{
"message" : $errorMessage
}
What I've tried
Using this configuration, this is the entire response returned to the client: Unexpected 'E'. This is one string and is not contained in a JSON object.
This E that's referenced in the error is the first character from my thrown error message, which is used to match the Lambda Error Regex. I know this as I briefly changed the first letter to X and I got Unexpected 'X' as the response.
When I change the first line of my mapping template to this (mapping the entire object rather than attempting to just map the errorMessage property)
#set($errorMessage = $input.path('$'))
I get only the stack trace from the Javascript error object.
{
"message" : [
"exports.handler (/var/task/index.js:9:11)"
]
}
This would suggest that either the entire response object being returned to API Gateway is just the stackTrace property from the Javascript error. But to me, this doesn't make sense as something is picking up the errorMessage as thats where the Unexpected 'E' message is coming from.
Similarly, when I try and map the errorType property, I get the same error as it also starts with E. I can only successfully map the message property when I use the entire $ input object, or just the stackTrace property.
What am I doing wrong here?
Other relevant code
Here's the API Gateway Error Model. The message property is clearly marked as a string type yet it only works when I return an array. Note: this is the default code
{
"$schema" : "http://json-schema.org/draft-04/schema#",
"title" : "Error Schema",
"type" : "object",
"properties" : {
"message" : { "type" : "string" }
}
}
Here's the Lambda function code
exports.handler = async (event, context, callback) => {
throw new Error(Error: ABC123);
};
I've figured it out. The solution is to use $input.json() rather than $input.path()
Here's my new mapping template
{
"errorMessage" : $input.json('$.errorMessage')
}
I am using batch http requests to add multiple aliases to Google groups. While the first alias for each group is added correctly (response 200), subsequent aliases in the same batch to the same group fail with error 412.
Here is my code:
def create_group_aliases(service, aliases_by_group_id):
alias_return = service.new_batch_http_request()
for id in aliases_by_group_id:
alias_list = aliases_by_group_id[id]
for alias in alias_list:
alias_return.add(service.groups().aliases().insert(groupKey=id, body={'alias': alias}))
alias_return.execute()
return alias_return
If I run this:
create_group_aliases(service, {'aaatestprime#foo.bar':['alphatest#foo.bar', 'betatest#foo.bar'], 'bbbtestprime#foo.bar':['gammatest#foo.bar', 'deltatest#foo.bar']})
My batch response gives me 200 OK responses for alphatest#foo.bar and gammatest#foo.bar but I get the below error for betatest#gfoo.bar and deltatest#foo.bar
{
"error": {
"errors": [
{
"domain": "global",
"reason": "conditionNotMet",
"message": "Concurrent email update.",
"locationType": "header",
"location": "If-Match"
}
],
"code": 412,
"message": "Concurrent email update."
}
}
I get the same behavior when trying to add multiple aliases to a user.
def create_aliases(service, aliases_by_user_id):
alias_return = service.new_batch_http_request()
for id in aliases_by_user_id:
alias_list = aliases_by_user_id[id]
for alias in alias_list:
alias_return.add(service.users().aliases().insert(userKey=id, body={'alias':alias}))
alias_return.execute()
return alias_return
Is this a bug or a feature? I can obviously add each alias as a separate job but I would prefer to use batches as they're so much easier to work with and I have several hundred aliases I need to add.
I'm attempting to update an existing sink using the (Python) Google Stackdriver Logging API, and am getting an error that I can't track down.
The API explorer for the method in question is here, to which I'm supplying projects/my_project/sinks/my_sink_name as the sinkName, and the following for the Request Body:
{
"name": "audit_logs",
"destination": "bigquery.googleapis.com/projects/my_project/datasets/destination_dataset",
"filter": "resource.type=\"bigquery_resource\""
}
When submitting, I get the following error:
400 Bad Request
{
"error": {
"code": 400,
"message": "Request contains an invalid argument.",
"status": "INVALID_ARGUMENT"
}
}
...which doesn't specify which argument is invalid, and I have tried several variations without any success.
Additional info: this request is based on one generated by the Python API. I have also tried specifying the full path of the sink in name to no avail, which is what the Python API generates, which seems contrary to the documentation.
Can you try the UpdateSink with uniqueWriterIdentity set to true?
From https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks/update:
"It is an error if the old value is true and the new value is set to false or defaulted to false."