1

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.

Community
  • 1
  • 1
Tiki
  • 105
  • 2
  • 7
  • How are you sending this request? Using the same code I'm getting the expected `NotFoundException`. – bossylobster May 20 '13 at 15:38
  • @bossylobster, thanks for responding. I'm sending the request from the API Explorer. I had inserted (using the insert method for the API) an entry into the datastore with the title 'Cookies' - I can see the entry in the datastore which means that it was inserted successfully. The error comes up when I try and use the above method and entering 'Cookies' into the title field on the API Explorer. – Tiki May 21 '13 at 05:10
  • Are you missing the below part for your Recipe(EndpointsModel) : _message_fields_schema = ('id', 'title', 'description', 'ingredients', 'instructions') – tony m May 21 '13 at 12:14
  • @tony that won't matter since the `get_recipe` method is using `title`. However, as you noted, since no `id` property is set, the `UpdateFromKey` method would never be called and `from_datastore` would never have a chance to be set. – bossylobster May 21 '13 at 14:58
  • @senitiki What is the payload that gets sent/printed in the APIs explorer? – bossylobster May 21 '13 at 15:18
  • @bossylobster - Request on API Explorer is: GET http://localhost:10086/_ah/api/recipes/v1/recipe/Cookie X-JavaScript-User-Agent: Google APIs Explorer – Tiki May 22 '13 at 07:37
  • @bossylobster - so I decided to start over and thus I cleared my entire datastore, made an insert (confirmed it was successful) and now the response is 404 Not Found - Show headers - {"error_message": "Recipe not found.","state": "APPLICATION_ERROR"} – Tiki May 22 '13 at 07:46
  • @tony - even after adding that, I still get the 404 Not Found error I've stated above. – Tiki May 22 '13 at 07:51

1 Answers1

1

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.

bossylobster
  • 9,993
  • 1
  • 42
  • 61
  • thanks for the explanation. As I understand - please correct me if I'm wrong - for this to work, I will need to make the simple data property into an EndpointsAliasProperty? If yes, how would I go about doing that? Or is it that we can only use 'id' (helper methods provided by EndpointsModel) and we cannot use any of the properties that we define (in this case 'title')? – Tiki May 23 '13 at 11:53
  • @senitiki Can you ask this as another SO question? I think it's perfectly valid for a new discussion. – bossylobster May 23 '13 at 16:05
  • @senitiki Where's that other question? :) – bossylobster May 24 '13 at 05:36
  • I've placed it here, http://stackoverflow.com/questions/16755372/cloud-endpoints-retrieving-a-single-entity-from-datastore-by-a-property-other – Tiki May 26 '13 at 01:38