How to retrieve ndb.KeyProperty when repeated=True using endpoints-proto-datastore

642 views Asked by At

I want to have my GAE backend API return a list of Contacts with the corresponding collection of Emails for each Contact. I am using the endpoints-proto-datastore and implemented this following the guidelines in this question.

My problem is that when the contacts_list method is called, I am getting:

Encountered unexpected error from ProtoRPC method implementation: BadValueError (Expected Key, got [])

I guess this happens because Contact.email_keys might be empty ([]) but don't know where to control this behaviour.

Here is the relevant part of my API implementation:

class Email(EndpointsModel):
    type = ndb.StringProperty(choices=('home', 'work', 'other'))
    email = ndb.StringProperty()
    #.... other properties

class Contact(EndpointsModel):
    first_name = ndb.StringProperty()
    last_name = ndb.StringProperty()
    email_keys = ndb.KeyProperty(kind=Email, repeated=True)
    #.... other properties

    @EndpointsAliasProperty(repeated=True, property_type=Email.ProtoModel())
    def emails(self):
        return ndb.get_multi(self.email_keys)

    _message_fields_schema = ('id', 'first_name', 'last_name')


@endpoints.api(name='cd', version='v1')
class MyApi(remote.Service):
    @Contact.query_method(query_fields=('limit', 'order', 'pageToken'),
                          collection_fields=('id', 'first_name', 'last_name', 'emails'),
                          path='contacts', name='contacts.list')
    def contacts_list(self, query):
        return query
1

There are 1 answers

2
Cato On BEST ANSWER

Well I found the solution myself; here it is.

Turns out that when a model inherits from EndpointsModel, has a property with repeated=True and you create an endpoints-proto-datastore query_method to retrieve the list of your model, there is a call in the stack to the _PopulateFilters method in module model.py of the library; here's the relevant stack trace:

File "lib/endpoints_proto_datastore/ndb/model.py", line 229, in _PopulateFilters self._AddFilter(prop == current_value)

Lines around 229 in model.py read as follows:

  # Only filter for non-null values
  if current_value is not None:
    self._AddFilter(prop == current_value)

If the model property in question has repeated=True, then current_value will be [] and that is where my code was breaking. I changed the relevant part in model.py as follows and that fixed the issue.

  # Only filter for non-null values...
  if current_value is not None:
      # ...and non empty arrays in case of properties with repeated=True 
      if current_value:
          self._AddFilter(prop == current_value)

Will create a comment on github to see if this gets included in the next release.