How to use Task Queue (Push Queue) with Protorpc.
I have a landing page form that do multiple actions when sending it:
The form send is implemented in the server side with protorpc.
class FormRequest(messages.Message)
field1 = messages.StringField(1, required=True)
field2 = messages.StringField(2, required=True)
...
class FormApi(remote.Service):
@remote.method(TravelRequest, message_types.VoidMessage)
def insert(self, request):
# Save the form in the DataStore
travel = FormModel(field1=request.field1, field2=request.field2)
travel.put()
# Send an email to the client
...
# Send the data to a third party
...
return message_types.VoidMessage()
This solution is stuck because the user need to wait all this request time. (In this case it is only 2-3s but it is a lot for a landing page form)
A good solution will be to use taskqueue to minimise the time the user need to wait:
(As an example)
class ...
@remote ...
def ...
# Save the form in the DataStore
taskqueue.add(url='/api/worker/save_to_db', params={'field1': request.field1, 'field2': request.field2})
# Send an email to the client
taskqueue.add(url='/api/worker/send_email', params={'field1': request.field1, 'field2': request.field2})
# Send the data to a third party (CRM)
taskqueue.add(url='/api/worker/send_to_crm', params={'field1': request.field1, 'field2': request.field2})
The "problem" is that protorpc get only json object as request. How to do this with TaskQueue(Push) ?
The default behavior of TaskQueue is to send params as a string of urlencoded and it's not conveniant to protorpc.
Let's define a Worker service for the taskqueue:
class WorkersApi(remote.Service):
@remote.method(TravelRequest, message_types.VoidMessage)
def save_to_db(self, request):
# Instead of write each parameter, I am using this "cheat"
params = {}
for field in request.all_fields():
params[field.name] = getattr(request, field.name)
# Save data in the datastore
form_model = FormModel(**params)
form_model.put()
return message_types.VoidMessage()
Pay attention that I am using the same message
object for the real request and for the taskqueue request (It is a big advantage to need not create and different message
object for each request)
The question is how to use taskqueue with this protorpc function.
As I say in the question, the default behavior of taskqueue is not conveniant.
The solution is to convert the orignal request/message (in our example the FormRequest) object back to string and set a header to taskqueue that the payload is application/json
.
Here's the code:
# This format string is take from the util file in the protorpc folder in Google App Engine source code
format_string = '%Y-%m-%dT%H:%M:%S.%f'
params = {}
for field in request.all_fields():
value = getattr(request, field.name)
if (isinstance(value, datetime.datetime)):
value = value.strftime(format_string)
params[field.name] = value
taskqueue.add(url='/api/workers.save_to_db', payload=json.dumps(params), headers={'content-type':'application/json'})
Do the same for the "email" and the "crm".