Send HTTP 204 response with Google Cloud Endpoints - google-app-engine

I'm creating an API using Google Cloud Endpoints where I would like to return a "no content" HTTP 204 response if there's nothing to return. I tried returning null, which throws an error on the development server, and a non-empty result on production with status code 200.
It is possible to send out a true 204 empty response or other types or custom responses?

To return a 204 No Content for a production Python Cloud Endpoints API, you can use VoidMessage.
from google.appengine.ext import endpoints
from protorpc import messages
from protorpc import message_types
from protorpc import remote
class MyMessage(messages.Message):
...
#endpoints.api('someapi', 'v1', 'Description')
class MyApi(remote.Service):
#endpoints.method(MyMessage, message_types.VoidMessage,
...)
def my_method(self, request):
...
return message_types.VoidMessage()
This currently returns a 200 on the development server, thanks for finding this bug!

This probably doesn't help, but the only way I know to manipulate the status code is by raising an exception. There are a default set of exceptions provided which map to 400, 401, 403, 404 and 500. The docs say you can subclass endpoints.ServiceException to generate other status codes, however I haven't been able to get this to work. If you set http_status to anything other than one of those listed above, it always results in a 400.
class TestException(endpoints.ServiceException):
http_status = httplib.NO_CONTENT
I run a test in my handler like this:
raise TestException('The status should be 204')
And I see this output when testing it using the API explorer:
400 Bad Request
- Show headers -
{
"error": {
"errors": [
{
"domain": "global",
"reason": "badRequest",
"message": "The status should be 204"
}
],
"code": 400,
"message": "The status should be 204"
}
}

Related

Positive Scenario Usage of StatusReport google home trait

In Google documentation, they explained how to use "statusReport" trait for query intent when there is an error or exception occurred for a device. I'm facing issue while using it for success status without any exception. I tried sending the response with simple status as SUCCESS, Google Home is saying the response "Sorry Unable to reach device".
The response which I was sending:
{ "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf", "payload": { "devices": { "123": { "online": true, "status": "SUCCESS" } } } }
can anyone help me to solve this issue?
After looking at the code, it is clear that you’re not sending the correct & complete response of the StatusReport trait. Your response contains the online attribute but missing the currentStatusReport attribute (it is required as it defines the current error statuses of the associated device IDs). For more information, visit https://developers.google.com/assistant/smarthome/traits/statusreport?hl=en

Files GraphAPI do return 403 until the file is loaded by the user

As User#1, we're using this API to create a shared link:
https://graph.microsoft.com/v1.0/drives/{{driveId}}/items/{{itemId}}/createLink
This is successful and returns a ShareURI.
As User#2, we're using this API to get information about the item shared by User#1.
https://graph.microsoft.com/v1.0/shares/{{base64ShareURI}}/driveItem
However, /driveItem returns a status code of 403 with the following body:
HTTP 403
{
"error": {
"code": "accessDenied",
"message": "The sharing link no longer exists, or you do not have
permission to access it.",
"innerError": {
"request-id": "73e65e0a-54b8-4722-9726-82297076276e",
"date": "2018-11-07T16:20:03"
}
}
}
To prevent this 403 from happening, User#2 needs to load the ShareURI in a web browser. Once User#2 does this, then the request to the exact same URI
https://graph.microsoft.com/v1.0/shares/{{base64ShareURI}}/driveItem
return 200OK and the expected json body.
Why does User#2 have to load the ShareURI in a browser before being able to use Graph APIs on that ShareURI? Is there a workaround?
To open it directly without visiting link just add header Prefer with value redeemSharingLink to request https://graph.microsoft.com/v1.0/shares/{{base64ShareURI}}/driveItem.
Docs:
redeemSharingLink should be considered equivalent to the caller navigating to the sharing link the browser (accepting the sharing gesture)
Reference: https://learn.microsoft.com/en-us/graph/api/shares-get?view=graph-rest-1.0&tabs=javascript

Leasing app engine task in compute engine

