Skip to main content

API Decorators

define REST schemas for aiohttp and mongodb.

pip install apidecorators

This API is valid for schemas in this way:

schema = { * primitives * objects * arrays of primitives * arrays of objects }

where objects are objects of primitives and this kind of objects.

Let me explain by examples:

from apidecorators.api import jwt_auth, get, insert, has_role, update, push, pull, \
                validate, get_many, delete, read_access, write_access, collection, aggregate, \
                update_array, get_from_array, public
from apidecorators.fields import all_fields            
from cerberus import Validator

#given this schema

s_budget = {
    '_id': {'type': 'string'},
    'applicant': {'type': 'string'},
    'offerer': {'type': 'string'},
    'description': {'type': 'string', 'required': True},
    'amount': {'type': 'integer'},
    'favorite': {'type': 'boolean'},
    'comment': {'type': 'dict',
                'schema': {
                    "text": {"type": "string"},
                    "date": {"type": "float"}
                }
    }
}

s_demand = {
    '_id': {'type': 'string'},
    'applicant': {'type': 'string'},
    'description': {'type': 'string', 'required': True},
    'location': {'type': 'string'},
    'budgets': {
        'type': 'list',
        'schema': s_budget
    }
}

v_demand = Validator(s_demand)
v_budget = Validator(s_budget)

# we define a POST this way

def set_routes_demand(routes):

    @routes.post('/api/demand') # aiohttp routes
    @jwt_auth # must receive a valid JWT token
    @collection('demand') # which collection
    @write_access({'*': '*'}) # any user can write any field
    @validate(validator=v_demanda) # set the cerberus validator
    @insert # it will be an insert
    async def post_demand(document, request, token):
        document['applicant'] = token['user']
        return document  # the returned document will be written in the collection described above

# we GET a document this way:

    @routes.get('/api/demand/{_id}')
    @jwt_auth
    @collection('demand')
    # the user stores in the field applicant can read all fields minus location
    # any other user can read only description and location
    @read_access({'applicant': all_fields(s_demand) - {'location'}, '*': {'description', 'location'}})
    @get # it will be a get
    async def get_demand(document, token):
        # the last chance to change the document that will be sent to the client    
        return document

# we PUT a document this way:

    @routes.put('/api/demand/{_id}')
    @jwt_auth
    @collection('demand')
    @write_access({'applicant': {'description', 'location'}})
    @validate(update=True, validator=v_demanda) # see the attribute update=True
    @update # it will be an update
    async def put_demand(old_doc, document, request, token):      
        return document

# let see how to push to an array:

    @routes.put('/api/demand/{_id}/budgets')
    @jwt_auth
    @collection('demand')
    @write_access({'*': {'description', 'amount'}})
    @validate(validator=v_budget)
    @push('budgets') # the name of the array
    async def push_budget(old_doc, document, request, token):
        document['offerer'] = token['user']
        document['applicant'] = old_doc['applicant']      
        return document

# update an element of an array

    @routes.put('/api/demand/{_id}/budgets/{sub_id}')
    @jwt_auth
    @collection('demand')
    # the user stores in offerer field of subdocument (sub_id) can update description and amount
    # the user stores in applicant field of subdocument (sub_id) can update favorite and comment
    # if you pass root a value different from '.', that will be the root where to check users of write_access
    @write_access({'offerer': {'description', 'amount'}, 'applicant': {'favorite', 'comment'}}, root='budgets')
    @update_array('budgets')
    async def update_budgets(old_doc, document, token):      
        return document

# get many

    @routes.get('/api/demand')
    @public
    @collection('demand')
    @read_access({'*': '*'})
    @get_many
    async def get_many_demands(col, query, token):  
        applicant = query["applicant"]
        return col.find({"applicant": applicant}).skip(0).limit(10)

# get from an array

    @routes.get('/api/demanddemand/{_id}/budgets')
    @jwt_auth
    @collection('demand')
    @read_access({'offerer': {'description', 'amount'}})
    @get_from_array('budgets')
    async def get_presupuestos(document, token):   
        #the chance to remove empty objects in array   
        return document

# and you can do aggregates

    @routes.get('/api/demand/aggregates/comments')
    @public # it is not restricted by a JWT token, the user will be anonymous
    @collection('demand')
    @aggregate
    async def get_aggr_comments(col, query, token):  
        offerer = query["offerer"]
        pipeline = [
    {"$match": {"budgets.offerer": offerer}},
    {"$unwind": "$budgets"},
    {"$match": {"budgets.offerer": offerer}},
    {"$group": {"_id": "$budgets.offerer", "comments": {"$push": {
        "text": "$budgets.comment.text",
        "date": "$budgets.comment.date",
        "author": "$applicant"
        }}}}
    ]
        return col.aggregate(pipeline)

In read_access and write_access you can use dot notation and the $ for the array. Example:

@read_access({'applicant': all_fields(s_demand) | {'budgets.$.comment'}})
#app.py
import asyncio
from demand import set_routes_demand
from aiohttp import web
from apidecorators.api import cors_factory


async def handle(loop):
    app = web.Application(loop=loop, middlewares=[cors_factory])
    routes = web.RouteTableDef()

    set_routes_demand(routes)
    app.router.add_routes(routes)
    await loop.create_server(app.make_handler(), '0.0.0.0', 8888)

def main():    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(handle(loop))
    print("Server started at port 8888")
    loop.run_forever()
    loop.close()

if __name__ == '__main__':
    main()

docker-compose.yml

    environment:
    - DB_URI=mongodb://<user>:<password>@url:port/data-base
    - DB=data-base
    - SECRET=secret

Release files for apidecorators 0.2.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for apidecorators 0.2.6
File Size Uploaded
apidecorators-0.2.6.tar.gz 6.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for apidecorators 0.2.6
File Interpreter ABI Platform
apidecorators-0.2.6-py3-none-any.whl Python 3 none any Details

Total release size: 14.0 kB

Release files / apidecorators-0.2.6.tar.gz

Download URL apidecorators-0.2.6.tar.gz
Size 6.5 kB
Tags Source
SHA-256 checksum
How to use checksums
b1a16d452549c1276e50344cc337f0a3477eafae2fcd2401c504620337c40f96
BLAKE2b-256 checksum
How to use checksums
66d5f8f75aadf3dd3dca663a4d75ff47df8c386730a27615bdee8be1b8cd3b8c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/1.12.1 pkginfo/1.4.2 requests/2.20.0 setuptools/39.0.1 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.7

Release files / apidecorators-0.2.6-py3-none-any.whl

Download URL apidecorators-0.2.6-py3-none-any.whl
Size 7.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8806527911afa77ebf9c5d488f4785d86121875b69c78065ea9be624ae4e3257
BLAKE2b-256 checksum
How to use checksums
2ed9ebb1ae030866c394fbd36689d31311f2af254108cd89325f89ca1c363aa0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/1.12.1 pkginfo/1.4.2 requests/2.20.0 setuptools/39.0.1 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.7

Release history Release notifications | RSS feed

This release

0.2.6 This release

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.11

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page