polymicro

PolyMicro is a Model Based Rest API system written around PynamoDD and Marshmallow for flask


Keywords
aws-lambda, dynamodb, flask, marshmallow, python3, rest-api
License
MIT
Install
pip install polymicro==0.2.0

Documentation

PolyMicro

Build Status Requirements Status codecov

PolyMicro is a Model Based Rest API system written around PynamoDB and Marshmallow for request and response marshalling and designed to work interchangeably with various different deployments.

Installation

Installation is simple.

pip install polymicro

See our example below for how easy it is to get up and runnning with polymicro.

Road Map

In the future we would like to bring the following features to PolyMicro.

  • Swappable Data Backends (For instance, supporting SQL Alchemy, etc.)
  • Configurable Response Types (Serializing to things other than json)

Example

It is as simple as one... two.... three.... (four).

Step 1: Create your PolyMicro app

from flask import Flask
from polymicro import PolyMicro

def get_request_user(self):
    # Get the user however you want. If there is no user for the request, return None
    return 'abc123'

app = Flask(__name__)
pm = PolyMicro(app, req_user_func=get_request_user)

This step revolves around setting the basics of the application. You need to create a flask app as you would do in any other flask application. Then we are going to create a function that tells PolyMicro how to get user information from the given request and pass that into the creation of our polymicro app.

Step 2: Create your PynamoDB Model

from datetime import datetime
from uuid import uuid4
from pynamodb.models import Model
from pynamodb.attributes import UnicodeAttribute, BooleanAttribute, UTCDateTimeAttribute

class Todo(Model):
    class Meta:
        table_name = 'todos'

    owner = UnicodeAttribute(hash_key=True)
    uuid = UnicodeAttribute(range_key=True, default=lambda: str(uuid4()))
    creation_date_time = UTCDateTimeAttribute(default=datetime.utcnow)

    text = UnicodeAttribute()
    done = BooleanAttribute(default=False)
    due_date_time = UTCDateTimeAttribute(null=True)

PynamoDB is a tool to write models in DynamoDB similar to an ORM. We use it to define information.

Step 3: Define Permissions (Optional)

from polymicro.permissions import Permission

class TodoPermission(Permission):
    def has_object_permission(self, user, method: str, instance):
        return instance.owner == user.uuid

    def has_permission(self, user, method: str):
        return 'administrator' in user.permissions or 'therapist' in user.permissions

Permissions are simple sets of rules from which you can define weather or not a user can access a particular resource and object. Return false if the permission proves the user cannot use the object or the method and true otherwise.

Step 4: Define the Resource

from polymicro import ModelResource, Create, List, Retrieve, Update, Delete

class TodoResource(ModelResource, Create, List, Retrieve, Update, Delete):
    model = Todo
    route = 'todo'
    permissions = (TodoPermission)
    dump_only = ('creation_date_time', 'owner', 'uuid')
    required = ('text', )

    def list(self, user, pagination_key=None, limit=25):
        return Todo.query(user.uuid, last_evaluated_key=pagination_key, limit=limit)

    def create(self, req: dict, user):
        req['owner'] = user.uuid
        return super().create(req, user)
            
TodoResource(pm)

We can define resources by composition. So first, define a class that extends from ModelResource. This class will form the base of the functionality that we will want to implement. Next, extending from the Create, List, Retrieve, Update, and Delete class will implement standard versions of those functions. You can also override the function.

These functions can return several different things.

One of these:

  • A single or collection of your model.
  • Anything that is JSON serializable

And optionally these:

  • A HTTP status code as an integer.
  • A string -> string dictionary representing extra headers.
  • For the list operation, a 4th item that allows for a pagination key to be returned in the response for pagination.

Below are some examples:

# Returning all three.
def retrieve(self, instance, user):
    return instance, 418, {'Extra': 'Something-Super-Secret'}

# Returning instance and status code
def retrieve(self, instance, user):
    return instance, 418
    
# Return just the single instance.
def retrieve(self, instance, user):
    return instance
    
# The list operation allows for a 4th item that allows for a pagination key that can be returned back later.
def list(self, user, pagination_key=None, limit=25):
    items = Todo.query(user.uuid, last_evaluated_key=pagination_key, limit=limit)
    return items, 418, {}, 'some-key'