wsme/doc/source/functions.rst
Stephen Finucane 7092903ba1 Rework documentation build
Modern docs! This fixes a couple of issues introduced in the previous
patches and generally cleans up a lot of mess.

Change-Id: Ib964c16251bce12fe498b13455ed3515ef205916
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
2019-11-18 13:58:59 -08:00

5.5 KiB

Functions

WSME is based on the idea that most of the time the input and output of web services are actually strictly typed. It uses this idea to ease the implementation of the actual functions by handling those input/output. It also proposes alternate protocols on top of a proper REST api.

This chapter explains in detail how to 'sign' a function with WSME.

The decorators

Depending on the framework you are using, you will have to use either a @wsme.signature decorator or a @wsme.wsexpose decorator.

@signature

The base @wsme.signature decorator defines the return and argument types of the function, and if needed a few more options.

The Flask and adapter proposes a specific version of it, which also wrap the function so that it becomes suitable for the host framework.

In any case, the use of @wsme.signature has the same meaning: tell WSME what is the signature of the function.

@wsexpose

The native Rest implementation, and the Pecan adapter add a @wsme.wsexpose decorator.

It does what @wsme.signature does, and exposes the function in the routing system of the host framework.

This decorator is generally used in an object-dispatch routing context.

Note

Since both decorators play the same role, the rest of this document will alway use @signature.

Signing a function

Signing a function is just a matter of decorating it with @signature:

@signature(int, int, int)
def multiply(a, b):
    return a * b

In this trivial example, we tell WSME that the 'multiply' function returns an integer, and takes two integer parameters.

WSME will match the argument types by order to determine the exact type of each named argument. This is important since most of the web service protocols don't provide strict argument ordering but only named parameters.

Optional arguments

Defining an argument as optional is done by providing a default value:

@signature(int, int, int):
def increment(value, delta=1):
    return value + delta

In this example, the caller may omit the 'delta' argument, and no 'MissingArgument' error will be raised.

Additionally, this argument will be documented as optional by the sphinx extension.

Body argument

When defining a Rest CRUD API, we generally have a URL to which we POST data.

For example:

@signature(Author, Author)
def update_author(data):
    # ...
    return data

Such a function will take at least one parameter, 'data', that is a structured type. With the default way of handling parameters, the body of the request would look like this:

{
    "data":
    {
        "id": 1,
        "name": "Pierre-Joseph"
    }
}

If you think (and you should) that it has one extra level of nesting, the 'body' argument is here for you:

@signature(Author, body=Author)
def update_author(data):
    # ...
    return data

With this syntax, we can now post a simpler body:

{
    "id": 1,
    "name": "Pierre-Joseph"
}

Note that this does not prevent the function from having multiple parameters; it just requires the body argument to be the last:

@signature(Author, bool, body=Author)
def update_author(force_update=False, data=None):
    # ...
    return data

In this case, the other arguments can be passed in the URL, in addition to the body parameter. For example, a POST on /author/SOMEID?force_update=true.

Status code

The default status codes returned by WSME are 200, 400 (if the client sends invalid inputs) and 500 (for server-side errors).

Since a proper Rest API should use different return codes (201, etc), one can use the 'status_code=' option of @signature to do so.

@signature(Author, body=Author, status_code=201)
def create_author(data):
    # ...
    return data

Of course this code will only be used if no error occurs.

In case the function needs to change the status code on a per-request basis, it can return a wsme.Response object, allowing it to override the status code:

@signature(Author, body=Author, status_code=202)
def update_author(data):
    # ...
    response = Response(data)
    if transaction_finished_and_successful:
        response.status_code = 200
    return response

Extra arguments

The default behavior of WSME is to reject requests that give extra/unknown arguments. In some (rare) cases, this is undesirable.

Adding 'ignore_extra_args=True' to @signature changes this behavior.

Note

If using this option seems to solve your problem, please think twice before using it!

Accessing the request

Most of the time direct access to the request object should not be needed, but in some cases it is.

On frameworks that propose a global access to the current request it is not an issue, but on frameworks like pyramid it is not the way to go.

To handle this use case, WSME has a special type, HostRequest:

from wsme.types import HostRequest

@signature(Author, HostRequest, body=Author)
def create_author(request, newauthor):
    # ...
    return newauthor

In this example, the request object of the host framework will be passed as the request parameter of the create_author function.