Skip to main content

Library for handling POST commands for Flask framework

Project description

Command Handler

PyPI version Build Status

Command Handler is a library for Flask framework which provides:

  • API method for posting new commands,
  • tools for easy command handlers management.

Installation

pip install command_handler

Usage

To use command_handler import CommandHandler object and call it with your flask application passed.

from command_handler import CommandHandler
from flask import Flask

app = Flask(__name__)
ch = CommandHandler(app)

This will add a new endpoint to your API: /command which is specified in Command input section.

Handlers can be added by addHandler method of the returned object as described in Defining handlers section.

Configuration

The way CommandHandler is designed allows to pass config parameters to the initializer.

Command input endpoint URL

It is possible to prefix default /command route with any string accepted by Flask's routing by defining rulePrefix parameter.

Following code sets URL to /foo/bar/command:

ch = CommandHandler(app, rulePrefix="/foo/bar")

It is also possible to change default /command route suffix by defining ruleSuffix.

Following code sets URL to /baz:

ch = CommandHandler(app, ruleSuffix="baz")

Request validators

It is possible to define command request validators. CommandHandler by default sets command and json validators and it is not possible to remove them.

Validators can be defined by setting validators parameter which accepts list of strings.

ch = CommandHandler(app, validators=["command", "json"])

List of possible validators can be found in Validators section

Custom validators

To register custom validator pass it to the command_handler.request.validator.ValidatorFactory.addAssert method. It has to accept one positional parameter which contains request passed to the view defined by CommandHandler.

If request is invalid it is needed to raise command_handler.request.validator.exceptions.AssertionFailedException. First parameter of its constructor will be send to the response's body and second will be send as response code.

from command_handler.request.validator.ValidatorFactory
from command_handler.request.validator.exceptions import AssertionFailedException


def fooValidator(request):
    raise AssertionFailedException("Something went wrong", 418)


ValidatorFactory.addAssert("foo", fooValidator)

Command input

By default route to the command input endpoint is /command. It can be configured as described in Configuration section.

Command input endpoint accepts only POST requests with the content matching following JSON schema:

{
    "type": "object",
    "properties": {
        "command": {
            "type": "string",
            "description": "Name of the sent command",
            "examples": [
                "foo",
                "foo.bar",
                "foo.bar.baz"
            ]
        },
        "payload": {
            "type": "object",
            "description": "Payload of the sent command",
        }
    },
    "required": [
        "command",
        "payload"
    ]
}

It is also required to send Content-Type header which value matches ^application/.*json$ regular expression.

Additional validators can be defined as specified in Validators section.

Defining handlers

Command handlers can be defined by calling addHandler method of CommandHandler instance.

ch = CommandHandler(app)

ch.addHandler(lambda payload, command: None, "foo.*", {})

The following parameters are accepted by addHandler method:

handler

Invokable object which is called when matching command has been posted.

handler function has to accept two parameters:

  1. command's payload,
  2. command's name.
command

String which command's name is matched against.

Command matching is described in Command matching section.

schema

JSON schema object used to validate command's payload.

transformer = None

transformer parameter can be used to transform command's payload before passing it to the handler.

It gets command's payload as only parameter and returns object passed to the handler as payload.

postProcessor = None

Invokable object which is called when handler processing is done.

It has to accept same parameters as handler. If transformer was passed to the addHandler call it has additional named parameter origPayload which contains original payload passed to the invoker.

If inner handler returns any value, it is passed as innerResult named parameter.

Raising exceptions

There is no possibility of changing response of a command input endpoint except raising an exception.

When command_handler.request.exceptions.InvalidRequestException is raised command input endpoint returns response with HTTP code specified during InvalidRequestEndpoint creation (by default it is 400: Bad Request) and message which matches following JSON schema:

{
    "type": "object",
    "properties": {
        "type": "string",
        "description": "Message passed to the `InvalidRequestException` during creation",
        "default": ""
    },
    "required": [
        "error"
    ]
}

It is preferred to use 4xx codes with InvalidRequestException.

Following snippet will respond with 418: I'm a teapot HTTP code and {"error": "I'm a teapot handler"} when foo.bar command is sent:

def fooHandler(payload, command):
    raise InvalidRequestException("I'm a teapot handler", code=418)

ch = CommandHandler(app)
ch.addHandler(fooHandler, "foo.bar", {})

When any other exception is raised command input endpoint returns response with HTTP code 500: Internal server error.

Command matching

Each command name sent to command input endpoint has to be a list of words delimited by dots.

Command name passed to addHandler method must also be in the same form. There are two special words for command name assigned to the handler:

  • * can substitute exactly one word,
  • # can substitute zero or more words.

Command handler invoker will call handler which matches command name of handler passed to addHandler method. If there is no matching handler command input endpoint will respond with 500: Internal server error.

If there is more than one match the handler which was added first will be called. However handler's registry is not allowing to add handler which is assigned to the already covered name. It means that order of adding handler matters.

For example following snippet will work properly but adding those handlers in reverse order will cause in raising an exception.

ch.addHandler(lambda payload, command: None, "foo.bar", {})
ch.addHandler(lambda payload, command: None, "foo.#", {})

Validators

Command Handler contains following predefined request validators:

command

Validator which verifies if:

  • request contains payload and command fields,
  • command field value is a string,
  • payload field value is a dictionary,
  • value of command field is matchable to defined handlers,
  • value of payload field matches schema which had been assigned to the handler,
json

Validator which verifies if:

  • request contains Content-Type header with value matching to ^application/.*json$ regex,
  • request content is json-parseable.
privateIp

Validator which verifies if remote IP address of the request is private.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

command_handler-1.2.1.tar.gz (16.4 kB view details)

Uploaded Source

Built Distribution

command_handler-1.2.1-py3-none-any.whl (20.6 kB view details)

Uploaded Python 3

File details

Details for the file command_handler-1.2.1.tar.gz.

File metadata

  • Download URL: command_handler-1.2.1.tar.gz
  • Upload date:
  • Size: 16.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.8

File hashes

Hashes for command_handler-1.2.1.tar.gz
Algorithm Hash digest
SHA256 bfdd77830b45e09546e6efb404d6a433d4dc7f462fd70fe957e90d8e2a857e3d
MD5 20101f60290f85cf97d195264f6f1c6d
BLAKE2b-256 7c786605e7ce9c4993ae88143bd638654a930250137e4a3c6ff7bb6ffac1fab9

See more details on using hashes here.

File details

Details for the file command_handler-1.2.1-py3-none-any.whl.

File metadata

  • Download URL: command_handler-1.2.1-py3-none-any.whl
  • Upload date:
  • Size: 20.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.8

File hashes

Hashes for command_handler-1.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fa40126ee14bbd210e3fdc48f8bb37a81c06c62f28d71725a8ae5d996f8dcfec
MD5 4c64c826572709eba467e56c82a37c3c
BLAKE2b-256 1b075f6500045f9ce587a7aee39ed5fa496a0daafc7c4f6912cbcc1b28ddb160

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page