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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

apidecorators-0.2.6.tar.gz (6.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

apidecorators-0.2.6-py3-none-any.whl (7.5 kB view details)

Uploaded Python 3

File details

Details for the file apidecorators-0.2.6.tar.gz.

File metadata

  • Download URL: apidecorators-0.2.6.tar.gz
  • Upload date:
  • Size: 6.5 kB
  • Tags: Source
  • Uploaded using 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

File hashes

Hashes for apidecorators-0.2.6.tar.gz
Algorithm Hash digest
SHA256 b1a16d452549c1276e50344cc337f0a3477eafae2fcd2401c504620337c40f96
MD5 8eef9dddeabb8090326cfdd8ce86e484
BLAKE2b-256 66d5f8f75aadf3dd3dca663a4d75ff47df8c386730a27615bdee8be1b8cd3b8c

See more details on using hashes here.

File details

Details for the file apidecorators-0.2.6-py3-none-any.whl.

File metadata

  • Download URL: apidecorators-0.2.6-py3-none-any.whl
  • Upload date:
  • Size: 7.5 kB
  • Tags: Python 3
  • Uploaded using 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

File hashes

Hashes for apidecorators-0.2.6-py3-none-any.whl
Algorithm Hash digest
SHA256 8806527911afa77ebf9c5d488f4785d86121875b69c78065ea9be624ae4e3257
MD5 5a0a43b4506b3a3c382380c5a4f6e138
BLAKE2b-256 2ed9ebb1ae030866c394fbd36689d31311f2af254108cd89325f89ca1c363aa0

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.6 This release

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

0.0.11

2 files

0.0.10

2 files

0.0.9

2 files

0.0.8

2 files

0.0.7

2 files

0.0.6

2 files

0.0.5

2 files

0.0.4

2 files

0.0.3

2 files

0.0.2

2 files

0.0.1

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page