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):