I'm trying to lease an app engine task from a pull queue in a compute engine instance but it keeps giving this error:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "you are not allowed to make this api call"
}
],
"code": 403,
"message": "you are not allowed to make this api call"
}
}
This is the code I'm using:
import httplib2, json, urllib
from oauth2client.client import AccessTokenCredentials
from apiclient.discovery import build
def FetchToken():
METADATA_SERVER = ('http://metadata/computeMetadata/v1/instance/service-accounts')
SERVICE_ACCOUNT = 'default'
http = httplib2.Http()
token_uri = '%s/%s/token' % (METADATA_SERVER, SERVICE_ACCOUNT)
resp, content = http.request(token_uri, method='GET',
body=None,
headers={'Metadata-Flavor': 'Google'})
print token_uri
print content
if resp.status == 200:
d = json.loads(content)
access_token = d['access_token'] # Save the access token
credentials = AccessTokenCredentials(d['access_token'],
'my-user-agent/1.0')
autho = credentials.authorize(http)
print autho
return autho
else:
print resp.status
task_api = build('taskqueue', 'v1beta2')
lease_req = task_api.tasks().lease(project='project-name',
taskqueue='pull-queue',
leaseSecs=30,
numTasks=1)
result = lease_req.execute(http=FetchToken()) ####ERRORS HERE
item = result.items[0]
print item['payload']
It seems like an authentication issue but it gives me the exact same error if I do the same lease request using a bullshit made-up project name so I can't be sure.
I also launched the instance with taskqueue enabled.
Any help would be greatly appreciated
In case anyone else is stuck on a problem like this I'll explain how it's working now.
Firstly I'm using a different (shorter) method of authentication:
from oauth2client import gce
credentials = gce.AppAssertionCredentials('')
http = httplib2.Http()
http=credentials.authorize(http)
credentials.refresh(http)
service = build('taskqueue', 'v1beta2', http=http)
Secondly, the reason my lease request was being denied is that in queue.yaml my service account email was set as a writer email. In the documentation it's mentioned that an email ending with #gmail.com will not have the rights of a user email when set as a writer email. It's not mentioned that that extends to emails ending with #developer.gserviceaccount.com.

Better error message when missing required fields from request

When missing a required field currently ProtoRPC returns a message like this:
{
"error": {
"code": 400,
"errors": [
{
"domain": "global",
"message": "Error parsing ProtoRPC request (Unable to parse request content: Message CombinedContainer is missing required field api_key)",
"reason": "badRequest"
}
],
"message": "Error parsing ProtoRPC request (Unable to parse request content: Message CombinedContainer is missing required field api_key)"
}
}
Is it possible to provide a better error message? Ideally, "missing required field api_key" in this example.
According to the Google Code Issue Tracker and Github issues this was once being worked on. However, there does not appear to be any activity.
I'd greatly appreciate any solutions or workarounds.
As of today, the ProtoRPC still returns the same unraised error which makes it harder for one to return a customized error response.
A simple workaround is to make the Message fields optional and enforce the requirement constraint somewhere in the endpoint handler/method.
So instead of;
class Request(Message):
name = StringField(1, required=True)
Set 'name' as optional,
class Request(Message):
name = StringField(1)
The requirement constraint can then be enforced in the endpoint handler method by simple if statements OR by mapping these fields to a Datastore entity that requires these fields, hence will allow exception handling of BadValueError and return a more customized error response.
Example:
try:
account = Account(name=request.name)
account.put()
except BadValueError:
return Response(status=False, message='Missing field "name"')

endpoints.ServiceException subclass returning 400 status code instead of 409

In the Cloud Endpoints documentation for exception handling, it is recommended to subclass the endpoints.ServiceException class to provide a custom http_status for 409 Conflict errors. This answer to another question indicates that some of the supported status codes get mapped by Google's infrastructure to other status codes, but 409 isn't one of the mapped status codes.
Using the ConflictException class from the documentation:
import endpoints
import httplib
class ConflictException(endpoints.ServiceException):
"""Conflict exception that is mapped to a 409 response."""
http_status = httplib.CONFLICT
When I raise the ConflictException:
#endpoints.method(request_message=apimodels.ClientMessage,
response_message=apimodels.ClientMessage,
name='insert',
path='/clients',
http_method='POST'
)
def insert(self, request):
client = models.Client.get_by_id(request.client_code)
if client:
raise ConflictException('Entity with the id "%s" exists.' % request.client_code)
...
I'm getting a 400 Bad Request as the response:
400 Bad Request
Content-Length: 220
Content-Type: application/json
Date: Thu, 27 Feb 2014 16:11:36 GMT
Server: Development/2.0
{
"error": {
"code": 400,
"errors": [
{
"domain": "global",
"message": "Entity with the id \"FOO\" exists.",
"reason": "badRequest"
}
],
"message": "Entity with the id \"FOO\" exists."
}
}
I'm getting the same 400 response code on both the local dev_appserver and deployed to App Engine (on 1.9.0). Stepping into the App Engine ProtoRPC code, the following line appears to be mapping all remote.ApplicationError types to a 400 status code.
If I update the endpoints.apiserving._ERROR_NAME_MAP dict to add my custom ConflictException class, I'm able to return a 409 successfully:
import endpoints
import httplib
from endpoints.apiserving import _ERROR_NAME_MAP
class ConflictException(endpoints.ServiceException):
"""Conflict exception that is mapped to a 409 response."""
http_status = httplib.CONFLICT
_ERROR_NAME_MAP[httplib.responses[ConflictException.http_status]] = ConflictException
Is this the correct way to implement endpoints.ServiceException subclasses?
It seems to be a bug as according to the bug report filed by Chris.

Resources