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
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
apidecorators-0.2.6.tar.gz
(6.5 kB
view details)
Built Distribution
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
Algorithm | Hash digest | |
---|---|---|
SHA256 | b1a16d452549c1276e50344cc337f0a3477eafae2fcd2401c504620337c40f96 |
|
MD5 | 8eef9dddeabb8090326cfdd8ce86e484 |
|
BLAKE2b-256 | 66d5f8f75aadf3dd3dca663a4d75ff47df8c386730a27615bdee8be1b8cd3b8c |
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
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8806527911afa77ebf9c5d488f4785d86121875b69c78065ea9be624ae4e3257 |
|
MD5 | 5a0a43b4506b3a3c382380c5a4f6e138 |
|
BLAKE2b-256 | 2ed9ebb1ae030866c394fbd36689d31311f2af254108cd89325f89ca1c363aa0 |