API development made easy: a smart Python 3 API framework

Overview

appkernel - API development made easy

alt build_status alt issues alt coverage GitHub license

What is Appkernel?

A super-easy to use API framework, enabling API creation from zero to production within minutes (no kidding: literally within minutes).

It provides data serialisation, transformation, validation, security, ORM, RPC and service mash functions out of the box (check out the roadmap for more details).

... and finally give a vote on awesome-python if you like the project, so it gets added to the list of RESTful python frameworks. Only 15 more votes are missing :)

Installation

    pip install appkernel

Crash Course

Let's build an awseome mini identity service:

class User(Model, MongoRepository):
    # define the resource schema as class meta data
    id = Property(str)
    name = Property(str, index=UniqueIndex)
    email = Property(str, validators=[Email], index=UniqueIndex)
    password = Property(str, converter=content_hasher(), omit=True)
    roles = Property(list, sub_type=str, default_value=['Login'])

    @classmethod
    def before_post(cls, *args, **kwargs):
        # this method is automatically called before persisting the instance
        # one can use after_post for hook after the persistence.
        user = kwargs.get('model')
        print(f'going to create the following user: {user}')



if __name__ == '__main__':
    # let's expose the user resource
    kernel = AppKernelEngine()
    kernel.register(User)

    # let's create and persist a sample user
    user = User(name='Test User', email='[email protected]', password='some pass')
    user.save()

    # and we are all set
    kernel.run()

That's all folks, our user service is ready to roll, the entity is saved, we can re-load the object from the database, or we can request its json schema for validation, or metadata to generate an SPA (Single Page Application). Of course validation and some more goodies are built-in as well :)

Retrieving our our User, using HTTP requests

GET request:

curl -i -X GET \
 'http://127.0.0.1:5000/users/'

And the result:

{
  "_items": [
    {
      "_type": "User",
      "email": "[email protected]",
      "id": "U0590e790-46cf-42a0-bdca-07b0694d08e2",
      "name": "Test User",
      "roles": [
        "Login"
      ]
    }
  ],
  "_links": {
    "self": {
      "href": "/users/"
    }
  }
}

Adding extra and secure methods using the @action decorator is easy as well:

@action(method='POST', require=[CurrentSubject(), Role('admin')])
def change_password(self, current_password, new_password):
    if not pbkdf2_sha256.verify(current_password, self.password):
        raise ServiceException(403, _('Current password is not correct'))
    else:
        self.password = new_password
        self.save()
    return _('Password changed')

The example above exposes the http://base_url/users/<user_id>/change_password endpoint and allows the user with admin role or the user with the current user_id to call it.

Create additional hooks, which are called before and after a HTTP method is executed, by simply adding a static method to the Model class following the convention: before_{http_method} and after_{http_method}:

Example:

@classmethod
def before_post(cls, *args, **kwargs):
    user = kwargs.get('model')
    print(f'going to create this user: {user}')

or inspect (and alter) the already persisted object:

@classmethod
def after_post(cls, *args, **kwargs):
    user = kwargs.get('model')
    print(f'this user was created: {user}')

We can also call other services using the built-in REST client proxy. In the snippet bellow we call the reservations endpoint on the Inventory service, by POST-ing a Reservation object.

    client = HttpClientServiceProxy('http://127.0.0.1:5000/')
    status_code, rsp_dict = client.reservations.post(Reservation(order_id=order.id, products=order.products))

Some features of the REST endpoint

  • GET /users/12345 - retrieve a User object by its database ID;
  • GET /users/?name=Jane&email=[email protected] - retrieve the User named Jane with e-mail address [email protected];
  • GET /users/?name=Jane&name=John&logic=OR - retrieve Jane or John;
  • GET /users/?roles=~Admin - retrieve all users which have the role Admin;
  • GET /users/?name=[Jane,John] - retrieve all user with the name Jane or John;
  • GET /users/?inserted=>2018-01-01&inserted=<2018-12-31 - return all users created in 2018;
  • GET /users/?page=1&page_size=5&sort_by=inserted&sort_order=DESC - return the first page of 5 elements;
  • GET /users/?query={"$or":[{"name": "Jane"}, {"name":"John"}]} - return users filtered with a native Mongo Query;
  • GET /users/meta - retrieve the metadata of the User class for constructing self-generating SPAs;
  • GET /users/schema - return the Json Schema of the User class used for validating objects;

