import json import logging import os from typing import List, Optional from unittest import mock from sqlalchemy.engine import Engine, create_engine from service.conf import settings from service.conf.secrets import get_secret logger = logging.getLogger(__name__) _engine: Optional[Engine] = None class SchemaPath: PUBLIC = "public" COMMONS = "commons" WORKSPACE = "workspace" COMPANY = "company" ALLIANCE = "alliance" @classmethod def choices(cls) -> List[str]: return [ cls.PUBLIC, cls.COMMONS, cls.WORKSPACE, cls.COMPANY, cls.ALLIANCE, ] def get_db_url(driver="postgresql+psycopg2") -> str: db_url = settings.DATABASE_URL if db_url: return db_url if settings.PROFILE == "auto_test": container_name = "database" if os.getenv("CODEPIPELINE"): # This is an ugly workaround. We didn't figure out in a timely fashion # how to rename the "src" to "data-processor" in buildsped_run-tests.yml container_name = "src_database" # The test db we setup on local container return f"{driver}://postgres:password@{container_name}:5432/test_db" # TODO! unify naming in fargate templates and secret names so you can use # get_secret(DATABASE_NAME) once if settings.DATABASE_NAME == "fansifter": secret = get_secret("fansifter-rds") elif settings.DATABASE_NAME == "fansifter-dev": # For backwards compatibility, temporarily duplicates the above # (until we've renamed the actual RDS). secret = get_secret("fansifter-rds") elif settings.DATABASE_NAME == "fansifter-live": secret = get_secret("fansifter-rds-live") elif settings.DATABASE_NAME == "fansifter-test": secret = get_secret("fansifter-rds-test") elif settings.DATABASE_NAME == "fansifter-ztbox": secret = get_secret("fansifter-rds-ztbox") else: raise ValueError(f"Unknown database name: {settings.DATABASE_NAME}") config = json.loads(secret) params = { "driver": driver, "host": config["host"], "port": config["port"], "dbname": config["dbname"], "user": config["username"], "password": config["password"], } return "{driver}://{user}:{password}@{host}/{dbname}".format(**params) def init_rds_engine() -> Engine: # Add pool_pre_ping=True if SSL thing continues return create_engine( url=get_db_url(), executemany_mode="values_plus_batch", pool_size=20, max_overflow=100, pool_recycle=3600, ) def get_rds_engine() -> Engine: """ Get RDS connection for a given db instance """ # Return mocked SqlAlchemy engine for unit testing only if settings.DATABASE_MOCK: return mock.MagicMock(spec=Engine) global _engine if _engine is None: _engine = init_rds_engine() return _engine engine = get_rds_engine()