| # If you have not yet seen the source in basic_with_auth/main.py and |
| # paging/main.py, please take a look. |
| |
| # In this sample we use a custom Enum for the "order" property in queries |
| # to strictly control the indexes used and make sure we have corresponding |
| # indexes created in index.yaml. |
| |
| import endpoints |
| |
| from google.appengine.ext import ndb |
| # This import allows us to define our own Enum using the ProtoRPC messages |
| # library. This is not usually needed, since EndpointsModel handles message |
| # definition, but in this case it is. |
| from protorpc import messages |
| from protorpc import remote |
| |
| # We import EndpointsAliasProperty so that we can define our own helper property |
| # similar to the properties "id", "entityKey", "limit", "order" and "pageToken" |
| # provided by EndpointsModel. |
| from endpoints_proto_datastore.ndb import EndpointsAliasProperty |
| from endpoints_proto_datastore.ndb import EndpointsModel |
| |
| |
| # This is an Enum used to strictly define which order values are allowed. |
| # In this case, we are only allowing two query orders and have an enum value |
| # corresponding to each. |
| class Order(messages.Enum): |
| MYFIRST = 1 |
| MYSECOND = 2 |
| |
| |
| class MyModel(EndpointsModel): |
| # As in simple_get/main.py, by setting _message_fields_schema, we can set a |
| # custom ProtoRPC message schema. We set the schema to the four properties |
| # corresponding to the NDB properties and exclude the fifth property, which is |
| # the alias property "order". Though the helper property "order" from |
| # EndpointsModel is not included in the message schema, since we define our |
| # own "order", this would be included if we did not define our own schema. |
| _message_fields_schema = ('attr1', 'attr2', 'owner', 'created') |
| |
| # The properties attr1 and attr2 are required here so that all entities will |
| # have values for performing queries. |
| attr1 = ndb.StringProperty(required=True) |
| attr2 = ndb.StringProperty(required=True) |
| created = ndb.DateTimeProperty(auto_now_add=True) |
| # As in basic_with_auth/main.py, an owner property is used and each entity |
| # created will have the current user saved as the owner. As with attr1 and |
| # attr2 above, we are also requiring the owner field so we can use it for |
| # queries too. |
| owner = ndb.UserProperty(required=True) |
| |
| # This is a setter which will be used by the helper property "order", which we |
| # are overriding here. The setter used for that helper property is also named |
| # OrderSet. This method will be called when order is set from a ProtoRPC |
| # query request. |
| def OrderSet(self, value): |
| # Since we wish to control which queries are made, we only accept values |
| # from our custom Enum type Order. |
| if not isinstance(value, Order): |
| raise TypeError('Expected an enum, received: %s.' % (value,)) |
| |
| # For MYFIRST, we order by attr1. |
| if value == Order.MYFIRST: |
| # Use the method OrderSet from the parent class to set the string value |
| # based on the enum. |
| super(MyModel, self).OrderSet('attr1') |
| # For MYSECOND, we order by attr2, but in descending order. |
| elif value == Order.MYSECOND: |
| # Use the method OrderSet from the parent class to set the string value |
| # based on the enum. |
| super(MyModel, self).OrderSet('-attr2') |
| # For either case, the order used here will be combined with an equality |
| # filter based on the current user, and we have the corresponding indexes |
| # specified in index.yaml so no index errors are experienced by our users. |
| |
| # If the value is not a valid Enum value, raise a TypeError. This should |
| # never occur since value is known to be an instance of Order. |
| else: |
| raise TypeError('Unexpected value of Order: %s.' % (value,)) |
| |
| # This EndpointsAliasProperty is our own helper property and overrides the |
| # original "order". We specify the setter as the function OrderSet which we |
| # just defined. The property_type is the class Order and the default value of |
| # the alias property is MYFIRST. |
| |
| # Endpoints alias properties must have a corresponding property type, which |
| # can be either a ProtoRPC field or a ProtoRPC message class or enum class. |
| # Here, by providing a property type of Order, we aid in the creation of a |
| # field corresponding to this property in a ProtoRPC message schema. |
| |
| # The EndpointsAliasProperty can be used as a decorator as is done here, or |
| # can be used in the same way NDB properties are, e.g. |
| # attr1 = ndb.StringProperty() |
| # and the similar |
| # order = EndpointsAliasProperty(OrderGet, setter=OrderSet, ...) |
| # where OrderGet would be the function defined here. |
| @EndpointsAliasProperty(setter=OrderSet, property_type=Order, |
| default=Order.MYFIRST) |
| def order(self): |
| # We only need to limit the values to Order enums, so we can use the getter |
| # from the helper property with no changes. |
| return super(MyModel, self).order |
| |
| |
| |
| # Since we are using auth, we want to test with the Google APIs Explorer: |
| # https://developers.google.com/apis-explorer/ |
| # By default, if allowed_client_ids is not specified, this is enabled by |
| # default. If you specify allowed_client_ids, you'll need to include |
| # endpoints.API_EXPLORER_CLIENT_ID in this list. This is necessary for auth |
| # tokens obtained by the API Explorer (on behalf of users) to be considered |
| # valid by our API. |
| @endpoints.api(name='myapi', version='v1', description='My Little API') |
| class MyApi(remote.Service): |
| |
| # We use specify that request_fields is ('attr1', 'attr2') because the |
| # created value is set when the entity is put to the datastore and the owner |
| # is set from the current user. As in basic_with_auth, since user_required is |
| # set to True, the current user will always be valid. |
| |
| # Since no response_fields are set, the four fields from |
| # _message_fields_schema will be sent in the response. |
| @MyModel.method(request_fields=('attr1', 'attr2'), |
| user_required=True, |
| path='mymodel', http_method='POST', name='mymodel.insert') |
| def MyModelInsert(self, my_model): |
| my_model.owner = endpoints.get_current_user() |
| my_model.put() |
| return my_model |
| |
| # As in paging/main.py, we use the fields limit, order and pageToken for |
| # paging, but here "order" is the Enum-based property we defined above. As |
| # mentioned in the definition of OrderSet, these order values are coupled with |
| # the filter for current user. |
| |
| # Since no collection_fields are set, each value in "items" in the response |
| # will use the four fields from _message_fields_schema. |
| @MyModel.query_method(query_fields=('limit', 'order', 'pageToken'), |
| user_required=True, |
| path='mymodels', name='mymodel.list') |
| def MyModelList(self, query): |
| # Current user is valid since user_required is set to True. |
| return query.filter(MyModel.owner == endpoints.get_current_user()) |
| |
| |
| application = endpoints.api_server([MyApi], restricted=False) |