summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-api/app.py
blob: 96a1ca7a8516a103f428b0c4cde5c766aff9abde (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env python3
import mimetypes
import os

from dotenv import load_dotenv
from flask import Flask, jsonify, redirect
from flask_compress import Compress
from flask_cors import CORS
from flask_restful import Api
from flask_swagger_ui import get_swaggerui_blueprint
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 setup_swagger(app):
    """
    Setup Swagger UI
    """
    SWAGGER_URL = '/docs'
    API_URL = '../schema.yml'

    swaggerui_blueprint = get_swaggerui_blueprint(
        SWAGGER_URL,
        API_URL,
        config={
            'app_name': "OpenDC API v2"
        },
        oauth_config={
            'clientId': os.environ.get("AUTH0_DOCS_CLIENT_ID", ""),
            'additionalQueryStringParams': {'audience': os.environ.get("AUTH0_AUDIENCE", "https://api.opendc.org/v2/")},
        }
    )
    app.register_blueprint(swaggerui_blueprint)


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

    # Define YAML content type
    mimetypes.add_type('text/yaml', '.yml')

    # Setup Sentry if DSN is specified
    setup_sentry()

    # Set up CORS support
    CORS(app)

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

    setup_api(app)
    setup_swagger(app)

    @app.route('/')
    def index():
        """
        Redirect the user to the API documentation if it accesses the API root.
        """
        return redirect('docs/')

    return app


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

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