Package: apidecorators
API Decoratorsdefine REST schemas for aiohttp and mongodb.pip install apidecoratorsThis 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:fromapidecorators.apiimportjwt_auth,get,insert,has_role,update,push,pull,\validate,get_many,delete,read_access,write_access,collection,aggregate,\update_array,get_from_array,publicfromapidecorators.fieldsimportall_fieldsfromcerberusimportValidator#given this schemas_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 waydefset_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 insertasyncdefpost_demand(document,request,token):document['applicant']=token['user']returndocument# 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 getasyncdefget_demand(document,token):# the last chance to change the document that will be sent to the clientreturndocument# 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 updateasyncdefput_demand(old_doc,document,request,token):returndocument# 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 arrayasyncdefpush_budget(old_doc,document,request,token):document['offerer']=token['user']document['applicant']=old_doc['applicant']returndocument# 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')asyncdefupdate_budgets(old_doc,document,token):returndocument# get many@routes.get('/api/demand')@public@collection('demand')@read_access({'*':'*'})@get_manyasyncdefget_many_demands(col,query,token):applicant=query["applicant"]returncol.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')asyncdefget_presupuestos(document,token):#the chance to remove empty objects in arrayreturndocument# 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')@aggregateasyncdefget_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"}}}}]returncol.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.pyimportasynciofromdemandimportset_routes_demandfromaiohttpimportwebfromapidecorators.apiimportcors_factoryasyncdefhandle(loop):app=web.Application(loop=loop,middlewares=[cors_factory])routes=web.RouteTableDef()set_routes_demand(routes)app.router.add_routes(routes)awaitloop.create_server(app.make_handler(),'0.0.0.0',8888)defmain():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.ymlenvironment:
    - DB_URI=mongodb://<user>:<password>@url:port/data-base
    - DB=data-base
    - SECRET=secret