diff --git a/queue_services/account-mailer/poetry.lock b/queue_services/account-mailer/poetry.lock index 3a944ff352..b20446186e 100644 --- a/queue_services/account-mailer/poetry.lock +++ b/queue_services/account-mailer/poetry.lock @@ -1,5 +1,16 @@ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +[[package]] +name = "aiofiles" +version = "24.1.0" +description = "File support for asyncio." +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, + {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, +] + [[package]] name = "aiohappyeyeballs" version = "2.4.3" @@ -646,6 +657,30 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "cloud-sql-python-connector" +version = "1.13.0" +description = "The Cloud SQL Python Connector is a library that can be used alongside a database driver to allow users with sufficient permissions to connect to a Cloud SQL database without having to manually allowlist IPs or manage SSL certificates." +optional = false +python-versions = ">=3.9" +files = [ + {file = "cloud_sql_python_connector-1.13.0-py2.py3-none-any.whl", hash = "sha256:238d963225761031648da8b208e2664a773fc27bc362538a381166297dbad687"}, + {file = "cloud_sql_python_connector-1.13.0.tar.gz", hash = "sha256:f68d40e47a36a999ba8a4586da4b14995769de52884844d7cdd4de4b6d12e87e"}, +] + +[package.dependencies] +aiofiles = "*" +aiohttp = "*" +cryptography = ">=42.0.0" +google-auth = ">=2.28.0" +Requests = "*" + +[package.extras] +asyncpg = ["asyncpg (>=0.29.0)"] +pg8000 = ["pg8000 (>=1.31.1)"] +pymysql = ["PyMySQL (>=1.1.0)"] +pytds = ["python-tds (>=1.15.0)"] + [[package]] name = "colorama" version = "0.4.6" @@ -741,6 +776,55 @@ files = [ [package.extras] toml = ["tomli"] +[[package]] +name = "cryptography" +version = "43.0.3" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"}, + {file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"}, + {file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"}, + {file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"}, + {file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"}, + {file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"}, + {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + [[package]] name = "dill" version = "0.3.9" @@ -3124,4 +3208,4 @@ test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-it [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "a6ed14626865fb0cc0c71c8d95ea8e83113f5c98737fdac7f05d95d9b26d3b60" +content-hash = "c3192e8be64d8db95f9785477ddb98a36f6e04aa12bcb45607a161fa7c59f60a" diff --git a/queue_services/account-mailer/pyproject.toml b/queue_services/account-mailer/pyproject.toml index b3b7bd811d..85da7cff62 100644 --- a/queue_services/account-mailer/pyproject.toml +++ b/queue_services/account-mailer/pyproject.toml @@ -41,6 +41,7 @@ zipp = "3.19.1" auth-api = { git = "https://github.com/bcgov/sbc-auth.git", rev = "feature-gcp-migration", subdirectory = "auth-api" } simple-cloudevent = { git = "https://github.com/daxiom/simple-cloudevent.py.git" } build-deps = { git = "https://github.com/bcgov/sbc-auth.git", rev = "feature-gcp-migration", subdirectory = "build-deps" } +cloud-sql-python-connector = "^1.13.0" [tool.poetry.group.dev.dependencies] psycopg2 = "^2.9.9" diff --git a/queue_services/account-mailer/src/account_mailer/__init__.py b/queue_services/account-mailer/src/account_mailer/__init__.py index e576ce42b5..ca4ecc4a9d 100644 --- a/queue_services/account-mailer/src/account_mailer/__init__.py +++ b/queue_services/account-mailer/src/account_mailer/__init__.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + """The Events Listener service. This module is the service worker for applying filings to the Business Database structure. @@ -18,6 +19,7 @@ from __future__ import annotations import os +from dataclasses import dataclass from auth_api.exceptions import ExceptionHandler from auth_api.models import db @@ -26,29 +28,78 @@ from auth_api.services.gcp_queue import queue from auth_api.utils.cache import cache from flask import Flask +from google.cloud.sql.connector import Connector -from account_mailer import config +from account_mailer import config as app_config from account_mailer.resources.worker import bp as worker_endpoint -def register_endpoints(app: Flask): - """Register endpoints with the flask application.""" - # Allow base route to match with, and without a trailing slash - app.url_map.strict_slashes = False +@dataclass +class DBConfig: + """Database configuration settings.""" + + unix_sock: str + database: str + user: str + password: str + - app.register_blueprint( - url_prefix='/', - blueprint=worker_endpoint, +def getconn(connector: Connector, db_config: DBConfig) -> object: + """Create a database connection. + + Args: + connector (Connector): The Google Cloud SQL connector instance. + db_config (DBConfig): The database configuration. + + Returns: + object: A connection object to the database. + """ + return connector.connect( + instance_connection_string=db_config.unix_sock.replace('/cloudsql/', ''), + ip_type='private', + user=db_config.user, + password=db_config.password, + db=db_config.database, + driver='pg8000', ) + + +def register_endpoints(app: Flask) -> None: + """Register endpoints with the Flask application. + + Args: + app (Flask): The Flask application instance. + """ + app.url_map.strict_slashes = False + app.register_blueprint(worker_endpoint, url_prefix='/') app.register_blueprint(ops_bp) def create_app(run_mode=os.getenv('DEPLOYMENT_ENV', 'production')) -> Flask: - """Return a configured Flask App using the Factory method.""" + """Return a configured Flask App using the Factory method. + + Args: + run_mode (str): The running mode of the application (e.g., production, development). + + Returns: + Flask: The configured Flask application instance. + """ app = Flask(__name__) - app.config.from_object(config.get_named_config(run_mode)) + app.config.from_object(app_config.get_named_config(run_mode)) app.config['ENV'] = run_mode + if app.config.get('DB_UNIX_SOCKET'): + connector = Connector(refresh_strategy='lazy') + db_config = DBConfig( + unix_sock=app.config.get('DB_UNIX_SOCKET'), + database=app.config.get('DB_NAME'), + user=app.config.get('DB_USER'), + password=app.config.get('DB_PASSWORD') + ) + app.config['SQLALCHEMY_ENGINE_OPTIONS'] = { + 'creator': lambda: getconn(connector, db_config) + } + db.init_app(app) flags.init_app(app) cache.init_app(app) diff --git a/queue_services/account-mailer/src/account_mailer/config.py b/queue_services/account-mailer/src/account_mailer/config.py index d418e07d29..a088bf2970 100644 --- a/queue_services/account-mailer/src/account_mailer/config.py +++ b/queue_services/account-mailer/src/account_mailer/config.py @@ -73,7 +73,7 @@ class _Config(): # pylint: disable=too-few-public-methods DB_HOST = os.getenv('DATABASE_HOST', '') DB_PORT = os.getenv('DATABASE_PORT', '5432') if DB_UNIX_SOCKET := os.getenv('DATABASE_UNIX_SOCKET', None): - SQLALCHEMY_DATABASE_URI = f'postgresql+psycopg2://{DB_USER}:{DB_PASSWORD}@/{DB_NAME}?host={DB_UNIX_SOCKET}' # noqa: E231, E501 + SQLALCHEMY_DATABASE_URI = 'postgresql+pg8000://' else: SQLALCHEMY_DATABASE_URI = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{int(DB_PORT)}/{DB_NAME}' # noqa: E231, E501