Question

I've been playing around with Google Cloud Endpoints for the past few days (aiming to hook it up with AngularJS) and I ran into a bit of trouble when I try to retrieve a single entity from my datastore.

My ndb model setup is:

class Ingredients(EndpointsModel):
    ingredient = ndb.StringProperty()

class Recipe(EndpointsModel):
    title = ndb.StringProperty(required=True)
    description = ndb.StringProperty(required=True)
    ingredients = ndb.StructuredProperty(Ingredients, repeated=True)
    instructions = ndb.StringProperty(required=True)

Here is the API method I've defined to retrieve the entity by 'title':

    @Recipe.method(request_fields=('title',), path='recipe/{title}',
                   http_method='GET', name='recipe.get')
    def get_recipe(self, recipe):
        if not recipe.from_datastore:
            raise endpoints.NotFoundException('Recipe not found.')
        return recipe   

The API method works fine if I were to use 'id' (helper methods provided by EndpointsModel) in place of 'title' for the request fields. When I use 'title', however, I'm getting

404 Not Found

{"error_message": "Recipe not found.","state": "APPLICATION_ERROR"}

Can anyone point out if I am missing something somewhere?

NOTE: See comments. The error in the question used to read

400 Bad Request

{"error_message": "Error parsing ProtoRPC request (Unable to parse request content: Message RecipeProto_title is missing required field title)", "state": "REQUEST_ERROR"}

but @sentiki was able to resolve this previous error.

Was it helpful?

Solution

The 404 is expected. The "magic" of the id property is that it calls UpdateFromKey.

This method tries to set an ndb.Key on the entity based on the request and then attempts to retrieve the entity stored with that key. If the entity exists, the values from the datastore are copied over to the entity parsed from the request and then the _from_datastore property is set to True.

By using request_fields=('title',), you have a simple data property rather than an EndpointsAliasProperty and so only the values are set. As a result, _from_datastore never gets set and your check

    if not recipe.from_datastore:
        raise endpoints.NotFoundException('Recipe not found.')

throws an endpoints.NotFoundException as expected.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top