Lily MicroService Framework for Humans
Project description
WARNING: this project is still undergoing some heavy changes and is still quite poorly documented so if you're interested in using it, well do that at your own risk.
Lily - microservices by humans for humans
Lily is built around:
- DDD (Domain Driven Design) = Commands + Events
- TDD+ (Test Driven Development / Documentation)
Foundations
Lily was inspired by various existing tools and methodologies. In order to understand the philosophy of Lily one must udnerstand two basic concepts:
COMMAND- is a thing one can performEVENT- is a consequence of executingCOMMAND(oneCOMMANDcan lead to many events).
In lily we define commands that are raising (python's raise) various events that are captured by the main events loop (do not confuse with node.js event loop).
Creating HTTP commands
Lily enable very simple and semantic creation of commands using various transport mechanism (HTTP, Websockets, Async) in a one unified way.
Each HTTP command is build around the same skeleton:
from lily import (
command,
Meta,
name,
Input,
Output,
serializers,
Access,
HTTPCommands,
)
class SampleCommands(HTTPCommands):
@command(
name=<NAME>,
meta=Meta(
title=<META_TITLE>,
description=<META_DESCRIPTION>,
domain=<META_DOMAIN>),
access=Access(access_list=<ACCESS_LIST>),
input=Input(body_parser=<BODY_PARSER>),
output=Output(serializer=<SERIALIZER>),
)
def <HTTP_VERB>(self, request):
raise self.event.<EXPECTED_EVENT>({'some': 'thing'})
The simplest are HTTP commands that can be defined in the following way:
from lily import (
command,
Meta,
name,
Input,
Output,
serializers,
Access,
HTTPCommands,
)
class SampleCommands(HTTPCommands):
@command(
name=name.Read(CatalogueItem),
meta=Meta(
title='Bulk Read Catalogue Items',
domain=CATALOGUE),
access=Access(access_list=['ADMIN']),
input=Input(body_parser=CatalogueItemParser),
output=Output(serializer=serializers.EmptySerializer),
)
def get(self, request):
raise self.event.Read({'some': 'thing'})
Names
FIXME: add it ...
Creating Authorizer class
Each command created in Lily can be protected from viewers who should not be
able to access it. Currently one can pass to the @command decorator
access_list which is passed to the Authorizer class.
from lily.base.events import EventFactory
class BaseAuthorizer(EventFactory):
"""Minimal Authorizer Class."""
def __init__(self, access_list):
self.access_list = access_list
def authorize(self, request):
try:
return {
'user_id': request.META['HTTP_X_CS_USER_ID'],
'account_type': request.META['HTTP_X_CS_ACCOUNT_TYPE'],
}
except KeyError:
raise self.AccessDenied('ACCESS_DENIED', context=request)
def log(self, authorize_data):
return authorize_data
But naturally it can take any form you wish. For example:
- it could expect
Authorizationheader and performBearertoken decoding - it could leverage the existence of
access_listallowing one to apply some sophisticatedauthorizationpolicy.
An example of fairly classical (jwt token based Authorizer would be):
from lily import BaseAuthorizer
from .token import AuthToken
class Authorizer(BaseAuthorizer):
def __init__(self, access_list):
self.access_list = access_list
def authorize(self, request):
try:
type_, token = request.META['HTTP_AUTHORIZATION'].split()
except KeyError:
raise self.AuthError('COULD_NOT_FIND_AUTH_TOKEN')
else:
if type_.lower().strip() != 'bearer':
raise self.AuthError('COULD_NOT_FIND_AUTH_TOKEN')
account = AuthToken.decode(token)
if account.type not in self.access_list:
raise self.AccessDenied('ACCESS_DENIED')
# -- return the enrichment that should be available as
# -- `request.access` attribute
return {'account': account}
def log(self, authorize_data):
return {
'account_id': authorize_data['account'].id
}
Notice how above custom Authorizer class inherits from BaseAuthorizer.
In order to enable custom Authorizer class one must set in the settings.py:
LILY_AUTHORIZER_CLASS = 'account.authorizer.Authorizer'
where naturally the module path would depend on a specific project set up.
Finally in order to use Authorization at the command level one must set in the @command definition:
from lily import (
command,
Meta,
name,
Output,
serializers,
Access,
HTTPCommands,
)
class SampleCommands(HTTPCommands):
@command(
name=name.Read(CatalogueItem),
meta=Meta(
title='Bulk Read Catalogue Items',
domain=CATALOGUE),
access=Access(access_list=['ADMIN']),
output=Output(serializer=serializers.EmptySerializer),
)
def get(self, request):
raise self.event.Read({'some': 'thing'})
where access entry explicitly specifies who can access a particular command, that list will be injected to the Authorizer on each request to the server.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lily-3.0.1.tar.gz.
File metadata
- Download URL: lily-3.0.1.tar.gz
- Upload date:
- Size: 1.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.1.13 CPython/3.9.13 Linux/5.13.0-1031-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1fe925c2d689c2c8efaea30a4d05763bae65a34baf95b4877136e00b93b635ca
|
|
| MD5 |
c56579bfd049f353b703eb1265ed662e
|
|
| BLAKE2b-256 |
7f559be3f5ed5b4e43caee7f28ee91c0c23b99b712b46dc0f42dc9cb694d31a8
|
File details
Details for the file lily-3.0.1-py3-none-any.whl.
File metadata
- Download URL: lily-3.0.1-py3-none-any.whl
- Upload date:
- Size: 1.3 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.1.13 CPython/3.9.13 Linux/5.13.0-1031-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4c7401d25951d24371f12dec9cb107961dd710a9d048635a55b09a687b56229
|
|
| MD5 |
3737a3dad2bfc47e92bc9bf9cda36f4c
|
|
| BLAKE2b-256 |
273e2a5d10cec660a9e47fa5ff8cd530440c46d8e46b0847dea56fc9bd75dc58
|