Additionally the following HTTP methods are supported:

  • POST: create a new user (or updates existing one by replacing it) using a json payload or multipart form data
  • PATCH: add or updates some fields on the User object
  • PUT: replaces a User object

A few features of the built-in ORM function

Find one single user matching the query Property:

user = User.where(name=='Some username').find_one()

Return the first 5 users which have the role "Admin":

user_generator = User.where(User.roles % 'Admin').find(page=0, page_size=5)

Or use native Mongo Query:

user_generator = Project.find_by_query({'name': 'user name'})

Atomic updates:

# reserve 10 products with product code TRS abd size M
query = StockInventory.where((StockInventory.product.code == 'TRS') & (StockInventory.product.size == ProductSize.M))
for _ in range(10):
    ...
    query.update(available=StockInventory.available - 1, reserved=StockInventory.reserved + 1)

One could extend the AuditedMongoRepository mixin instead of the MongoRepository and we would end up with 3 extra fields:

  • inserted: the date-time of insertion;
  • updated: the date-time of the last update;
  • version: the number of versions stored for this document;

Some more extras baked into the Model

Generate the ID value automatically using a uuid generator and a prefix 'U':

id = Property(..., generator=uuid_generator('U'))

Add a Unique index to the User's name property:

name = Property(..., index=UniqueIndex)

Validate the e-mail property, using the NotEmpty and Email validators

email = Property(..., validators=[Email, NotEmpty])

Add schema validation to the database:

User.add_schema_validation(validation_action='error')

Hash the password and omit this attribute from the json representation:

password = Property(..., converter=content_hasher(rounds=10), omit=True)

Run the generators on the attributes and validate the object (usually not needed, since it is implicitly called by save and dumps methods):

user.finalise_and_validate()

Security is also part of the mix

The following snippet shows the declarative way of access control:

user_service = kernel.register(User, methods=['GET', 'PUT', 'POST', 'PATCH', 'DELETE'])
user_service.deny_all().require(Role('user'), methods='GET').require(Role('admin'),
                                                                         methods=['PUT', 'POST', 'PATCH', 'DELETE'])
  1. user_service.deny_all(): by default access to all methods is forbidden;
  2. require(Role('user'), methods='GET'): GET methods can be used by users having the Role: user (basic login role);
  3. require(Role('admin'), methods=['PUT', 'POST', 'PATCH', 'DELETE']): one needs the Role: admin in order to call other http methods;

I want to know the current status of the project

For more details feel free to check out the documentation

What are we building here?

The vision of the project is to provide you with a full-fledged microservice chassis, as defined by Chris Richardson to help creating beautiful APIs quikly and efficiently.

How does it helps you?

We've spent the time on analysing the stack, made the hard choices for you in terms of Database/ORM/Security/Rate Limiting and so on, so you don't have to. You can focus entirely on delivering business value from day one and being the rockstar of your project.

Currently supported (and fully tested) features:

  • REST endpoints over HTTP
  • Full range of CRUD operations
  • Customizable resource endpoints
  • Customizable, multiple item endpoints
  • Filtering and Sorting
  • Pagination
  • Data Validation
  • Extensible Data Validation
  • Default Values
  • Projections
  • Embedded Resource Serialization
  • Custom ID Fields
  • MongoDB Aggregation Framework
  • Powered by Flask

Contribute

Be part of the development: contribute to the project :)

