Skip to content

Make the nspek postgres connection host/port/database configurable#389

Open
Odin107 wants to merge 2 commits into
mainfrom
feat/configurable-nspek-connection
Open

Make the nspek postgres connection host/port/database configurable#389
Odin107 wants to merge 2 commits into
mainfrom
feat/configurable-nspek-connection

Conversation

@Odin107

@Odin107 Odin107 commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Makes the NSPEK module's database connection target configurable. set_nspek_connection previously hardcoded localhost:5432/nspek, so NaeringsspesifikasjonWindow / NaeringsspesifikasjonTab could only ever reach a database literally named nspek on localhost. Any deployment whose NSPEK data lives in a differently named database (or on a different host/port) crashes at construction with FATAL: database "nspek" does not exist. This PR adds optional host / port / database parameters (defaulting to the previous values) and threads db_host / db_port / db_name through the module classes, mirroring the existing db_user parameter. Fully backward compatible.

Changes Made

  • src/ssb_dash_framework/modules/nspek/nspek_utils.py
    • set_nspek_connection(...) gains host="localhost", port=5432, database="nspek".
    • Extracted a small, pure _build_nspek_conn_url(...) helper that builds the conninfo URL (makes the behavior unit-testable without a live DB).
    • Removed a stray print(conn_url) debug statement that leaked the connection URL to stdout.
    • Fixed the docstring (it documented a non-existent database_url argument).
  • src/ssb_dash_framework/modules/nspek/nspek.py
    • Naeringsspesifikasjon.__init__, NaeringsspesifikasjonTab.__init__, and NaeringsspesifikasjonWindow.__init__ gain db_host / db_port / db_name (same defaults) and forward them to set_nspek_connection.
  • tests/test_nspek_connection.py (new)
    • Covers the URL builder (defaults + custom target + user encoding) and that set_nspek_connection forwards the target to ConnectionPool.

Why These Changes Were Needed

The NSPEK module opens its own pooled connection, separate from the app's main set_postgres_connection, and that connection was pinned to localhost:5432/nspek. In an environment where the relevant Postgres instance (e.g. behind a CloudSQL proxy on localhost:5432) does not contain a database called nspek, instantiating NaeringsspesifikasjonWindow(...) aborts the whole app build with a PoolTimeout whose underlying cause is FATAL: database "nspek" does not exist. There was no way to point the module at the correct database short of monkeypatching set_nspek_connection. Exposing the connection target makes the module usable across deployments without forking the framework.

Implementation Details

  • Backward compatible by construction: every new parameter defaults to the previously hardcoded value (localhost / 5432 / nspek), so existing callers - including the two other internal call sites (nspek_controls.py and the __main__ demo in nspek_utils.py) - are unaffected.
  • Pure URL builder: _build_nspek_conn_url isolates the postgresql://{user}@{host}:{port}/{database} construction (with quote_plus user encoding) so it can be asserted directly in a unit test, avoiding the live-DB validation that set_nspek_connection performs at the end.
  • Consistent plumbing: db_host / db_port / db_name follow the exact pattern of the existing db_user parameter through NaeringsspesifikasjonTab / Window.

Code Changes

nspek_utils.py - configurable target + extracted builder, debug print removed:

-def set_nspek_connection(database_user: str | None = None) -> None:
-    """Helper function to configure a pooled connection to a postgres database.
+def _build_nspek_conn_url(
+    database_user: str, host: str = "localhost", port: int = 5432, database: str = "nspek"
+) -> str:
+    """Build the psycopg ``conninfo`` URL for the nspek postgres connection."""
+    encoded_user = quote_plus(database_user)
+    return f"postgresql://{encoded_user}@{host}:{port}/{database}"
+
+
+def set_nspek_connection(
+    database_user: str | None = None,
+    host: str = "localhost",
+    port: int = 5432,
+    database: str = "nspek",
+) -> None:
+    """Helper function to configure a pooled connection to the nspek postgres database. ..."""
     ...
-    encoded_user = quote_plus(DB_USER)
-    conn_url = f"postgresql://{encoded_user}@localhost:5432/nspek"
-    if DB_USER.startswith("nspek-developers"):
-        print(conn_url)
+    conn_url = _build_nspek_conn_url(DB_USER, host=host, port=port, database=database)

nspek.py - threaded through the module classes (window shown; tab/base identical):

-    def __init__(self, time_units: list[str], db_user: str | None = None, **kwargs: Any) -> None:
+    def __init__(
+        self,
+        time_units: list[str],
+        db_user: str | None = None,
+        db_host: str = "localhost",
+        db_port: int = 5432,
+        db_name: str = "nspek",
+        **kwargs: Any,
+    ) -> None:
         """Initializes the NaeringsspesifikasjonWindow class."""
-        Naeringsspesifikasjon.__init__(self, time_units=time_units, db_user=db_user)
+        Naeringsspesifikasjon.__init__(
+            self, time_units=time_units, db_user=db_user,
+            db_host=db_host, db_port=db_port, db_name=db_name,
+        )
         WindowImplementation.__init__(self, **kwargs)

Example downstream usage - point the NSPEK tab at the deployment's actual database:

-NaeringsspesifikasjonWindow(time_units=perioder)
+NaeringsspesifikasjonWindow(
+    time_units=perioder,
+    db_host=settings.database_host,
+    db_port=settings.database_port,
+    db_name=settings.dapla_team,
+)

Testing / Validation

  • New tests/test_nspek_connection.py (no live DB; the post-construction validation is satisfied with a MagicMock(spec=BaseBackend)):
    • test_build_nspek_conn_url_defaults / test_build_nspek_conn_url_custom_target - URL is built correctly, user is quote_plus-encoded.
    • test_set_nspek_connection_forwards_target_to_pool - host/port/database reach ConnectionPool(conninfo=...).
    • test_set_nspek_connection_defaults_unchanged - omitting the args yields the original localhost:5432/nspek URL.
    • Result: 4 passed.
  • Both edited modules compile; inspect.signature confirms db_host/db_port/db_name are present on all three classes with "nspek"/5432/"localhost" defaults.

Reviewer Notes

  • No behavioral change by default - every default equals the previously hardcoded value.
  • Scope: only the Naeringsspesifikasjon class family is threaded (the user-facing Window/Tab). The other set_nspek_connection caller in nspek_controls.py keeps the defaults; if you'd like the NSPEK controls to be configurable too, that's a small parallel follow-up.
  • Design question: the NSPEK module maintains its own pool, separate from the app's main set_postgres_connection. Making the target configurable is the minimal fix; if NSPEK data generally lives in the same database as the rest of the app, an alternative worth considering is letting the module reuse the main connection instead of opening its own. Happy to take this whichever direction you prefer.

ssb-djo and others added 2 commits June 24, 2026 16:59
set_nspek_connection hardcoded the connection target to localhost:5432/nspek, so
the NaeringsspesifikasjonWindow/Tab modules could only ever reach a database
literally named "nspek" on localhost. Deployments whose nspek data lives in a
differently named database (or a different host/port) fail at construction with
`FATAL: database "nspek" does not exist`.

Add host/port/database parameters to set_nspek_connection (defaulting to the
previous localhost:5432/nspek values) and thread db_host/db_port/db_name through
Naeringsspesifikasjon and its Tab/Window subclasses, mirroring the existing
db_user parameter. Extract the conninfo construction into a small, testable
_build_nspek_conn_url helper and drop a stray print() of the connection URL.

Defaults preserve current behavior; the other internal callers (nspek_controls,
the __main__ demo) are unaffected.
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
E Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

💡 Need a hand with PR review? Try Gitar by Sonar!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant