diff --git a/notify-service/notify-api/src/notify_api/__init__.py b/notify-service/notify-api/src/notify_api/__init__.py index 257a9a92..f8bf309f 100644 --- a/notify-service/notify-api/src/notify_api/__init__.py +++ b/notify-service/notify-api/src/notify_api/__init__.py @@ -21,6 +21,7 @@ from flask import Flask from flask_cors import CORS from google.cloud.sql.connector import Connector +from sqlalchemy import event from structured_logging import StructuredLogging from notify_api import models @@ -50,7 +51,6 @@ def getconn(db_config: DBConfig) -> object: """Create a database connection. Args: - connector (Connector): The Google Cloud SQL connector instance. db_config (DBConfig): The database configuration. Returns: @@ -66,9 +66,11 @@ def getconn(db_config: DBConfig) -> object: enable_iam_auth=True ) - cursor = conn.cursor() - cursor.execute(f"SET search_path TO {db_config.schema}") - cursor.close() + if db_config.schema: + cursor = conn.cursor() + cursor.execute(f"SET search_path TO {db_config.schema},public") + cursor.execute(f"SET LOCAL search_path TO {db_config.schema}, public;") + cursor.close() return conn @@ -81,18 +83,34 @@ def create_app(run_mode=APP_RUNNING_ENVIRONMENT): CORS(app, resources="*") + schema = app.config.get("DB_SCHEMA", "public") + if app.config["DB_INSTANCE_CONNECTION_NAME"]: db_config = DBConfig( instance_name=app.config["DB_INSTANCE_CONNECTION_NAME"], database=app.config["DB_NAME"], user=app.config["DB_USER"], ip_type="private", - schema=app.config.get("DB_SCHEMA", "public"), + schema=schema if run_mode != "migration" else None ) - app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {"creator": lambda: getconn(db_config)} + + app.config["SQLALCHEMY_ENGINE_OPTIONS"] = { + "creator": lambda: getconn(db_config), + "pool_pre_ping": True, + "pool_recycle": 300 + } db.init_app(app) + if run_mode != "migration": + with app.app_context(): + engine = db.engine + @event.listens_for(engine, "checkout") + def set_search_path_on_checkout(dbapi_connection, connection_record, connection_proxy): + cursor = dbapi_connection.cursor() + cursor.execute(f"SET search_path TO {schema},public") + cursor.close() + if run_mode == "migration": from flask_migrate import Migrate, upgrade @@ -133,4 +151,4 @@ def shell_context(): """Shell context objects.""" return {"app": app, "jwt": jwt, "db": db, "models": models} # pragma: no cover - app.shell_context_processor(shell_context) + app.shell_context_processor(shell_context) \ No newline at end of file diff --git a/notify-service/notify-delivery/.env.sample b/notify-service/notify-delivery/.env.sample index e9888c4f..bdfd3d6e 100644 --- a/notify-service/notify-delivery/.env.sample +++ b/notify-service/notify-delivery/.env.sample @@ -11,12 +11,18 @@ # API Endpoints NOTIFY_API_URL= NOTIFY_API_VERSION= - NAMEX_API_URL= - NAMEX_API_VERSION= - # Letter urls - DECIDE_BUSINESS_URL= - COLIN_URL= - CORP_FORMS_URL= - SOCIETIES_URL= - DASHBOARD_URL= \ No newline at end of file + # SQL Alchemy for REMOTE connection using service account key + NOTIFY_DATABASE_INSTANCE_CONNECTION_NAME= + NOTIFY_DATABASE_USERNAME= + NOTIFY_DATABASE_NAME= + GOOGLE_APPLICATION_CREDENTIALS= + NOTIFY_DATABASE_SCHEMA= + + # SQL Alchemy for connection either to locally running db or cloudsql proxy + NOTIFY_DATABASE_HOST= + NOTIFY_DATABASE_PORT= + NOTIFY_DATABASE_USERNAME= + NOTIFY_DATABASE_PASSWORD= + NOTIFY_DATABASE_SCHEMA= + NOTIFY_DATABASE_NAME= \ No newline at end of file diff --git a/notify-service/notify-delivery/devops/vaults.gcp.env b/notify-service/notify-delivery/devops/vaults.gcp.env index 296d6bb8..00786b81 100644 --- a/notify-service/notify-delivery/devops/vaults.gcp.env +++ b/notify-service/notify-delivery/devops/vaults.gcp.env @@ -6,6 +6,7 @@ GC_NOTIFY_SMS_TEMPLATE_ID="op://notify/$APP_ENV/gc-notify/GC_NOTIFY_SMS_TEMPLATE GC_NOTIFY_EMAIL_REPLY_TO_ID="op://notify/$APP_ENV/gc-notify/GC_NOTIFY_EMAIL_REPLY_TO_ID" NOTIFY_DATABASE_INSTANCE_CONNECTION_NAME="op://database/$APP_ENV/notify-db-gcp/NOTIFY_DATABASE_INSTANCE_CONNECTION_NAME" NOTIFY_DATABASE_USERNAME="op://database/$APP_ENV/notify-db-gcp/NOTIFY_DATABASE_USERNAME" +NOTIFY_DATABASE_SCHEMA="op://database/$APP_ENV/notify-db-gcp/NOTIFY_DATABASE_SCHEMA" NOTIFY_DATABASE_NAME="op://database/$APP_ENV/notify-db-gcp/NOTIFY_DATABASE_NAME" GCP_AUTH_KEY="op://gcp-queue/$APP_ENV/c4hnrd/COMMON_GCP_AUTH_KEY" VERIFY_PUBSUB_EMAIL="op://gcp-queue/$APP_ENV/c4hnrd/COMMON_SERVICE_ACCOUNT" diff --git a/notify-service/notify-delivery/poetry.lock b/notify-service/notify-delivery/poetry.lock index fdbf5c0d..65d1cc1f 100644 --- a/notify-service/notify-delivery/poetry.lock +++ b/notify-service/notify-delivery/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "aiofiles" @@ -1679,7 +1679,7 @@ requests = ">=2.0.0" [[package]] name = "notify-api" -version = "4.0.0" +version = "4.0.1" description = "" optional = false python-versions = "^3.12" @@ -1710,8 +1710,8 @@ structured-logging = {git = "https://github.com/bcgov/sbc-connect-common.git", b [package.source] type = "git" url = "https://github.com/bcgov/bcros-common.git" -reference = "main" -resolved_reference = "f3d85570766603e458468ad00fc03db814d5e3bd" +reference = "feature-schema-follow-up" +resolved_reference = "dee92370517e69c39ae8581db95a95aef56d54ba" subdirectory = "notify-service/notify-api" [[package]] @@ -2802,4 +2802,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "11e0a0bb8a052e75f01a6872932420057ed0a2d3854d3adb6a5a7f9beef4fd3b" +content-hash = "9462f53c7160560a95f1963ded43e7561487773305ad0ca5c9bdb50774732cf6" diff --git a/notify-service/notify-delivery/pyproject.toml b/notify-service/notify-delivery/pyproject.toml index fc689b68..00376fdc 100644 --- a/notify-service/notify-delivery/pyproject.toml +++ b/notify-service/notify-delivery/pyproject.toml @@ -9,7 +9,7 @@ packages = [{include = "notify_delivery", from = "src"}] [tool.poetry.dependencies] python = "^3.12" -notify-api = { git = "https://github.com/bcgov/bcros-common.git", subdirectory = "notify-service/notify-api", branch = "main" } +notify-api = { git = "https://github.com/bcgov/bcros-common.git", subdirectory = "notify-service/notify-api", branch = "feature-schema-follow-up" } [tool.poetry.group.test.dependencies] pytest = "^8.2.2" diff --git a/notify-service/notify-delivery/src/notify_delivery/__init__.py b/notify-service/notify-delivery/src/notify_delivery/__init__.py index de3e8eae..8e0315c0 100644 --- a/notify-service/notify-delivery/src/notify_delivery/__init__.py +++ b/notify-service/notify-delivery/src/notify_delivery/__init__.py @@ -23,6 +23,7 @@ from flask import Flask from google.cloud.sql.connector import Connector from notify_api.models import db +from sqlalchemy import event from structured_logging import StructuredLogging from notify_delivery.config import config @@ -41,6 +42,8 @@ class DBConfig: database: str user: str ip_type: str + schema: str + def getconn(connector: Connector, db_config: DBConfig) -> object: @@ -53,7 +56,7 @@ def getconn(connector: Connector, db_config: DBConfig) -> object: Returns: object: A connection object to the database. """ - return connector.connect( + conn = connector.connect( instance_connection_string=db_config.instance_name, db=db_config.database, user=db_config.user, @@ -62,11 +65,20 @@ def getconn(connector: Connector, db_config: DBConfig) -> object: enable_iam_auth=True, ) + if db_config.schema: + cursor = conn.cursor() + cursor.execute(f"SET search_path TO {db_config.schema},public") + cursor.execute(f"SET LOCAL search_path TO {db_config.schema}, public;") + cursor.close() + + return conn + def create_app(run_mode=APP_RUNNING_ENVIRONMENT): """Return a configured Flask App using the Factory method.""" app = Flask(__name__) app.config.from_object(config[run_mode]) + schema = app.config.get("DB_SCHEMA", None) if app.config["DB_INSTANCE_CONNECTION_NAME"]: connector = Connector(refresh_strategy="lazy") @@ -75,11 +87,20 @@ def create_app(run_mode=APP_RUNNING_ENVIRONMENT): database=app.config["DB_NAME"], user=app.config["DB_USER"], ip_type=app.config["DB_IP_TYPE"], + schema=schema ) app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {"creator": lambda: getconn(connector, db_config)} db.init_app(app) + with app.app_context(): + engine = db.engine + @event.listens_for(engine, "checkout") + def set_search_path_on_checkout(dbapi_connection, connection_record, connection_proxy): + cursor = dbapi_connection.cursor() + cursor.execute(f"SET search_path TO {schema},public") + cursor.close() + queue.init_app(app) register_endpoints(app) diff --git a/notify-service/notify-delivery/src/notify_delivery/config.py b/notify-service/notify-delivery/src/notify_delivery/config.py index 8aaef745..b7234f8a 100644 --- a/notify-service/notify-delivery/src/notify_delivery/config.py +++ b/notify-service/notify-delivery/src/notify_delivery/config.py @@ -34,16 +34,15 @@ class Config: DEPLOYMENT_PLATFORM = os.getenv("DEPLOYMENT_PLATFORM", "GCP") DEPLOYMENT_ENV= os.getenv("DEPLOYMENT_ENV", "production") SQLALCHEMY_TRACK_MODIFICATIONS = False + DB_NAME = os.getenv("NOTIFY_DATABASE_NAME", "") + DB_USER = os.getenv("NOTIFY_DATABASE_USERNAME", "") + DB_SCHEMA = os.getenv("NOTIFY_DATABASE_SCHEMA", "public") # POSTGRESQL if DB_INSTANCE_CONNECTION_NAME := os.getenv("NOTIFY_DATABASE_INSTANCE_CONNECTION_NAME", None): - DB_NAME = os.getenv("NOTIFY_DATABASE_NAME", "") - DB_USER = os.getenv("NOTIFY_DATABASE_USERNAME", "") SQLALCHEMY_DATABASE_URI = "postgresql+pg8000://" else: - DB_USER = os.getenv("NOTIFY_DATABASE_USERNAME", "") DB_PASSWORD = os.getenv("NOTIFY_DATABASE_PASSWORD", "") - DB_NAME = os.getenv("NOTIFY_DATABASE_NAME", "") DB_HOST = os.getenv("NOTIFY_DATABASE_HOST", "") DB_PORT = os.getenv("NOTIFY_DATABASE_PORT", "5432") SQLALCHEMY_DATABASE_URI = f"postgresql+pg8000://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"