pythonflaskeve

How to make eve create DOMAIN endpoints programmatically


I am trying to make Python Eve create different collections programmatically,

Let's say I want to expose an endpoint receiving a schema to be able to create that collection in mongo:

i.e

DOMAIN = {}

app.route('/gen')
def gen(schema):
    app.config['DOMAIN'][schema.name] = schema.def # <-- This obviously does not work, don't know how to focus it

So that via curl I could post this schema def:

curl -H "Content-Type: application/json" -d '[{"name":"test", "def": "{\"age\":\"int\"}"}]' http://localhost:5000/gen

And POST objects of this new collection(test) created

curl -H "Content-Type: application/json" -d '[{"age":5]' http://localhost:5000/test

Obviously this is just the initial problem. In order to persist it in the future I will need to save this data in mongo, and load it once application starts, so that "mongo autodefines python eve DOMAIN itself". I hope this will also be possible to achieve


Solution

  • My approach is to use custom settings for Eve() object:

    app = eve.Eve(settings=settings)

    where settings contains DOMAIN definition:

    settings = {
        "SERVER_NAME": None,
        "DEBUG": True,
    
        # MongoDB params
        "MONGO_HOST": '...',
        "MONGO_PORT": 27017,
        "MONGO_USERNAME": '...',
        "MONGO_PASSWORD": '...',
    
        .....
    
        # add to DOMAIN all collections defined in `schema`
        "DOMAIN": {
            # doc
            'app_doc': {
                'item_title': 'app_doc',
                'resource_methods': ['GET', 'POST', 'DELETE'],
                'allow_unknown': True,
                'schema': {
                    'name': {'type': 'string', 'required': True},
        ....
    }
    

    The settings variable could be modified in order to receive parameters from database (I use a collection named app_schema where I keep these custom definitions for endpoints).

    Just connect to Mongo (I use pymongo) before instantiation of Eve(), then fill settings["DOMAIN"] with all data from app_schema collection, then pass this settings variable to Eve(settings=settings). Exemple here:

    # setup Mongo connection (@see config.py - store here default schemas and DOMAIN)
    client = MongoClient(settings["MONGO_HOST"], settings["MONGO_PORT"])
    db = client.docfill
    
    # check app_schema collection
    tab_schemas = db.app_schema.find({})
    
    
    def load_settings_from_schema_collection():
        """
        Defines a new settings DOMAIN variable loaded with metadata regarding "ent_"-collections
            create other API endpoints by definitions found in schema
            this is a huge workload, as it analyzes each schemadef and create endpoints for various operations
            like GET/POST/DEL/PUT/PATCH and search
            :return:
        """
        i = 0
    
        # add to `settings` new definitions from app_schema collection
        global settings
    
        # now parse each doc and create settings table for it, the insert settings table into DOMAIN definition
        for doc in tab_schemas:
            i = i + 1
            # this name should be unique for each collection
            this_collection_name = "ent_" + doc["collection"]
            # only allow "ent_" prefixed schemas to be overridden
            this_schema_setting = {
                "datasource": {
                    "source": this_collection_name  # important ca tabela sa fie definita cu prefix
                },
                "resource_methods": ['GET', 'POST', 'DELETE'],
                "item_methods": ['GET', 'DELETE', 'PUT', 'PATCH'],
                "schema": {}
            }
    
            for fld_meta in doc["content"]:
                this_schema_setting["schema"][fld_meta] = {"type": doc["content"][fld_meta]["type"]}
    
                # is there a required option ?
                if "required" in doc["content"][fld_meta]:
                    this_schema_setting["schema"][fld_meta] = {"required": bool(doc["content"][fld_meta]["required"])}
    
            settings["DOMAIN"][this_collection_name] = this_schema_setting
    
            # output everything in settings variable to config.js (just for viewing what happens in settings)
            file = "config.js"
            with open(file, 'w') as filetowrite:
                filetowrite.write('settings = ' + json.dumps(settings, indent=4, sort_keys=True))
    
        return 1
    
    
    # load settings from schema collections in MongoDB: collection=app_schema
    load_settings_from_schema_collection()
    

    And finally, start server:

    app = eve.Eve(settings=settings)
    
    app.run(...)
    

    Hope it helps!