summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-api/app.py
blob: 5041457f8e7d8d434c03ed6bcc14a2a9a0da8fb0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/usr/bin/env python3
import os

from dotenv import load_dotenv
from flask import Flask, jsonify
from flask_compress import Compress
from flask_cors import CORS
from flask_restful import Api
from marshmallow import ValidationError

from opendc.api.portfolios import Portfolio, PortfolioScenarios
from opendc.api.prefabs import Prefab, PrefabList
from opendc.api.projects import ProjectList, Project, ProjectTopologies, ProjectPortfolios
from opendc.api.scenarios import Scenario
from opendc.api.schedulers import SchedulerList
from opendc.api.topologies import Topology
from opendc.api.traces import TraceList, Trace
from opendc.auth import AuthError
from opendc.util import JSONEncoder


# Load environmental variables from dotenv file
load_dotenv()


def setup_sentry():
    """
    Setup the Sentry integration for Flask if a DSN is supplied via the environmental variables.
    """
    if 'SENTRY_DSN' not in os.environ:
        return

    import sentry_sdk
    from sentry_sdk.integrations.flask import FlaskIntegration

    sentry_sdk.init(
        integrations=[FlaskIntegration()],
        traces_sample_rate=0.1
    )


def setup_api(app):
    """
    Setup the API interface.
    """
    api = Api(app)
    # Map to ('string', 'ObjectId') passing type and format
    api.add_resource(ProjectList, '/projects/')
    api.add_resource(Project, '/projects/<string:project_id>')
    api.add_resource(ProjectTopologies, '/projects/<string:project_id>/topologies')
    api.add_resource(ProjectPortfolios, '/projects/<string:project_id>/portfolios')
    api.add_resource(Topology, '/topologies/<string:topology_id>')
    api.add_resource(PrefabList, '/prefabs/')
    api.add_resource(Prefab, '/prefabs/<string:prefab_id>')
    api.add_resource(Portfolio, '/portfolios/<string:portfolio_id>')
    api.add_resource(PortfolioScenarios, '/portfolios/<string:portfolio_id>/scenarios')
    api.add_resource(Scenario, '/scenarios/<string:scenario_id>')
    api.add_resource(TraceList, '/traces/')
    api.add_resource(Trace, '/traces/<string:trace_id>')
    api.add_resource(SchedulerList, '/schedulers/')

    @app.errorhandler(AuthError)
    def handle_auth_error(ex):
        response = jsonify(ex.error)
        response.status_code = ex.status_code
        return response

    @app.errorhandler(ValidationError)
    def handle_validation_error(ex):
        return {'message': 'Input validation failed', 'errors': ex.messages}, 400

    return api


def create_app(testing=False):
    app = Flask(__name__)
    app.config['TESTING'] = testing
    app.config['SECRET_KEY'] = os.environ['OPENDC_FLASK_SECRET']
    app.config['RESTFUL_JSON'] = {'cls': JSONEncoder}
    app.json_encoder = JSONEncoder

    # Setup Sentry if DSN is specified
    setup_sentry()

    # Set up CORS support
    CORS(app)

    # Setup compression
    compress = Compress()
    compress.init_app(app)

    # Setup API
    setup_api(app)

    return app


application = create_app(testing="OPENDC_FLASK_TESTING" in os.environ)

if __name__ == '__main__':
    application.run()