Why did we built this?

  • We had the need to build a myriad of small services in our daily business, ranging from data-aggregation pipelines, to housekeeping services and other process automation services. These do share similar requirements and the underlying infrastructure needed to be rebuilt and tested over and over again. The question arose: what if we avoid spending valuable time on the boilerplate and focus only on the fun part?

  • Often time takes a substantial effort to make a valuable internal hack or proof of concept presentable to customers, until it reaches the maturity in terms reliability, fault tolerance and security. What if all these non-functional requirements would be taken care by an underlying platform?

  • There are several initiatives out there (Flask Admin, Flask Rest Extension and so), which do target parts of the problem, but they either need substantial effort to make them play nice together, either they feel complicated and uneasy to use. We wanted something simple and beautiful, which we love working with.

These were the major driving question, which lead to the development of App Kernel.

How does it works?

AppKernel is built around the concepts of Domain Driven Design. You can start the project by laying out the model. The first step is to define the validation and data generations rules. For making life easier, one can also set default values. Than one can extend several built-in classes in order to augment the model with extended functionality:

  • extending the Repository class (or its descendants) adds and ORM persistency capability to the model;
  • extending the Service class (or its descendants) add the capablity to expose the model over REST services;
You might also like...
Tracking development of the Class Schedule Siri Shortcut, an iOS program that checks the type of school day and tells you class scheduling.

Class Schedule Shortcut Tracking development of the Class Schedule Siri Shortcut, an iOS program that checks the type of school day and tells you clas

Structured, dependable legos for starknet development.

Structured, dependable legos for starknet development.

Traditionally, there is considerable friction for developers when setting up development environments

This self-led, half-day training will teach participants the patterns and best practices for working with GitHub Codespaces

World Happiness Report is a publication of the Sustainable Development Solutions Network

World-Happiness-Report We are going to visualise what are the factors and which

Gba-free-fonts - Free font resources for GBA game development
Gba-free-fonts - Free font resources for GBA game development

gba-free-fonts Free font resources for GBA game development This repo contains m

Simple and easy to use python API for the COVID registration booking system of the math department @ unipd (torre archimede)

Simple and easy to use python API for the COVID registration booking system of the math department @ unipd (torre archimede). This API creates an interface with the official browser, with more useful functionalities.

This repository provides a set of easy to understand and tested Python samples for using Acronis Cyber Platform API.

Base Acronis Cyber Platform API operations with Python !!! info Copyright © 2019-2021 Acronis International GmbH. This is distributed under MIT licens

Driving lessons made simpler. Custom scheduling API built with Python.
Driving lessons made simpler. Custom scheduling API built with Python.

NOTE This is a mirror of a GitLab repository. Dryvo Dryvo is a unique solution for the driving lessons industry. Our aim is to save the teacher’s time

A PG3D API Made with Python

PG3D Python API A Pixel Gun 3D Python API (Public Ver) Features Count: 29 How To Use? import api as pbn Examples pbn.isBanned(192819483) - True pbn.f

Comments
  • Logo Design

    Logo Design

    Hi. I am a graphic designer. I volunteer to design a logo for open source projects. I can design it for you to use it in the readme file. What dou you say?

    opened by Batarian711 4
  • print() is a function in Python 3

    print() is a function in Python 3

    flake8 testing of https://github.com/accelero-cloud/appkernel on Python 3.6.3

    $ flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics

    ./appkernel/compat.py:10:1: F822 undefined name 'unicode' in __all__
    __all__ = ('bytes', 'set', 'unicode', 'long', 'unichr', 'queue')
    ^
    ./appkernel/compat.py:10:1: F822 undefined name 'unichr' in __all__
    __all__ = ('bytes', 'set', 'unicode', 'long', 'unichr', 'queue')
    ^
    ./appkernel/engine.py:44:25: F821 undefined name 'iterable'
                return list(iterable)
                            ^
    ./appkernel/hacks/hacks.py:16:48: E999 SyntaxError: invalid syntax
            print "Creating table %s with fields %s" % (self.table_name, self.fields)
                                                   ^
    ./appkernel/hacks/ioc.py:137:22: E999 SyntaxError: invalid syntax
                print self.prefix, message
                         ^
    ./tutorials/metaprogramming/order_processing_b.py:43:43: E999 SyntaxError: invalid syntax
            print '>> Audit time: %r  %2.2f ms' % (method.__name__, (te - ts) * 1000)
                                              ^
    ./tutorials/metaprogramming/order_processing_c.py:42:39: E999 SyntaxError: invalid syntax
        print '>> Audit time: %r  %2.2f ms' % (wrapped_function.__name__, (te - ts) * 1000)
                                          ^
    4     E999 SyntaxError: invalid syntax
    1     F821 undefined name 'iterable'
    2     F822 undefined name 'unicode' in __all__
    7
    
    opened by cclauss 1
  • Config file for pyup.io

    Config file for pyup.io

    Hi there and thanks for using pyup.io!

    Since you are using a non-default config I've created one for you.

    There are a lot of things you can configure on top of that, so make sure to check out the docs to see what I can do for you.

    opened by pyup-bot 0
  • Initial Update

    Initial Update

    The bot created this issue to inform you that pyup.io has been set up on this repo. Once you have closed it, the bot will open pull requests for updates as soon as they are available.

    opened by pyup-bot 0
