Summary: zato-apitest, a newly released tool lets everyone test their APIs in a human-friendly way, in plain English, with no programming needed. Plenty of features out of the box, built-in demo mode, screenshots and heaps of documentation await on GitHub.

Installation:

$ sudo pip install zato-apitest

Here's a screenshot of a demo executed with apitest demo running against a live test server:

Screenshots

What it can do:

  • Invoke HTTP APIs

  • Use JSON Pointers or XPath to set request's elements to strings, integers, floats, lists, random ones from a set of values, random strings, dates now/random/before/after/between.

  • Check that JSON and XML elements, exist, don't exist, that an element is an integer, float, list, empty, non-empty, that it belongs to a list or doesn't.

  • Set custom HTTP headers, user agent strings, method and SOAP action.

  • Check that HTTP headers are or are not of expected value, that a header exists or not, contains a value or not, is empty or not, starts with a value or not and ends with a value or not.

  • Read configuration from environment and config files.

  • Store values extracted out of previous steps for use in subsequent steps, i.e. get a list of objects, pick ID of the first one and use this ID in later steps.

  • Be integrated with JUnit

  • Be very easily extended in Python

Note that zato-apitest is meant to test APIs only. It's doesn't simulate a browser nor any sort of user interactions. It's meant purely for machine-machine API testing.

Originally part of Zato - open-source ESB, SOA, REST, APIs and cloud integrations in Python.

In addition to HTTP Zato itself supports AMQP, ZeroMQ, WebSphere MQ, including JMS, Redis, FTP, OpenERP, SMTP, IMAP, SQL, Amazon S3, OpenStack Swift and more so it's guaranteed zato-apitest will grow support for more protocols and transport layers with time.

More details on GitHub.

Now, is that cool or is that cool? :-)

Article presenting how OpenStack notifications in Zato can be consumed and configured with no programming needed.

Whereas the previous instalment focused on sending messages out to OpenStack-based systems, the current one describes how to receive information placed in OpenStack containers.

As it happens, it's only a matter of creating a connection, filling out a form and pressing OK, as below:

Screenshots

Screenshots

Here is what just happened after clicking OK:

  • A background asynchronous task has been started to monitor a set of OpenStack containers each 5 seconds
  • Any objects found matching, or not, the name pattern are taken into account for further processing
  • A service of one's choice will be invoked each time an object is taken off a container
  • The service will receive the contents of this object if the latter's name matches, or not, a certain pattern

And that's it, no programming is needed. Naturally, a piece of code is needed to accept the data each object holds but this is nothing OpenStack-specific. For instance, a basic service that logs everything it receives may look like:

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

from __future__ import absolute_import, division, print_function, unicode_literals

# Zato
from zato.server.service import Service

class ProcessNewAccounts(Service):
    def handle(self):
        self.log_input()

New on GitHub, Zato HTTP audit log allows you to store requests and responses, along with their headers in a database that can be queried at any time.

Recognizing that certain parts of messages should never be stored anywhere because of their sensitive nature, one can also specify JSON Pointers or XPath expressions indicating which elements in a message should be masked out - for instance, user passwords and social security numbers don't necessarily belong in the audit log.

The database can be asked to return only those items that match a given criteria, for instance - a correlation ID or a value of a header.

As always with each feature, audit log comes with both REST and SOAP APIs to facilitate the creation of interesting supplementary tools on top of what is available by default.

Screenshots

Screenshots

Screenshots

Screenshots

Summary: How to navigate over XML documents with reusable XPath expressions, including ones with namespaces.

Along with JSON Pointers, presented recently, all the features in the post will be released in Zato 2.0, which is currently in development on GitHub.

XPath expressions and XML namespaces can be defined once and reused in many services using the web admin GUI, API or enmasse. For instance, here are definitions needed to map identifiers between hypothetical ERP and CRM systems.

Screenshots

Screenshots

Screenshots

Screenshots

And that's how a sample service may look like - notice that response is an empty document initially and all the required elements are created on fly, including placing them in namespaces defined in the GUI.

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

from __future__ import absolute_import, division, print_function, unicode_literals

# lxml
from lxml import etree

# Zato
from zato.server.service import Service

class IdMapper(Service):
    name = 'id.mapper2'

    def handle(self):

        # Obtain an XPath wrapper for the service's request
        req_xp = self.msg.xpath()

        # Get a couple of request elements
        cust_id = req_xp.get('ERP CustomerId')
        account_id = req_xp.get('ERP AccountId')

        # Prepare response and its associated XPath wrapper.
        resp = etree.Element('resp')
        resp_xp = self.msg.xpath(resp)

        # Map request elements into response
        resp_xp.set('CRM CustomerId', cust_id)
        resp_xp.set('CRM AccountId', account_id)

        # Set a constant value, note that in response XML namespaces
        # will be added as needed.
        resp_xp.set('CRM Provider', 'ABC')

        # Return response
        self.response.payload = etree.tostring(resp, pretty_print=True)

Screenshots

As always - that very code can be hot-deployed onto a cluster and re-configured on fly. Should any updates be needed to XPath expressions or namespaces used, no code changes will have to be performed - such a service is purely configuration-driven and can be exposed over multiple channels, such as HTTP, AMQP, ZeroMQ or JMS WebSphere MQ.

JSON Pointer syntax, as defined in RFC 6901 offers means to navigate through JSON documents /using/paths/in/documents - here's how to make use of them in the upcoming version 2.0 of Zato.

Systems being integrated will usually offer standard ways to refer to certain objects transfered in JSON documents. For instance, a customer ID will almost always be in the same location across documents no matter the actual API call.

We can take advantage of it and define a set of reusable JSON Pointers that will be referred to in body of multiple services. They will be R in IRA of Interesting, Reusable and Atomic APIs one should strive to design.

Here come the definitions and the net result..

Screenshots

Screenshots

Screenshots

Screenshots

.. the service ..

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

from __future__ import absolute_import, division, print_function, unicode_literals

# stdlib
from json import dumps

# Zato
from zato.server.service import Service

class IdMapper(Service):
    name = 'id.mapper'

    def handle(self):

        # Obtain a JSON pointer to the service's request
        req_jsp = self.msg.json_pointer()

        # Get a couple of request elements
        cust_id = req_jsp.get('ERP CustomerId')
        account_id = req_jsp.get('ERP AccountId')

        # Prepare a response and its associated JSON pointer.
        # Note that JSON pointers can be used with regular Python dicts.
        resp = {}
        resp_jsp = self.msg.json_pointer(resp)

        # Map request elements into response
        resp_jsp.set('CRM CustomerId', cust_id)
        resp_jsp.set('CRM AccountId', account_id)

        # Return response
        self.response.payload = dumps(resp)

.. and the discussion of what has been presented:

  • A JSON Pointer by default points to the request a service receives
  • Calling .get on a pointer fetches a value from the underlying document
  • JSON Pointers can work with regular Python dicts, they're not strictly limited to JSON
  • Calling .set assigns a value under the path previously configured
  • When setting values, all paths that don't exist are created on fly (similar to mkdir -p in shell)

JSON Pointers can be freely reused in more than one service - and should one day an ERP or CRM above decide to change their data model only the JSON Pointer's definition will have to be updated, with no code changes to the services necessary.

Note that the very same API is also available for XPath expressions, presented in a companion post.