Flongo Framework
A framework for rapid application development on Flask. Includes:
- Built-in easy HTTP request/response management and error handling
- Built-in query string and response body parsing / normalization
- Built-in request and response payload validation using JSONSchema
- Built-in CORS handling and request origin configuration
- Built-in endpoint permissions handling with cookie-based JWT
- Built-in bindings for MongoDB including fixtures and index management
- Built-in automatic CRUD handling for endpoints given a MongoDB collection name
- Built-in detailed logging and fine-tuneable configurations
- Built-in Sentry integration with detailed spans for request tracing
- Built-in Gmail utility
- Dockerfile/Docker Compose to easily build and run the application
Sample App
from flongo_framework.application import Application
from flongo_framework.api.routing import App_Routes, Route, Route_Schema, \
Route_Handler, Default_Route_Handler, Route_Permissions
from flongo_framework.api.routing.utils import Authentication_Util
from flongo_framework.api.responses import API_JSON_Response, API_Message_Response
from flongo_framework.api.responses.errors import API_Error_Message, API_Error_Response
from flongo_framework.database.mongodb.index import MongoDB_Indices, MongoDB_Index
from flongo_framework.database.mongodb.fixture import MongoDB_Fixtures, MongoDB_Fixture
from flongo_framework.config.settings import App_Settings, Flask_Settings, MongoDB_Settings
from flongo_framework.config.enums.logs.log_levels import LOG_LEVELS
from typing import Any
from bson import ObjectId
from datetime import datetime
def throw(exception_type:type, msg:Any):
raise exception_type(msg)
routes = App_Routes(
Route(
url='/request',
handler=Route_Handler(
GET=lambda request: API_Message_Response("Sample GET request"),
POST=lambda request: API_JSON_Response({'sample_record': f'{request.payload["_id"]}', 'created': True}, 201),
PUT=lambda request: API_JSON_Response({'payload': request.payload}),
DELETE=lambda request: API_JSON_Response({'date': datetime.now(), 'deleted': True}),
),
request_schema=Route_Schema(
POST={
'type': 'object',
'additionalProperties': False,
'properties': {
'_id': {'type': 'integer'}
},
'required': ['_id']
}
),
response_schema=Route_Schema(
PUT={
'type': 'object',
'additionalProperties': False,
'properties': {
'payload': {'type': 'object'}
},
'required': ['payload']
}
),
log_level=LOG_LEVELS.DEBUG
),
Route(
url='/error',
handler=Route_Handler(
GET=lambda request: throw(ValueError, "Oh no! A value error!"),
POST=lambda request: throw(API_Error_Message, "Oh no! An API error!"),
PUT=lambda request: throw(API_Error_Response, {'data': request.payload, 'error': 'Oh no!'}),
),
log_level=LOG_LEVELS.DEBUG
),
Route(
url='/database',
handler=Route_Handler(
POST=lambda request: API_Message_Response(request.collection.insert_one(request.payload) if request.collection != None else 'No collection!'),
GET=lambda request: API_Message_Response(request.collection.find_one(request.payload) if request.collection != None else 'No collection!')
),
log_level=LOG_LEVELS.DEBUG,
collection_name='sample'
),
Route(
url='/default',
handler=Default_Route_Handler(),
log_level=LOG_LEVELS.DEBUG,
collection_name='default'
),
Route(
url='/permissions',
handler=Default_Route_Handler(
GET=lambda request: Authentication_Util.set_identity_cookies(
response=API_Message_Response("Authenticated!"),
_id="1234",
username="test",
roles="user"
),
DELETE=lambda request: Authentication_Util.unset_identity_cookies(
response=API_Message_Response("Logged out!"),
)
),
log_level=LOG_LEVELS.DEBUG,
collection_name='permissions',
permissions=Route_Permissions(POST='user', PUT='admin')
),
)
settings = App_Settings(
flask=Flask_Settings(
env="local",
debug_mode=True,
log_level=LOG_LEVELS.DEBUG,
config_log_level=LOG_LEVELS.DEBUG
),
mongodb=MongoDB_Settings(
log_level=LOG_LEVELS.DEBUG
)
)
indices = MongoDB_Indices(
MongoDB_Index("sample", "name")
)
fixtures = MongoDB_Fixtures(
MongoDB_Fixture("sample", {"_id": ObjectId("652790328c73b750984aee34"), "name": "Peter"})
)
app = Application(routes=routes, settings=settings, indices=indices, fixtures=fixtures)
def get_app():
return app.app
if __name__ == '__main__':
app.run()
Running With Docker
Building
From the root directory run:
docker build -t <your_image_name> -f docker/Dockerfile .
Running
Run the server image on port 8080 from the Docker GUI or with
docker run -p 8080:8080 <your_image_name>
Note: that MongoDB must be configured on the same Docker network as the app (Use Docker Compose to do this automatically)
Running With Docker Compose
Since the server might depend on MongoDB, you can use Docker Compose to start the Dockerized application in conjuction with a Dockerized MongoDB instance
Building the Server Container
From the docker/ directory containing docker-compose.yml
, run:
docker-compose build
Running the Server + MongoDB
From the docker/ directory containing docker-compose.yml
, run the following to start the server on port 8080 and MongoDB on port 27017:
docker-compose up --force-recreate
If you are running the application with the environment configured to sandbox
or higher, the application will run using gunicorn. If you are running with it configured in a lower environment, the application will run via Flask directly and will allow hot-reloads when code is changed