Defining outbound connections

Start by making sure rhc is in python's path,


In [ ]:
import sys
sys.path.append('/opt/rhc')

and importing a couple of components.


In [ ]:
import rhc.micro as micro
import rhc.async as async

Connections to HTTP resources can be defined using the CONNECTION and RESOURCE directives in a micro file. A simple definition follows.


In [ ]:
p=micro.load_connection([
    'CONNECTION placeholder http://jsonplaceholder.typicode.com',
    'RESOURCE document /posts/1',
])

Now, make a connection and see what happens.


In [ ]:
async.wait(micro.connection.placeholder.document(_trace=True))

What happened?

This code performs a GET on http://jsonplaceholder.typicode.com/posts/1 and prints the result. There are simpler ways to perform this task, like using the wonderful requests library, but this solution is designed to perform well in a microservice environment where numerous connections are being handled simultaneously.

How does it work?

Function load_connection

The load_connection helper function allows for the dynamic loading of connection definitions. In this case, the definition is contained in a list, but could also be loaded from a file by specifying the file's name, or by specifying a dot-separated path to the file in the python code tree.

In a microservice implementation, the connection definitions are included in the micro file, or in one of the imported files. This function is included for experimentation and development.

CONNECTION

The CONNECTION directive provides a name and a url for a service. The connection is added to rhc.micro, and can be accessed as rhc.micro.connection.{name}. Since rhc.micro is imported as micro, the rhc preface is skipped in the example.

All by itself, a CONNECTION doesn't provide much.

RESOURCE

The RESOURCE directive adds a specific HTTP resource to the most recently specified CONNECTION. In this case, the resource name is document and the path to the resource is /posts/1; when combined with the CONNECTION, the full resource url is http://jsonplaceholder.typicode.com/posts/1.

The resource is added to the connection, and can be accessed as micro.connection.{connection name}.{resource name} or, specifically, micro.connection.placeholder.document.

Function wait

The wait helper function starts a connection to the resource and waits until it is done, printing the result.

This hints at the asynchronous activity underpinning micro.connection, which will become more apparent in subsequent examples.

In a microservice, the wait function is never used, since it would cause the entire service to block until wait completes. This function is included for testing and development.

Adding Parameters

It would be nice to parameterize our RESOURCE so that we can specify a document other than /posts/1. This is accomplished by changing the RESOURCE directive to include a curly-brace delimited name


In [ ]:
micro.load_connection([
    'CONNECTION placeholder http://jsonplaceholder.typicode.com',
    'RESOURCE document /posts/{document_id}',
])

which adds a required argument to the micro.connection.placeholder.document function. Now the wait call looks like this:


In [ ]:
async.wait(micro.connection.placeholder.document(1))

Adding non-path parameters

Although it doesn't make sense to add a document to a GET request, we can do it for demonstration purposes. Add a trace=true to the RESOURCE like this:


In [ ]:
micro.load_connection([
    'CONNECTION placeholder http://jsonplaceholder.typicode.com',
    'RESOURCE document /posts/{document_id} trace=true',
])

This will log the entire HTTP document when it is sent, making it easy for us to see what is going on. Make sure to enable debug logging, by doing something like the following:


In [ ]:
import logging
logging.basicConfig(level=logging.DEBUG)

Note: In a production microservice, you should never use trace=debug. Documents often contain sensitive information that you don't want to end up in logs.

A json document will be assembled from the keyword arguments to micro.connection.placeholder.document. Try running the example with this wait call:


In [ ]:
async.wait(micro.connection.placeholder.document('1', a=1, b=2))

Required and Optional parameters

Most REST documents are not composed of random collections of keyword arguments. With the addition of two directives, specific arguments can be required or optionally specified for each RESOURCE.

Change the connection list to look like this:


In [ ]:
micro.load_connection([
    'CONNECTION placeholder http://jsonplaceholder.typicode.com',
    'RESOURCE document /posts/{document_number} trace=true',
    'REQUIRED first_name',
    'OPTIONAL planet default=earth',
])

The document resource now has two required arguments: document_id from the path and first_name from the REQUIRED directive. If the optional argument planet is not supplied, it will use the default value of earth.

Run the example with this wait call:


In [ ]:
async.wait(micro.connection.placeholder.document(1, 2))

Note: path substitutions come first, in left-to-right order as encountered in the path, followed, in order, by each REQUIRED argument. OPTIONAL arguments, if specified, can be in any order.