Generating API documentation

A recurring need in larger integration projects is generation of API documentation for users belonging to different, yet related, target groups. Read on to learn how to generate Zato-based API specifications for more than one group from a single source of information.

A typical scenario is granting access to the same APIs to external and internal users - what they have in common is that all of them may want to access the same APIs yet not all of them should have access to documentation on the same level of details.

For instance - external developers should only know what a given endpoint is for and how to use it but internal ones may also be given information about its inner workings, the kind of details that external users should never learn about.

If documentation for two such groups is kept separately, it may require more maintenance effort than necessary - after all, if most of it is the same for everyone then it would make sense to keep it in one place and only add or remove details, depending on which group particular API documentation needs to be generated for.

The previous article went through the process of generating API specifications step by step - this one goes even further by adding introducing the notion of tags that drive which parts of documentation to generate or not.

Python docstrings

Let us use one of the same services as previously - here is its basic form:

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

# Zato
from zato.server.service import Int, Service

class RechargeCard(Service):
    """ Recharges a pre-paid card.
    Amount must not be less than 1 and it cannot be greater than 10000.
    """

    class SimpleIO:
        input_required = 'number', Int('amount')
        output_required = Int('status')

The result, when generated as Sphinx, will be like below:

Now, we would like to add some details that only internal users should have access to - this is how the docstring should be modified, assuming for a moment that there are two CRM systems in the company the usage of which depends on a particular end user's account ..

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

# Zato
from zato.server.service import Int, Service

class RechargeCard(Service):
    """ Recharges a pre-paid card.
    Amount must not be less than 1 and it cannot be greater than 10000.

    #internal

    For user accounts starting with QM - MyCRM is queried.
    For any other account - MyCRM-2 is queried.
    """

    class SimpleIO:
        input_required = 'number', Int('amount')
        output_required = Int('status')

.. with the corresponding change in the output:

What happened above was that we applied a tag called #internal - anything that follows it is included in the output only if that very tag is requested when generating the documentation.

Note that the tag name is arbitrary, it could be any other name. Note also that there may be more than tag in a docstring, e.g. #confidential, #private or anything else.

Command-line usage

To actually include the #internal tag, the zato apispec command needs to be told about it explicitly, as below:

$ zato apispec /path/to/server \
  --dir /path/to/output/directory \
  --include api.* \
  --tags public,internal

If you do not give the command any tags, only the public part of the docstring, one without any tags, will be included in the resulting documentation.

To make your clear to other developers, you can also directly use the tag public in the command - this will make it easier to understand that you want to generate publicly available information and nothing else.

$ zato apispec /path/to/server \
  --dir /path/to/output/directory \
  --include api.* \
  --tags public

Wrapping up

This is it, you are done now - you can keep your API documentation in one place now and include only the relevant parts of it depending on context - this ensures that no matter who the recipient of your documentation is, it will be always generated from a single source, thus ensuring that all the output will stay in sync.