Invoking other services

Zato services can invoke one another synchronously (self.invoke) or in an asynchronous manner (self.invoke_async) so that you can choose whether the invoking service is blocked waiting for the response or if the other service should be run in background.

Note that for the latter, you will not receive a response directly, instead, when invoking another service asynchronously you are given a Correlation ID the service being invoked will also receive so that its response, if any, can be correlated later on, using other Zato mechanisms, specific to your application.

How to pass a request to another service

Passing a request is most convenient if another service uses Simple IO (SIO), in such cases it is possible to use any kind of input, e.g. a dataclass, a plain Python dictionary or any arbitrary Python object can be sent to another service.

from zato.server.service import Service

class MyService(Service):
    def handle(self):

        # A regular dictionary will do when invoking an SIO service
        input_data = {'cluster_id': 1}

        response = self.invoke('zato.security.get-list', input_data)
        for item in response.zato_security_get_list_response:
            self.logger.info(item.name)

Use the 'data_format' parameter if a service expects the incoming data to be in a particular data format, such as JSON or CSV.

# stdlib
from json import dumps

# Zato
from zato.common import DATA_FORMAT
from zato.server.service import Service

class MyService(Service):
    def handle(self):

        input_data = {'customer_id': 123, 'phone_no': '123-456-789'}
        service_name = 'invoking.my-service2'

        response = self.invoke(service_name, input_data, data_format=DATA_FORMAT.JSON)
        self.logger.info(response)

If the target service uses SimpleIO, its input parameters can be also sent inline, directly in the self.invoke method, just like with a regular Python function call, without creating an input dict or model object upfront:

response = self.invoke(service_name, customer_id=123, phone_no='123-456-789')

This example shows an asynchronous call, conducted in background:

# stdlib
from json import dumps

# Zato
from zato.common import DATA_FORMAT
from zato.server.service import Service

class MyService(Service):
    def handle(self):

        input_data = {'customer_id': 123, 'phone_no': '123-456-789'}
        service_name = 'invoking.my-service2'

        cid = self.invoke_async(service_name, input_data, data_format=DATA_FORMAT.JSON)
        self.logger.info(cid)

class MyService2(Service):
    def handle(self):
        # Note how the payload is a Python dictionary
        self.logger.info(self.request.payload['customer_id'])

Reading responses

SIO services will produce dictionaries as their responses and you can choose to use them directly or to convert a response to a Bunch in order to use the dot-notation when accessing the response's elements. Thus, the examples below are equivalent.

# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class MyService(Service):
    def handle(self):
        response = self.invoke('invoking.my-service2')
        self.logger.info(response['my_response_elem']['cust_name'])

class MyService2(Service):
    class SimpleIO:
        input_required  = 'cust_id'
        output_required = 'cust_name'
        response_elem = 'my_response_elem'

    def handle(self):
        self.response.payload.cust_name = 'Alexis Smith'

When invoking a service asynchronously you do not know when its response will be produced.

Hence, the response from an asynchronous invocation is the Correlation ID the service which is being invoked will receive.

You can use the CID returned to correlate asynchronous responses when they are available - how it is performed is up to your application.

# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class MyService(Service):
    def handle(self):
        cid = self.invoke_async('invoking.my-service2')
        self.logger.info('Got CID %s', cid)

class MyService2(Service):
    def handle(self):
        self.logger.info('My CID is %s', self.cid)
INFO - Got CID c37780e779666e59ed284f92
INFO - My CID is c37780e779666e59ed284f92