Releases(v1.2.4)
  • v1.2.4(Sep 15, 2018)

  • v1.2.0(Sep 14, 2018)

    • production ready gevent based WSGI server is initialised (if pip install gevent is issued prior to launching the service)
    • simplified service initialistion
    • unified and improved logging
    • additional documentation regarding installation and integration into own project
    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(Sep 9, 2018)

    Changes

    • bugfixes on the validation framework
    • possible to expose services which do not inherit from the Model class
    • @link decorator renamed to @action
    • introduced @resource decorator for easy method exposure on plain classes
    • http rpc client
    • complete tutorial is added to the project
    • bulk inserts
    • atomic updates
    • extended Query methods (update_one, find_one_and_update, etc.)
    Source code(tar.gz)
    Source code(zip)
  • v1.0(Jul 8, 2018)

    Model

    • [x] validation on the data model using multiple custom validators
    • [x] json serialisation support
    • [x] json schema generator
    • [x] value generators
    • [x] value converters
    • [x] wire-format marshaller
    • [x] omitted fields

    ORM Feature

    • [x] basic CRUD (create/update/delete) operations
    • [x] easy to use active record style queries
    • [x] automatically generated prefixed database ID
    • [x] index management (unique index, text index, etc.) on the database
    • [x] database schema validation and schema management
    • [x] builtin converters for serialising or deserialising the model to and from various other formats
    • [x] audited fields (eg. automatically added created, updated, updated_by fields)
    • [x] document versioning
    • [x] Bulk Inserts

    REST Service Endpoints

    • [x] REST services (GET, PUT, POST, PATCH, DELETE)
    • [x] HATEOAS actions on model
    • [x] model metadata and json schema
    • [x] URL query interface
    • [x] Read-only by default
    • [x] role based account management (RBAC)
    • [x] basic authentication and JWT token support
    • [x] customised, machine readable error messages
    Source code(tar.gz)
    Source code(zip)
Rick Astley Language is a rick roll oriented, dynamic, strong, esoteric programming language.

Rick Roll Language / Rick Astley Language A rick roll oriented, dynamic, strong, esoteric programming language. Prolegomenon The reasons that I made t

Rick Roll Programming Language 658 Jan 09, 2023
STAC in Jupyter Notebooks

stac-nb STAC in Jupyter Notebooks Install pip install stac-nb Usage To use stac-nb in a project, start Jupyter Lab (jupyter lab), create a new noteboo

Darren Wiens 32 Oct 04, 2022
Waydroid is a container-based approach to boot a full Android system on a regular GNU/Linux system like Ubuntu.

Waydroid is a container-based approach to boot a full Android system on a regular GNU/Linux system like Ubuntu.

WayDroid 4.7k Jan 08, 2023
The presented desktop application was made to solve 1d schrodinger eqation

schrodinger_equation_1d_solver The presented desktop application was made to solve 1d schrodinger eqation. It implements Numerov's algorithm (step by

