import os from authlib.integrations.requests_client import OAuthError from connexion import FlaskApp from connexion.resolver import MethodViewResolver from flask_cors import CORS from flask_migrate import Migrate from swagger_ui_bundle import swagger_ui_3_path from werkzeug.exceptions import NotFound from werkzeug.utils import redirect from delphi_api.const import DEBUG, ENVIRONMENT from delphi_api.core.tracing import configure_logging, log_request from delphi_api.core.utility_views import HealthView from delphi_api.errors import ( ApplicationError, AuthError, BigTableError, ExternalServiceError, InvalidInputError, ProtoBinaryAttributeError, RangeError, ) from delphi_api.errors.exceptions import FeatureNotImplementedError from delphi_api.errors.handlers import ( handle_auth_error, handle_generic_error, handle_invalid_input_error, handle_not_found_error, handle_not_implemented_error, handle_proto_binary_attribute_error, handle_unchecked_exception, ) def get_app(name: str = __name__.split('.')[0]): # regarding name, see: https://flask.palletsprojects.com/en/1.1.x/api/#application-object # don't cache the logger if app is in debug mode (for testing) cache_logger = False if DEBUG else True # configure logging for the application configure_logging(cache_logger=cache_logger) # don't validate responses in stage/prod to improve performance validate_responses = ENVIRONMENT not in {'stage', 'stage-mobile', 'prod'} options = { 'swagger_path': swagger_ui_3_path, } main = FlaskApp(name, options=options, resolver=MethodViewResolver(f'delphi_api')) main.add_api('v2/swagger.yaml', strict_validation=True, validate_responses=validate_responses) main.add_api('v3/swagger.yaml', strict_validation=True, validate_responses=validate_responses) main.app.config.from_object(os.getenv('FLASK_CONFIG_MODULE', 'delphi_api.config.local')) # add CORS support CORS(main.app) from delphi_api.v3.data_models.postgres_db import db db.init_app(main.app) # expose db migrations through flask-migrate migrate = Migrate(main.app, db) from delphi_api.v3.data_models.schemas import ma ma.init_app(main.app) # log requests after completion main.app.after_request(log_request) # Register routes non-standard vs. the swagger paths main.add_url_rule('/', view_func=lambda: redirect('/v3/ui')) main.add_url_rule('/health', view_func=HealthView.get) main.add_url_rule('/v2/health', view_func=HealthView.get) # Register error handlers main.app.register_error_handler(AuthError, handle_auth_error) main.app.register_error_handler(OAuthError, handle_auth_error) main.app.register_error_handler(InvalidInputError, handle_invalid_input_error) main.app.register_error_handler(ProtoBinaryAttributeError, handle_proto_binary_attribute_error) main.app.register_error_handler(RangeError, handle_invalid_input_error) main.app.register_error_handler(BigTableError, handle_generic_error) main.app.register_error_handler(ApplicationError, handle_generic_error) main.app.register_error_handler(ExternalServiceError, handle_generic_error) main.app.register_error_handler(NotFound, handle_not_found_error) main.app.register_error_handler(FeatureNotImplementedError, handle_generic_error) main.app.register_error_handler(NotImplementedError, handle_not_implemented_error) main.app.register_error_handler(Exception, handle_unchecked_exception) return main app = get_app() flask_app = app.app