Pymacaron

Star
Logo

A python microservice framework

Reference:

Overview
Quick start
Project files
OpenAPI specification
API objects
Server code
Deployment pipeline
Docker packaging
JWT authentication
Configuration
Error handling
Asynchronous execution
Database serialisation
Testing
Monitoring

API definition with OpenAPI

Pymacaron uses OpenAPI (aka swagger) to generate Flask routes and pydantic objects classes used by your API.

You should be familiar with the OpenAPI specification and its examples.

The prefered way to store swagger files in a pymacaron project is under the apis/ directory.

Limitations

Pymacaron supports only OpenAPI version 2.

Automatic code generation

Starting a pymacaron server involves calling the methods ‘load_apis()’ and ‘start()’ on a pymacaron.API instance:

form flask import Flask
from pymacaron import API, letsgo

def start_server(port=80, debug=False):

    api = API(
        Flask(__name__))
        port=port,
        debug=debug,
    )

    api.load_apis()

    api.start()

letsgo(__name__, callback=start)

Method ‘load_apis()’ finds your microservice’s OpenAPI specifications (usually located under ‘/apis’) and does two things:

Note that ‘load_apis()’ generates the models and app files only if they are missing, or if the swagger file has been updated more recently than them.

Examples of those files can be found in pymacaron-helloworld: helloworld_models.py and helloworld_app.py.

It is recommended to commit those files together with your swagger files.

Custom OpenAPI attributes

Pymacaron extends the OpenAPI standard with the following custom attributes:

x-parent: path.to.parentclass

In a model definition, make the pydantic class of this model inherit from ‘parentclass’.

definitions:
  UserProfile:
    type: object
    description: A user profile
    x-parent: mymodule.users.UserProfileMethods
    properties:
      userId:
        description: User ID
        type: string
      name:
        description: User name
        type: string
      ...

x-nullable: true

Under a property in a model definition, indicate that if the pydantic attribute named after this property holds the value None, the json document representing the object should contain that property with the value null. If ‘x-nullable: true’ is omitted, the null property is removed from the json document.

Example:

definitions:
  UserProfile:
    type: object
    description: A user profile
    properties:
      userId:
        x-nullable: true
        description: User ID
        type: string
      name:
        description: User name
        type: string      

Then the following objects will serialize to json as follows:

>>> p = apipool.example.UserProfile(userId=None, name=None)
>>> p.to_json()
{'userId': null}

x-mandatory: true

Under a property in a model definition, indicate that this property must be set in the corresponding pydantic object.

Example:

definitions:
  UserProfile:
    type: object
    description: A user profile
    properties:
      userId:
        x-mandatory: true
        description: User ID
        type: string
      name:
        description: User name
        type: string      

Instantiating a UserProfile now requires ‘userId’ to be set:

>>> p = apipool.example.UserProfile(userId='bob')
UserProfile(userId='bob', name=None)
>>> p = apipool.example.UserProfile()
pydantic.error_wrappers.ValidationError: 1 validation errors for UserProfile
userId
 field required (type=value_error.missing)

x-bind-server: path.to.endpointmethod

In an endpoint definition, tell Flask to bind the python method ‘endpointmethod’ to this route. See example below.

x-decorate-server: path.to.endpointdecorator

In an endpoint definition, wrap the endpoint method with the method ‘endpointdecorator’. Typically used to enforce JWT authentication.

Binding an API endpoint to a Python method

For every API endpoint in your microservice, tell Pymacaron which python method to execute by setting ‘x-bind-server’ in the OpenAPI endpoint definition, as in:

/login:
  post:
    summary: Login a user.
    produces:
      - application/json
    x-bind-server: myserver.handlers.do_login
    parameters:
      - in: body
        name: body
        description: User login credentials.
        required: true
        schema:
          $ref: "#/definitions/Credentials"
    responses:
      200:
        description: API version
        schema:
          $ref: '#/definitions/TokenAndProfile'
      default:
        description: Error
        schema:
          $ref: '#/definitions/Error'

Your login method could look like:


    from pymacaron import get_model
    from pymacaron.exceptions import PyMacaronException

    def do_login(credentials):
        if not authenticate_user(credentials):
            raise PyMacaronException("Login failed")

        # Get the class representing bravado-core Welcome objects
        return get_model('LoginResponse')(
            token=generate_user_token(),
            profile=get_user_profile(),
        )

Builtin endpoints

All pymacaron services implement the following default endpoints: