Custom exception message from google endpoints exception - python

Currently, I've a Python endpoints service to change the name of a user. If there is no problem, I return a MessageField with a lot of informations.
But sometimes, the request is correct and I want to say to client that there is an error that he can handle : "Hey, sorry but there is already a user with this name", or also, "Hey, sorry but you have already change your name today !".
The problem is, when I raise an endpoint exception like a UnauthorizedException or anything else, I can just put a custom message :
raise endpoints.UnauthorizedException('Invalid user_id or auth_token !')
result in :
{
"error": {
"errors": [
{
"domain": "global",
"reason": "required",
"message": "Invalid user_id or auth_token !",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Invalid user_id or auth_token !"
}
}
Is there a way to really customize this message ? Or to return a completely different MessageField in this case ?
For example, I would like to return a JSON like this (maybe with HTTP Code 400) :
{
"error": {
"username_already_exist": 1
}
}
Thanks !

If you don't want to return the JSON that is build with an exception, you'll have to do a straight up return in your code instead of raising an exception. You can build the JSON you mention and then return that whenever you hit the "exception point".
Or what you can do is use the message to send the error to the client, then have the client catch that exception, parse appropriately, and display whatever you want.

Related

How do you set fulfillment state to 'ReadyForFulfillment' using AWS Lambda & Lex?

I keep getting lambda response errors when trying to set the event response object, which contains multiple slots for fulfillment state, which are currently set to none. I want to set them as 'ReadyForFulfillment', which apparently is a dialogAction option. I've been trying now for over 15 hours on this one issue and I can't find a solution.
In my cloudwatch logs, there is no errors appearing which makes debugging a pain.
I have a slot with the name 'policy', which I am validating in my code, and based on the validation, it sends one of two responses to lex. A dialogAction asking for a new policy format (works), and a dialogAction that will proceed if it is correctly validated and change the state to 'ReadyForFulfillment'
Here is my current Lambda:
import json
import logging
import re
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
def dispatch(event):
slots = event["currentIntent"]["slots"]
policy = slots["policy"]
if re.match(r"policy\.([\w]+\.)+[\w]+(?=[\s]|$)", policy):
print("Valid input.")
retStr = "valid"
savePolicy = policy
else:
print("Invalid input.")
retStr = "invalid"
if retStr == "invalid":
return {
"dialogAction": {
"type": "ElicitSlot",
"message": {
"contentType": "PlainText",
"content": "Invalid policy format entered. Please try another format now:"
},
"intentName": "policyprovider",
"slots": {
"policy": policy
},
"slotToElicit": "policy"
}
}
else:
return {
"dialogAction": {
"type": "Close",
"fulfillmentState": "ReadyForFulfillment",
}
}
def lambda_handler(event, context):
logger.debug('event={}'.format(event))
response = dispatch(event)
logger.debug(response)
logger.debug('eventAfter={}'.format(event))
try:
return response
except Exception as e:
logger.debug("Exception: {}".format(e))
This results in on Lex chat bot, something similar to this:
Lex: What is your policy name?
User Input: test
Lex: Invalid policy format entered. Please try another format now:
User Input: policy.foobar.foobar
Lex: An error has occurred: The server encountered an error processing the Lambda response
On Cloudwatch, there is no sign of this error or further details what is causing it. The lex intent never changes to 'ReadyForFulfillment' either.
This is the event response on Lex:
RequestID: blah-blah-blah
{
"activeContexts": [],
"alternativeIntents": [
{
"intentName": "AMAZON.FallbackIntent",
"nluIntentConfidence": null,
"slots": {}
},
{
"intentName": "alternativePolicy",
"nluIntentConfidence": {
"score": 0.31
},
"slots": {
"TestItem": null
}
}
],
"botVersion": "$LATEST",
"dialogState": "ElicitSlot",
"intentName": "policyprovider",
"message": "Invalid policy format entered. Please try another format now:",
"messageFormat": "PlainText",
"nluIntentConfidence": {
"score": 1
},
"responseCard": null,
"sentimentResponse": null,
"sessionAttributes": {},
"sessionId": "2022-05-04T11:22:40-blah-blah",
"slotToElicit": "policy",
"slots": {
"policy": "test"
I am working on the assumption that you are using AWS Lex V1. If so, your error is likely because the response message you're returning from your Lambda function in the case of valid user input contains an invalid value.
As per the developer guide for Lex V1, the acceptable response values for fulfillmentState are Fulfilled and Failed.
This will effectively tell Lex that the interaction has been completed successfully. Assuming you want to perform some custom processing in the case of valid input, include that operation in your existing Lambda function so that by the time you reply back to the user, the fulfillment has indeed been performed.

python yt api "The request is missing a valid API key."

im using the youtube api in python, i using this code :
r = requests.get('https://www.googleapis.com/youtube/v3/videos')
print(r.text)
and i get this output :
{
"error": {
"code": 403,
"message": "The request is missing a valid API key.",
"errors": [
{
"message": "The request is missing a valid API key.",
"domain": "global",
"reason": "forbidden"
}
],
"status": "PERMISSION_DENIED"
}
}
Process finished with exit code 0
i already have my api key so my question is how do i add this api key to the GET request and make this work
The API key should be added as a GET parameter called key, like so:
r = requests.get('https://www.googleapis.com/youtube/v3/videos?key=YOUR_API_KEY&id=7lCDEYXw3mM&...')
print(r.text)
The documentation has a few more examples.

Python Firebase authentication

So I am trying to obtain some data from Firebase, which ofcourse has some rules/security constraints defined. I can authenticate my user from the Login & Auth tab, and I'd like to get data from firebase, however my user is still not authenticated.
user = ref.authenticate(email, password) , which returns the following for user
{
u'token':{some long token here}',
u'user':{
u'uid':u'ef44b781-8842-4f28-abf0-2ac9aa0b2bea',
u'provider':u'password',
u'email':u'user#email.com',
u'isTemporaryPassword':False,
u'sessionKey':u'{session key here}}',
u'md5_hash':u'{md5_hash here}}',
u'id':u'ef44b781-8842-4f28-abf0-2ac9aa0b2bea'
}
}
Now that I know the user is authenticated (otherwise it returns something along the lines of an error, I would like to do a simple GET conversations = firebase.get(FIREBASE_NAME + '/conversations/' + me), where 'me' is the user['user']['uid']
I have the following structure for conversations:
conversations/my-uid/other-uid/{data}
I would think my user is authenticated, still it returns a PermissionDenied
EDIT
Solved this by using a different library you can find here. The initial library I used did not support authentication, while this one does. The way it was solved, was by implementing some functions from the other one and sending my token as follow:
FIREBASE.child('/messages/').get(token=token)
You should not send passwords in the URL, you can do this way:
__FIREBASE_USER_VERIFY_SERVICE = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword"
__FIREBASE_API_KEY = __your_api_key_here__
def user_login(email, passwd):
url = "%s?key=%s" % (__FIREBASE_USER_VERIFY_SERVICE, __FIREBASE_API_KEY)
data = {"email": email,
"password": passwd,
"returnSecureToken": True}
result = requests.post(url, json=data)
is_login_successful = result.ok
json_result = result.json()
return json_result # authToken=> json_result['idToken']
If successful, it will result like:
{
"displayName": "",
"email": "your_users_email#example.com",
"expiresIn": "3600",
"idToken": "abc123...",
"kind": "identitytoolkit#VerifyPasswordResponse",
"localId": "UWQ...x2",
"refreshToken": "def098...",
"registered": true
}
If fails (wrong password etc) :
{
"error": {
"code": 400,
"errors": [
{
"domain": "global",
"message": "INVALID_PASSWORD",
"reason": "invalid"
}
],
"message": "INVALID_PASSWORD"
}
}
or may be
{
"error": {
"code": 400,
"errors": [
{
"domain": "global",
"message": "MISSING_PASSWORD",
"reason": "invalid"
}
],
"message": "MISSING_PASSWORD"
}
}
Solved this by using a different library you can find here. The initial library I used did not support authentication, while this one does. The way it was solved, was by implementing some functions from the other one and authenticate as follows:
def auth_with_password(self, email, password):
request_ref = 'https://auth.firebase.com/auth/firebase?firebase={0}&email={1}&password={2}'.\
format(self.fire_base_name, email, password)
request_object = self.requests.get(request_ref)
return request_object.json()
Then to make an authorized call, do this
user = auth_with_password(email, password)
token = user['user']['token']
FIREBASE.child('/messages/').get(token=token)
Make sure your token is correct. The library supports this, but otherwise I would suggest that you use Firebase token generator for Python

Python flask and custom client error messages

I'm currently writing a REST API for an app I'm working on. The app is written in python using flask. I have the following:
try:
_profile = profile(
name=request.json['name'],
password=profile.get_salted_password('blablabla'),
email=request.json['email'],
created_by=1,
last_updated_by=1
)
except AssertionError:
abort(400)
session = DatabaseEngine.getSession()
session.add(_profile)
try:
session.commit()
except IntegrityError:
abort(400)
The error handler looks like this:
#app.errorhandler(400)
def not_found(error):
return make_response(standard_response(None, 400, 'Bad request'), 400)
I'm using the error 400 to denote both a problem with a sqlalchemy model validator and a unique constraint when writing to the database and in both cases the following error is sent to the client:
{
"data": null,
"error": {
"msg": "Bad request",
"no": 400
},
"success": false
}
Is there a way to still use abort(400) but also set the error somehow so that the error handler can take care of adding additional information for the error object in the result?
I would like it to be more in line with:
{
"data": null,
"error": {
"msg": "(IntegrityError) duplicate key value violates unique constraint profile_email_key",
"no": 400
},
"success": false
}
you can directly put a custom response in abort() function:
abort(make_response("Integrity Error", 400))
Alternatively, you can put it in the error handler function
#app.errorhandler(400)
def not_found(error):
resp = make_response("Integrity Error", 400)
return resp
errorhandler can take an exception type as well:
#app.errorhandler(AssertionError)
def handle_sqlalchemy_assertion_error(err):
return make_response(standard_response(None, 400, err.message), 400)
i know am late to the game, but for anyone who wants another solution, mine is based on the answer by #codegeek.
i was able to accomplish something similar with the following in my ServerResponse.py module:
def duplicate(message=""):
response = make_response()
response.status_code = 409
response.headers = {
"X-Status-Reason" : message or "Duplicate entry"
}
abort(response)
then i can call
ServerResponse.duplicate('Duplicate submission. An article with a similar title already exists.')
this makes it easy in my AngularJS app to check for a response status and display the X-Status-Reason default or customized message

Trying to delete a post using Blogger API returns "not found" error

Sending a DELETE request to Blogger REST API (v3.0), I'm trying to delete a post using delete method. For this I use the following code:
api_uri = 'https://www.googleapis.com/blogger/v3/blogs/%s/posts/%s' % (blogId, postId)
result = urlfetch.fetch(url=api_uri,
method=urlfetch.DELETE,
headers={'Authorization' : oauth_token})
self.response.out.write(result.content)
But the server returns:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "notFound",
"message": "Not Found"
}
],
"code": 404,
"message": "Not Found"
}
}
However, I can retrieve information about this post, using the following code:
api_uri = 'https://www.googleapis.com/blogger/v3/blogs/%s/posts/%s' % (blogId, postId)
result = urlfetch.fetch(url=api_uri,
headers={'Authorization' : oauth_token})
self.response.out.write(result.content)
At this moment, I can't understand what am I doing wrong — the request is authorized, the blogId and postId are correct — but anyway, the server returns "not found" error.
If you know how to solve this problem or you can give useful advice — help me please. Thank you for your time and consideration of this matter.
UPD 1: If I send requests to the following URLs:
# https://www.googleapis.com/blogger/v3/users/{userID}
# https://www.googleapis.com/blogger/v3/users/self
The server also returns:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "notFound",
"message": "Not Found"
}
],
"code": 404,
"message": "Not Found"
}
}
UPD 2: I forgot to say that I'm using OAuth 2.0 for Server to Server Applications. Thus, to get authorization token, I send request to https://accounts.google.com/o/oauth2/token using the following JWT Claim Set:
jwt_claim_set = {
'iss' : '{id}#developer.gserviceaccount.com',
'scope' : 'https://www.googleapis.com/auth/blogger',
'aud' : 'https://accounts.google.com/o/oauth2/token',
'exp' : expire,
'iat' : timestamp
}
The server returns:
{
"access_token" : "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
"token_type" : "Bearer",
"expires_in" : 3600
}
And define variable oauth_token, using:
data = simplejson.loads(result.content)
oauth_token = data['token_type'] + ' ' + data['access_token']
Are you sure that you're using OAuth2 properly? It seems to me that you're not properly logged in, and that's why you're getting those errors.
Try those same queries using Google OAuh2 Playground (https://code.google.com/oauthplayground/) and see what happens.

Categories