From 8030a867ece731d07b9d81400c16c6aad80ae23d Mon Sep 17 00:00:00 2001 From: ZdenekSrotyr Date: Fri, 1 May 2026 23:09:15 +0200 Subject: [PATCH] fix(admin-api): keep source_type validator permissive when primary is 'local' (bootstrap) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The strict source_type-availability validator from the prior commit broke ~12 existing tests that register tables on the default test instance (where `data_source.type` resolves to 'local' since no instance.yaml is loaded). The intent of the validator is to catch *explicit* misconfig: `type=bigquery` instance + `source_type=keboola` payload with no `data_source.keboola.*` block. The bootstrap workflow — admin sets up a fresh instance and registers a few tables before pointing at a real source — should not be gated here. Loosen the check: when `get_data_source_type()` returns 'local' (the fallback when no `data_source.type` is set), skip the rejection. The explicit mismatch case still 422s because that path resolves `configured_primary` to a real source type. Also adds an autouse keboola_instance fixture to test_journey_sync_query.py which exercises Keboola registrations through the full sync→query flow — the fixture documents the test's data-source assumption rather than relying on the bootstrap escape hatch. --- app/api/admin.py | 21 ++++++++++++++++++++- tests/test_journey_sync_query.py | 26 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/app/api/admin.py b/app/api/admin.py index 0ac8e41..325c235 100644 --- a/app/api/admin.py +++ b/app/api/admin.py @@ -1402,11 +1402,20 @@ def _validate_source_type_configured(source_type: Optional[str]) -> None: - it matches the instance's primary ``data_source.type``, OR - a non-empty ``data_source.`` block exists in the - effective `instance.yaml` (multi-source instances),OR + effective `instance.yaml` (multi-source instances), OR - it's in the small allowlist of types that don't sit under `data_source.*` at all (Jira, local — see ``_SOURCE_TYPES_INDEPENDENT_OF_DATA_SOURCE``). + Special case: when the configured primary is ``'local'`` (the default + when an instance is freshly bootstrapped and no `data_source.type` has + been set yet), the validator stays permissive — refusing registrations + here would block the first-time-setup workflow where the operator + registers a few tables against a not-yet-fully-configured instance. + The misconfiguration that this validator targets is the *explicit + mismatch*: `type=bigquery` instance + `source_type=keboola` payload + with no `data_source.keboola.*` block. That case still 422s. + A bare/None source_type is tolerated for backward compat with legacy CLI scripts; the route resolves it later against ``get_data_source_type()``. @@ -1429,6 +1438,16 @@ def _validate_source_type_configured(source_type: Optional[str]) -> None: # Truthy non-empty dict / mapping / scalar — treat as configured. return + # Bootstrap-friendliness: a primary of 'local' means the instance hasn't + # been pointed at a real source yet (or has been deliberately set to + # local-only). Don't gate registrations in that state — the operator is + # likely in the middle of first-time setup and will fill in the config + # next. The check still fires when primary is an actual source type + # (bigquery / keboola) and the requested source_type doesn't match + # AND has no secondary block. + if configured_primary == "local": + return + raise HTTPException( status_code=422, detail=( diff --git a/tests/test_journey_sync_query.py b/tests/test_journey_sync_query.py index fc42e29..00c9ff9 100644 --- a/tests/test_journey_sync_query.py +++ b/tests/test_journey_sync_query.py @@ -12,6 +12,32 @@ def _auth(token: str) -> dict: return {"Authorization": f"Bearer {token}"} +@pytest.fixture(autouse=True) +def _keboola_instance(monkeypatch): + """Keboola instance fixture — required by tests that POST + `source_type='keboola'` payloads. The register-table source-type + availability validator refuses Keboola registrations on the default + unconfigured test instance (where get_data_source_type() returns + 'local').""" + fake_cfg = { + "data_source": { + "type": "keboola", + "keboola": { + "stack_url": "https://connection.keboola.com", + "project_id": "1234", + "token_env": "KEBOOLA_STORAGE_TOKEN", + }, + }, + } + monkeypatch.setattr( + "app.instance_config.load_instance_config", lambda: fake_cfg, raising=False, + ) + from app.instance_config import reset_cache + reset_cache() + yield + reset_cache() + + @pytest.mark.journey class TestSyncAndQuery: def test_register_create_rebuild_query(self, seeded_app, mock_extract_factory):