"""Application. The API application is a `flask` application. It provides simple features such as registering a url for a specific handlers. """ from abacus_common_logic.connectors.database import db from abacus_common_logic.marshalling.custom_fields import ma from abacus_common_logic.marshalling.helpers import patch_default_validation_status from abacus_common_logic.utils.users import set_flask_user_details_from_headers from flask import Flask from owslogger import flask_logger from owsrequest import flask_request from owsresponse import response from owsresponse.adaptors.flask import flaskify from pymysql.constants import CLIENT from werkzeug.exceptions import HTTPException, InternalServerError from payment.connectors.cortex_search.ext import FlaskCortexSearch from payment.logic.exceptions import EntityDoesNotExist, LogicError def create_app(config): """Create an app.""" app = Flask(config.SERVICE_NAME) app.config.from_object(config) app.url_map.strict_slashes = False setup_application_log(app, config) setup_owsrequest(app, config) setup_db(app, config) setup_marshmallow(app) setup_cortex_search(app) register_blueprints(app) register_request_handler(app) register_error_handlers(app) patch_default_validation_status() return app def setup_owsrequest(app, config): """Set up request framework.""" try: import uwsgi # noqa uwsgiRunning = True except ImportError: uwsgiRunning = False flask_request.set_rules_validator(app, 'payment/access_rules.yml') flask_request.setup( app, config.ENVIRONMENT, add_request_context=True, label_profile=True, verify_access=False, rules_file=None, access_log_only=config.ONLY_LOG_ACCESS_ERRORS, exclude_paths=[config.HEALTH_CHECK], uwsgi_cache_enabled=uwsgiRunning, ) def setup_application_log(app, config): """Set up the application log.""" flask_logger.setup( app, config.ENVIRONMENT, config.LOGGER_NAME, config.LOGGER_LEVEL, config.SERVICE_NAME, config.SERVICE_VERSION, exclude_paths=[config.HEALTH_CHECK], ) def setup_db(app, config): """Set up the database.""" app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_DATABASE_URI'] = ( 'mysql+pymysql://{}:{}@{}:{}/{}?charset=UTF8MB4'.format( config.MYSQL_DB_USER, config.MYSQL_DB_PASS, config.MYSQL_DB_HOST, config.MYSQL_DB_PORT, config.MYSQL_DB_NAME, ) ) if config.ENVIRONMENT in (config.DEV_ENVIRONMENT, config.TEST_ENVIRONMENT): app.config['SQLALCHEMY_ENGINE_OPTIONS'] = { 'connect_args': {'client_flag': CLIENT.MULTI_STATEMENTS} } db.init_app(app) def setup_marshmallow(app): """Set up marshmallow.""" ma.init_app(app) def setup_cortex_search(app): """Set up Snowflake Cortex Search.""" FlaskCortexSearch(app) def register_blueprints(app): """Register blueprints.""" from payment.blueprints.base import base_api from payment.blueprints.payment_allocation import payment_allocation_api from payment.blueprints.payment_group import payment_group_api from payment.blueprints.payment_group_payment import payment_group_payment_api from payment.blueprints.payment_group_payment_account import ( payment_group_payment_account_api, ) from payment.blueprints.payment_group_payment_batch import ( payment_group_payment_batch_api, ) from payment.blueprints.payment_group_payment_batch_account import ( payment_group_payment_batch_account_api, ) from payment.blueprints.payment_minimum import payment_minimum_api from payment.blueprints.payment_search import payment_search_api from payment.blueprints.reference_payment_type import reference_payment_type_api from payment.blueprints.reference_tax_withholding import ( reference_tax_withholding_api, ) from payment.blueprints.report_payment import ( report_payment_api, ) from payment.blueprints.report_payment_group_payment import ( report_payment_group_payment_api, ) from payment.blueprints.worksheet_account_contract_closing_balance import ( worksheet_account_contract_closing_balance_api, ) from payment.blueprints.worksheet_account_contract_payable_details import ( worksheet_account_contract_payable_details_api, ) from payment.blueprints.worksheet_account_contract_taxable_revenue import ( worksheet_account_contract_taxable_revenue_api, ) from payment.blueprints.worksheet_payable_balance_after_tax import ( worksheet_payable_balance_after_tax_api, ) from payment.blueprints.worksheet_payment_contract_advance import ( worksheet_payment_contract_advance_api, ) from payment.blueprints.worksheet_payment_custom import ( worksheet_payment_custom_api, ) from payment.blueprints.worksheet_tax_correction import worksheet_tax_correction_api from payment.blueprints.worksheet_tax_correction_vat import ( worksheet_tax_correction_vat_api, ) app.register_blueprint(base_api) app.register_blueprint(payment_allocation_api) app.register_blueprint(payment_group_api) app.register_blueprint(payment_group_payment_api) app.register_blueprint(payment_group_payment_account_api) app.register_blueprint(payment_group_payment_batch_api) app.register_blueprint(payment_group_payment_batch_account_api) app.register_blueprint(payment_minimum_api) app.register_blueprint(reference_tax_withholding_api) app.register_blueprint(report_payment_api) app.register_blueprint(report_payment_group_payment_api) app.register_blueprint(worksheet_payment_contract_advance_api) app.register_blueprint(worksheet_payment_custom_api) app.register_blueprint(worksheet_account_contract_closing_balance_api) app.register_blueprint(worksheet_payable_balance_after_tax_api) app.register_blueprint(worksheet_account_contract_payable_details_api) app.register_blueprint(worksheet_tax_correction_api) app.register_blueprint(worksheet_account_contract_taxable_revenue_api) app.register_blueprint(worksheet_tax_correction_vat_api) app.register_blueprint(reference_payment_type_api) app.register_blueprint(payment_search_api) def register_error_handlers(app): """Register error handlers.""" def entity_does_not_exist_error_handler(error): """Handle entity does not exist errors.""" return flaskify( response.create_error_response(message=str(error), status=404, code='error') ) app.register_error_handler(EntityDoesNotExist, entity_does_not_exist_error_handler) def logic_error_handler(error): """Handle logic errors.""" return flaskify( response.create_error_response(message=str(error), status=400, code='error') ) app.register_error_handler(LogicError, logic_error_handler) def http_error_handler(error): """Handle http errors.""" return flaskify( response.create_error_response( message=error.description, status=error.code, code='error' ) ) app.register_error_handler(HTTPException, http_error_handler) def exception_handler(_): """Handle exceptions.""" message = ( 'The server encountered an internal error ' 'and was unable to complete your request.' ) return flaskify(response.create_fatal_response(message)) app.register_error_handler(InternalServerError, exception_handler) def register_request_handler(app): """Register function to preprocess request data.""" @app.before_request def before_request_start(): """Parse the user info in the header into flask.g.""" set_flask_user_details_from_headers()