Metadata-Version: 2.1
Name: apidecorators
Version: 0.2.5
Summary: API Decorators
Home-page: https://gitlab.com/miguel.alarcos/apidecorators
Author: Miguel Ángel Alarcos Torrecillas
Author-email: miguel.alarcos@gmail.com
License: UNKNOWN
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:
        
        ```python
        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:
        
        ```python
        @read_access({'applicant': all_fields(s_demand) | {'budgets.$.comment'}})
            
        ```
        
        ```python
        #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
        ```yml
            environment:
            - DB_URI=mongodb://<user>:<password>@url:port/data-base
            - DB=data-base
            - SECRET=secret
        ```
        
        
Platform: UNKNOWN
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Description-Content-Type: text/markdown
