Passed
Pull Request — master (#406)
by Jace
01:24
created

configure_exceptions()   A

Complexity

Conditions 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
ccs 5
cts 5
cp 1
cc 2
crap 2
rs 9.4285
1 1
import os
2 1
import logging
3 1
from pathlib import Path
4 1
from urllib.parse import urlencode, unquote
5
6 1
from flask import request, current_app, url_for
7 1
from flask_api import FlaskAPI
8 1
from flask_api.exceptions import APIException, NotFound
9
import bugsnag
10 1
from bugsnag.flask import handle_exceptions
11 1
12 1
from . import extensions
13 1
from . import services
14
from . import stores
15
from . import routes
16 1
17
18
log = logging.getLogger('api')
19 1
20 1
21
class TemplateNotFound(NotFound):
22
    detail = "Template not found."
23 1
24 1
25
class InvalidMaskedCode(NotFound):
26
    detail = "Masked URL does not match any image."
27 1
28 1
29 1
class FilenameTooLong(APIException):
30
    status_code = 414
31
    detail = "Filename too long."
32 1
33 1
34 1
class InvalidImageLink(APIException):
35
    status_code = 415
36
    detail = "Unsupported image type."
37 1
38 1
39 1
def create_app(config):
40
    app = FlaskAPI(__name__)
41 1
    app.config.from_object(config)
42
43 1
    configure_exceptions(app)
44 1
    configure_logging(app)
45 1
46
    register_extensions(app)
47 1
    register_services(app)
48
    register_blueprints(app)
49 1
50
    enable_cache_busting(app)
51
52 1
    return app
53 1
54
55 1
def configure_exceptions(app):
56 1
    if app.config['BUGSNAG_API_KEY']:  # pragma: no cover
57 1
        bugsnag.configure(
58
            api_key=app.config['BUGSNAG_API_KEY'],
59
            project_root=app.config['ROOT'],
60 1
        )
61 1
        handle_exceptions(app)
62
63
64 1
def configure_logging(app):
65 1
    logging.basicConfig(level=app.config['LOG_LEVEL'],
66
                        format="%(levelname)s: %(message)s")
67
    logging.getLogger('yorm').setLevel(logging.WARNING)
68
    logging.getLogger('requests').setLevel(logging.WARNING)
69
    logging.getLogger('PIL').setLevel(logging.INFO)
70
71
72 1
def register_extensions(app):
73 1
    extensions.cors.init_app(app, methods=['GET', 'OPTIONS'], allow_headers='*')
74
75 1
76 1
def register_services(app):
77
    exceptions = services.Exceptions(
78 1
        TemplateNotFound,
79 1
        InvalidMaskedCode,
80
        FilenameTooLong,
81 1
        InvalidImageLink,
82
    )
83
84
    templates_root = os.path.join(app.config['ROOT'], 'data', 'templates')
85 1
    template_store = stores.template.TemplateStore(templates_root)
86
87
    fonts_root = os.path.join(app.config['ROOT'], 'data', 'fonts')
88
    font_store = stores.font.FontStore(fonts_root)
89 1
90
    images_root = os.path.join(app.config['ROOT'], 'data', 'images')
91
    image_store = stores.image.ImageStore(images_root, app.config)
92
93 1
    app.link_service = services.link.LinkService(
94
        exceptions=exceptions,
95
        template_store=template_store,
96
    )
97
    app.template_service = services.template.TemplateService(
98
        exceptions=exceptions,
99
        template_store=template_store,
100 1
    )
101 1
    app.font_service = services.font.FontService(
102 1
        exceptions=exceptions,
103 1
        font_store=font_store
104 1
    )
105 1
    app.image_service = services.image.ImageService(
106
        exceptions=exceptions,
107 1
        template_store=template_store,
108
        font_store=font_store,
109 1
        image_store=image_store,
110
    )
111
112 1
    def log_request(response):
113 1
        if current_app.debug:
114 1
            path = request.path
115 1
            if request.args:
116 1
                path += "?%s" % unquote(urlencode(request.args))
117 1
            log.info("%s: %s - %i", request.method, path,
118 1
                     response.status_code)
119 1
        return response
120 1
121 1
    app.after_request(log_request)
122 1
123 1
124 1
def register_blueprints(app):
125
    app.register_blueprint(routes.api_aliases.blueprint)
126
    app.register_blueprint(routes.api_fonts.blueprint)
127 1
    app.register_blueprint(routes.api_legacy.blueprint)
128
    app.register_blueprint(routes.api_links.blueprint)
129 1
    app.register_blueprint(routes.api_root.blueprint)
130 1
    app.register_blueprint(routes.api_search.blueprint)
131 1
    app.register_blueprint(routes.api_templates.blueprint)
132 1
    app.register_blueprint(routes.custom.blueprint)
133 1
    app.register_blueprint(routes.image.blueprint)
134 1
    app.register_blueprint(routes.index.blueprint)
135 1
    app.register_blueprint(routes.latest.blueprint)
136
    app.register_blueprint(routes.static.blueprint)
137 1
138
139
def enable_cache_busting(app):
140
141
    def dated_url_for(endpoint, **values):
142
        if endpoint == 'static':
143
            filename = values.get('filename', None)
144
            if filename:
145
                path = Path(app.root_path, endpoint, filename)
146
                values['q'] = int(path.stat().st_mtime)
147
        return url_for(endpoint, **values)
148
149
    app.context_processor(lambda: dict(url_for=dated_url_for))
150