"""MySQL Connector. Manages interactions with MySQL. """ from contextlib import contextmanager import functools from owsresponse import response from sentry_sdk import capture_exception from sqlalchemy import create_engine from sqlalchemy import exc from sqlalchemy.orm import declarative_base from sqlalchemy.orm import sessionmaker from blacklist_manager import config ar_db_engine = create_engine(config.AR_DB_URL, **config.DB_OPTS) ar_database_session = sessionmaker(bind=ar_db_engine) ar_database_ro_session = sessionmaker(bind=ar_db_engine, autoflush=False, autocommit=False) BaseModel = declarative_base() ArModel = declarative_base() @contextmanager def ar_db_ro_session(): """Provide Read-Only Session wrapping.""" session = ar_database_ro_session() try: yield session finally: session.close() @contextmanager def ar_db_session(): """Provide transactional scope around series of operations for art_relations. Taken from http://docs.sqlalchemy.org/en/latest/orm/session_basics.html. This handles rollback and closing of session, so there is no need to do that throughout the code. Usage: with ar_database_session() as session: session.execute(query) """ session = ar_database_session() try: yield session session.commit() except: # noqa: E722 session.rollback() raise finally: session.close() def ar_db_session_wrap(func): """Wrap shared Art Relations DB Session. Creates a new session if one isn't passed in. This lets us share/pass-in a common session across multiple functions, making them all transactional. """ @functools.wraps(func) def wrapper(*args, **kwargs): session = kwargs.pop('session', None) if session: return func(*args, session=session, **kwargs) else: with ar_db_session() as session: return func(*args, session=session, **kwargs) return wrapper def wrap_db_errors(function): """Decorate the given function with logic to handle SQLAlchemy errors. If a SQLAlchemy exception is thrown, it will be caught and logged and the function will return a fatal response. Args: function (func): the function to decorate Returns: func: function decorated with error-handling logic """ @functools.wraps(function) def call_function_with_error_handling(*args, **kwargs): try: function_return = function(*args, **kwargs) except exc.SQLAlchemyError as exception: capture_exception(exception) return response.create_fatal_response() return function_return return call_function_with_error_handling