Skip to main content

API Decorators

Project description

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

Project details


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

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.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page