Artem Kashapov 2 Dec 29, 2021
Coinloggr - A learning resource and social platform for the coin collecting community

Coinloggr A learning resource and social platform for the coin collecting commun

John Galiszewski 1 Jan 10, 2022
An Notifier Program that Notifies you to relax your eyes Every 15 Minutes👀

Every 15 Minutes ⌛ Every 15 Minutes is an application that is used to Notify you to Relax your eyes Every 15 Minutes, This is fully made with Python a

FSP Gang s' YT 2 Oct 18, 2021
Gives criticality score for an open source project

Open Source Project Criticality Score (Beta) This project is maintained by members of the Securing Critical Projects WG. Goals Generate a criticality

Open Source Security Foundation (OpenSSF) 1.1k Dec 23, 2022
Small exercises to get you used to reading and writing Python code!

Pythonlings Welcome to Pythonlings, an automated Python tutorial program (inspired by Rustlings and Haskellings). WIP This program is still working in

鹤翔万里 5 Sep 23, 2022
It is Keqin Wang first project in CMU, trying to use DRL(PPO) to control a 5-dof manipulator to draw line in space.

5dof-robot-writing this project aim to use PPO control a 5 dof manipulator to draw lines in 3d space. Introduction to the files the pybullet environme

Keqin Wang 4 Aug 22, 2022
Get information about what a Python frame is currently doing, particularly the AST node being executed

executing This mini-package lets you get information about what a frame is currently doing, particularly the AST node being executed. Usage Getting th

Alex Hall 211 Jan 01, 2023
A stupid obfuscation thing

StupidObfuscation A stupid obfuscation thing How it works The obfuscator takes a string, splits into pieces of one, then, using the table from letter.

Echo 2 May 03, 2022
Prometheus exporter for chess.com player data

chess-exporter Prometheus exporter for chess.com player data implemented via chess.com's published data API and Prometheus Python Client Example use c

Mário Uhrík 7 Feb 28, 2022
RELATE is an Environment for Learning And TEaching

RELATE Relate is an Environment for Learning And TEaching RELATE is a web-based courseware package. It is set apart by the following features: Focus o

Andreas Klöckner 311 Dec 25, 2022
tox-gh is a tox plugin which helps running tox on GitHub Actions with multiple different Python versions on multiple workers in parallel

tox-gh is a tox plugin which helps running tox on GitHub Actions with multiple different Python versions on multiple workers in parallel. This project is inspired by tox-travis.

tox development team 19 Dec 26, 2022
Ferramenta de monitoramento do risco de colapso no sistema de saúde em municípios brasileiros com a Covid-19.

FarolCovid 🚦 Ferramenta de monitoramento do risco de colapso no sistema de saúde em municípios brasileiros com a Covid-19. Monitoring tool & simulati

Impulso 49 Jul 10, 2022
Yet another Python Implementation of the Elo rating system.

Python Implementation - Elo Rating System Yet another Python Implementation of the Elo rating system (how innovative am I right?). Only supports 1vs1

Kraktoos 5 Dec 22, 2022
External Network Pentest Automation using Shodan API and other tools.

Chopin External Network Pentest Automation using Shodan API and other tools. Workflow Input a file containing CIDR ranges. Converts CIDR ranges to ind

Aditya Dixit 9 Aug 04, 2022
Automatically load and dump your dataclasses 📂🙋

file dataclasses Installation By default, filedataclasses comes with support for JSON files only. To support other formats like YAML and TOML, filedat

Alon 1 Dec 30, 2021
A type based dependency injection framework for Python 3.9+

Alluka A type based dependency injection framework for Python 3.9+. Installation You can install Alluka from PyPI using the following command in any P

Lucina 16 Dec 15, 2022
Service for working with open data of the State Duma of the Russian Federation

Сервис для работы с открытыми данными Госдумы РФ Исходные данные из API Госдумы РФ извлекаются с помощью Apache Nifi и приземляются в хранилище Clickh

Aleksandr Sergeenko 2 Feb 14, 2022