Messages
========
Google Cloud Endpoints uses `protorpc messages `__ as the data marshalling library for sending and receiving data v Ferris provides a lot of utilities around defining messages and using them with endpoints.
Most of ferris' message functionality comes from the `protopigeon `__ library.
Defining messages
-----------------
This is done exactly the same as with the protorpc library::
from protorpc import messages
class MyMessage(messages.Message):
greeting = messages.StringField(1)
Remember endpoints requires that messages used for requests and responses need to be available when a module is imported. This means that you can't create new messages and runtime and expect them to work. Messages should be defined as top-level classes in the module they belong to.
Generating messages from models
-------------------------------
You can generate a message for any ``ndb.Model`` class using ``protopigeon``. All properties, including structured properties, are supported with the exception of computed properties.
For example if you have this model::
from google.appengine.ext import ndb
class Post(ndb.Model):
title = ndb.StringProperty()
content = ndb.TextProperty()
favorites = ndb.IntegerProperty()
created = ndb.DateTimeProperty(auto_now_add=True)
You can generate a message for it::
import protopigeon
PostMessage = protopigeon.model_message(Post)
This is equivalent of doing this manually::
from protorpc import messages
from protorpc.message_types import DateTimeField
class PostMessage(messages.Message):
title = messages.StringField(1)
content = messages.StringField(2)
favorites = messages.IntegerField(3)
created = DateTimeField(4)
Translating between entities and messages
-----------------------------------------
If you have a datastore entity (an instance of a Model) and want to put its data into your generated message::
post = Post(title="Test post please ignore", content="This is just a test", favorites=1230)
post_message = protopigeon.to_message(post, PostMessage)
Conversely, if you have an instance of a message and you want an entity::
post = protopigeon.to_entity(post_message, Post)
post.put()
You can even use this to update an existing entity instance (this works vice-versa with existing message instances too)::
Post = Post.query.get()
protopigeon.to_entity(post_message, post)
List messages
-------------
Endpoints doesn't allow you to directly return a list of messages. Instead, you need to wrap it in a container message like so::
PostListMessage(messages.Message):
items = messages.MessageField(PostMessage, 1, repeated=True)
posts = Post.query()
list_message = PostListMessage()
list_message.items = [protopigeon.to_message(x, PostMessage) for x in posts]
This quickly becomes repetitive. Instead you can use :func:`~protopigeon.list_message` to generate these messages for you::
PostListMessage = protorpc.list_message(PostMessage)
To simplify the translation part you can use :func:`~ferris3.messages.serialize_list`::
posts = Post.query()
list_message = ferris3.messages.serialize_list(PostListMessage, posts)
Composing messages
------------------
Messages can't inherit from other messages, but you can use the :func:`~protopigeon.compose` method to emulate it.::
class Origin(Message):
year = IntegerField(1)
location = StringField(2)
class Traveler(Message):
name = StringField(1)
species = StringField(1)
class Tag(Message):
urlsafe = StringField(1)
TravelerWithOriginAndTag = protopigeon.compose(Origin, Destination, Tag)
instance = TravelerWithOriginAndTag(
name='The Doctor',
year=2013,
location='Gallifrey',
species='Time Lord',
urlsafe='the_doctor'
)
API Reference
-------------
.. function:: protopigeon.model_message(Model, only=None, exclude=None, converters=None)
Generates a protorpc message for the given model. Pass in ``only`` or ``exclude`` to control which fields are present in the generated message.
.. autofunction:: protopigeon.to_message
Converts the given ``entity`` into a protorpc message using the given ``message`` class.
.. autofunction:: ferris3.messages.serialize
Alias for :func:`protopigeon.to_message`
.. autofunction:: protopigeon.to_entity
Converts the given ``message`` into a ndb entity using the given ``model`` class.
.. autofunction:: ferris3.messages.deserialize
Alias for :func:`protopigeon.to_entity`
.. autofunction:: protopigeon.list_message
Wraps the given message in a container message that has ``items`` and ``next_page_token`` fields.
.. autofunction:: ferris3.messages.serialize_list
Transforms all of the provided entities and places it in the message's ``items`` attribute.
.. autofunction:: protopigeon.compose
Combines two or more messages into one single message class.