In order to get a basic knowledge in web service backend for mobile clients using Google’s App Engine technologies, I am writing this tutorial that is about a very simple Chat with single conversation channel and OAuth for user’s authorisation. The technologies that are used here are the Google App Engine for the backend hosting, where the server is written in Python and Google Cloud Endpoints. In this part-1 of the tutorial I will show you the Server Side of the Google Cloud Endpoints application in Python. In next tutorials I will present you the client’s side starting with a simple iOS client in part-2.
The project consists of a very simple “Chat Room” (only one and it will be static just for convenience) where users must be authorised under OAuth mechanism to send and receive messages. The users will be able to post messages (just text for this post) and then can retrieve the conversations of this channel. It is a very simple application and it is implemented just to show you how the GAE cloud infrastructure is working with clients.
If you never heard of Google’s App Engine a good start is here ( for python) https://developers.google.com/appengine/docs/python/gettingstartedpython27/introduction.
Considering that you already familiar with the Google App Engine technology, and you have installed the required Google App Engine Launcher we starting with creating an empty project.
In the app.yaml file we only need the basic information and the Endpoints information :
application: sugarapptest version: 1 runtime: python27 threadsafe: true api_version: 1 handlers: # Endpoints handler - url: /_ah/spi/.* script: main.application
The app.yaml file is a description file of the application, here we entered the application id, the version, the python runtime version, if it is threadsafe and the api’s version. The only difference from an simple empty application is the Endpoint handler that we added for the Google’s Api explorer that we will see later.
If you are not familiar with Google Cloud Endpoints here is a very nice reference with great documentation : https://developers.google.com/appengine/docs/python/endpoints/ .
I am going to use a very useful python module from a google developer Mr. Dhemes, that allows you to interact directly with the model entities of your API methods rather than ProtoRPC requests. This is the link that you have to download the lib and put it on your project https://github.com/GoogleCloudPlatform/endpoints-proto-datastore .
Ok lets start now, lets take a look at the code bellow, this code is as simple as it can be, you can do so many things with GAE Cloud endpoints with few lines :
# description: A very simple chat Google app engine service using OAuth # author: Alex sug4r # email: firstname.lastname@example.org # date: 2/2/13 from google.appengine.ext import endpoints # for endpoints from google.appengine.ext import ndb # for data storage from protorpc import remote # for API from protorpc import messages from endpoints_proto_datastore.ndb import EndpointsAliasProperty from endpoints_proto_datastore.ndb import EndpointsModel from endpoints_proto_datastore.ndb import EndpointsUserProperty # Static Default Channel IRC_CHANNEL = 'channel' # order by class, to order by created date time class Order(messages.Enum): WHEN = 1 # Database Model & RPC Model for Chat Message class ChatMessage(EndpointsModel): _message_fields_schema = ('content', 'fromUser', 'toChannel', 'created') # Set model schema content = ndb.StringProperty(required=True) # the message's content fromUser = ndb.UserProperty(required=True) # get username from OAuth toChannel = ndb.StringProperty(required=True) # send to channel created = ndb.DateTimeProperty(auto_now_add=True) # when the messages is created # set order def OrderSet(self, value): if value == Order.WHEN: super(ChatMessage, self).OrderSet('created') else: raise TypeError('Unexpected value of Order: %s.' % (value,)) # apply order @EndpointsAliasProperty(setter=OrderSet, property_type=Order, default=Order.WHEN) def order(self): return super(ChatMessage, self).order # The API @endpoints.api(name='sugarchat', version='v1', description='Test google endpoints chat') class ChatAPI(remote.Service): # Method to POST a Message @ChatMessage.method(user_required=True, # for OAuth request_fields=('content',), name='chatMessage.send', path='chatMessage') def send_chatMessage(self, chatMessage): chatMessage.fromUser = endpoints.get_current_user() #from OAuth chatMessage.toChannel = IRC_CHANNEL chatMessage.put() return chatMessage # Method to Get a Conversation @ChatMessage.query_method(user_required=True, query_fields=('limit', 'order', 'pageToken'), name='chatMessage.conversation', path='conversation') def conversation_messages(self, query): return query.filter(ChatMessage.toChannel == IRC_CHANNEL) application = endpoints.api_server([ChatAPI])
So let’s take it from the beginning, first we have imported endpoints for Google’s Cloud Endpoints usage and ndb for Google’s Datastore usage from Google’s App Engine module. Next we are importing the endpoints_proto_datastore’s EndpointsModel, this is the class from the module that you downloaded before and will make our life a looooot easier. This IRC_CHANNEL is a constant (for this post) that describes our only one channel that we have into our app.
But see the good stuff here, the class ChatMessage is a sub class of EndpointsModel and it is something like a ndb Model and protoRPC Message at the same time. Very convenient huh? Thank you google! This model is our Chat Message that the user sends to (the only one channel we have). The properties of this model are of course the content, that it is the text that user sends that is a string and it is required (otherwise is not data at all), the id of the user who send that is a user property that takes value automatically from OAuth, and the created date that is a timestamp that triggers the time of ChatMessage creation. Consider that next there is a Sorting method defined, used to order by created timestamp, we need to specify the model’s schema with the line above. The OrderSet function is used to set the order of model results based on a value (WHEN) and the order method is a EndpointsAliasProperty decorator that is used to apply order by (sorting).
Next we only have to construct our API Service, we create a class called ChatAPI that is sub class of remote.service, so it is a service class that provides the properties of API Service to our instance. But, there is something strange above this line of code, this @endpoints.api thing is a decorator for the service that Google provides to us. With this structure we build our service, the data we use here is a name for the service, a version and a human readable description.
Of course this class needs some methods to operate. We need two methods, one to send a message to the channel, and one to read all messages from the channel. So we creating an instance method called send_chatMessage that take as a parameter ChatMessage model, the message to be sent. We set the “fromUser” property to the currently logged user from OAuth, the ‘toChannel’ property to our only one channel (in a real application we want to change channels). After we get the right data, we store this data in the datastore using put() and return the new message. Using the same Decorator we required values as you see.
The last method that we need is a method that will return the list of all messages (Conversation) on the channel. So we return the query from datastore applying a filter, to show results based on selected channel. I know this is not needed for this application because we have only one channel. The decorator here is a little more complex, because it has some parameters like ‘limit’ that is used to show a limited number of messages, and ‘pageToken’ that provides a token for the next page results when there is limit on them.
Finally we set the application to be a service that contains our API : ChatAPI.
Time to see our app online, we run the Google App Engine launcher, run the application locally (or remotely if you prefer), and with this address in our browser we can have access to API apps on GAE : localhost:8080/_ah/api/explorer or <yourapp>.appspot.com/_ah/api/explorer .
If everything it’s gone well you will see something like this :
Here is the Api application when you click on that, you can see your API methods :
Now let’s try to create a new message :
Everything went great and as you can see it works, but before you are completely sure you have to check your data store also :
Now don’t forget that we can see all messages from he method we create to show conversations, after adding 2 more messages log in using OAuth you can see the following :
As you can see, there is a sorting method applied here, because I set as default sorting, order by created date.
That was the Server’s side code, hopefully after everything is working great we are ready to create some client applications to store / retrieve messages form the server. At part-2 of this tutorial I will show you how to create a very simple iOS client for this endpoints application.