fix(admin-api): keep source_type validator permissive when primary is 'local' (bootstrap)

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.
This commit is contained in:
ZdenekSrotyr 2026-05-01 23:09:15 +02:00
parent bc3ba0d43d
commit 8030a867ec
2 changed files with 46 additions and 1 deletions

View file

@ -1402,11 +1402,20 @@ def _validate_source_type_configured(source_type: Optional[str]) -> None:
- it matches the instance's primary ``data_source.type``, OR - it matches the instance's primary ``data_source.type``, OR
- a non-empty ``data_source.<source_type>`` block exists in the - a non-empty ``data_source.<source_type>`` 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 - it's in the small allowlist of types that don't sit under
`data_source.*` at all (Jira, local see `data_source.*` at all (Jira, local see
``_SOURCE_TYPES_INDEPENDENT_OF_DATA_SOURCE``). ``_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 A bare/None source_type is tolerated for backward compat with legacy
CLI scripts; the route resolves it later against CLI scripts; the route resolves it later against
``get_data_source_type()``. ``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. # Truthy non-empty dict / mapping / scalar — treat as configured.
return 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( raise HTTPException(
status_code=422, status_code=422,
detail=( detail=(

View file

@ -12,6 +12,32 @@ def _auth(token: str) -> dict:
return {"Authorization": f"Bearer {token}"} 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 @pytest.mark.journey
class TestSyncAndQuery: class TestSyncAndQuery:
def test_register_create_rebuild_query(self, seeded_app, mock_extract_factory): def test_register_create_rebuild_query(self, seeded_app, mock_extract_factory):