Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion apps/backend/src/api/routes/ingestion/staging.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
detect_column_type_from_sqla,
read_and_transform_data,
)
from data_manipulation.constants import DB_URI_PREFIX
from data_manipulation.constants import DB_URI_PREFIX, DEFAULT_GEOMETRY_COLUMN
from data_manipulation.database import schema_exists, table_exists
from data_manipulation.ingestion import read_data_from_postgis
from data_manipulation.logging import configure_logging
Expand Down Expand Up @@ -927,6 +927,12 @@ def get_staging_metadata(
MetaData(schema=schema),
autoload_with=data_engine,
)
if source_file_type in (FileType.JSON, FileType.GEOJSON):
# The extension-based guess made at submission time can be wrong for these
# two: a .geojson source with no real geometry (e.g. an OGC Features
# service that always emits GeoJSON) reads as a plain table, and vice
# versa. Report what the staging table actually turned out to be.
source_file_type = FileType.GEOJSON if DEFAULT_GEOMETRY_COLUMN in table.c else FileType.JSON
row_count = data_session.scalar(select(func.count()).select_from(table)) or 0
original_projection = _detect_original_projection(
staging_table_name,
Expand Down
23 changes: 23 additions & 0 deletions apps/backend/tests/api/routes/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from collections.abc import Generator
from types import SimpleNamespace
from unittest.mock import patch

import pytest


@pytest.fixture
def staging_metadata_deps() -> Generator[SimpleNamespace, None, None]:
"""Patch every dependency get_staging_metadata needs besides the IntegrityLink
and staging table content, which each test configures itself via .load and
.table."""
with (
patch("src.api.routes.ingestion.staging.get_staging_schema", return_value="staging"),
patch("src.api.routes.ingestion.staging.select"),
patch("src.api.routes.ingestion.staging.Table") as mock_table,
patch("src.api.routes.ingestion.staging._resolve_columns") as mock_resolve_cols,
patch("src.api.routes.ingestion.staging._detect_original_projection") as mock_detect_proj,
patch("src.api.routes.ingestion.staging.load_authorized_integrity_link") as mock_load,
):
mock_resolve_cols.return_value = ([], None)
mock_detect_proj.return_value = None
yield SimpleNamespace(load=mock_load, table=mock_table)
73 changes: 20 additions & 53 deletions apps/backend/tests/api/routes/test_staging_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Tests for API (OGC service) source type in staging endpoints."""

from datetime import date, datetime, timezone
from types import SimpleNamespace
from unittest.mock import MagicMock, patch
from uuid import uuid4

Expand All @@ -16,7 +17,19 @@
edit_staging,
get_staging_metadata,
)
from src.models.data_import import ImportType
from src.models.data_import import ImportType, StagingMetadataResponse


def _call_get_staging_metadata() -> StagingMetadataResponse:
data_session = MagicMock()
data_session.scalar.return_value = 0
return get_staging_metadata(
data_session=data_session,
datafeeder_session=MagicMock(),
geo_ctx=MagicMock(),
integrity_link_id=str(uuid4()),
group_ids=[],
)


class TestProcessImportSourceApi:
Expand Down Expand Up @@ -166,20 +179,8 @@ def test_title_falls_back_to_layer_name(

assert result.title == "ns:buildings"

@patch("src.api.routes.ingestion.staging.get_staging_schema", return_value="staging")
@patch("src.api.routes.ingestion.staging.select")
@patch("src.api.routes.ingestion.staging.Table")
@patch("src.api.routes.ingestion.staging._resolve_columns")
@patch("src.api.routes.ingestion.staging._detect_original_projection")
@patch("src.api.routes.ingestion.staging.load_authorized_integrity_link")
def test_layer_name_returned_in_response_for_api_type(
self,
mock_load: MagicMock,
mock_detect_proj: MagicMock,
mock_resolve_cols: MagicMock,
mock_table: MagicMock,
mock_select: MagicMock,
mock_get_schema: MagicMock,
self, staging_metadata_deps: SimpleNamespace
) -> None:
"""layer_name field in response matches source_layer for API import type."""
mock_link = MagicMock()
Expand All @@ -191,38 +192,15 @@ def test_layer_name_returned_in_response_for_api_type(
mock_link.source_layer = "ns:buildings"
mock_link.integrity_transformation = None
mock_link.final_table_name = None
mock_load.return_value = (mock_link, MagicMock())
mock_resolve_cols.return_value = ([], None)
mock_detect_proj.return_value = None

data_session = MagicMock()
data_session.scalar.return_value = 0
staging_metadata_deps.load.return_value = (mock_link, MagicMock())

result = get_staging_metadata(
data_session=data_session,
datafeeder_session=MagicMock(),
geo_ctx=MagicMock(),
integrity_link_id=str(uuid4()),
group_ids=[],
)
result = _call_get_staging_metadata()

assert result.title == "My WFS Layer"
assert result.layer_name == "ns:buildings"

@patch("src.api.routes.ingestion.staging.get_staging_schema", return_value="staging")
@patch("src.api.routes.ingestion.staging.select")
@patch("src.api.routes.ingestion.staging.Table")
@patch("src.api.routes.ingestion.staging._resolve_columns")
@patch("src.api.routes.ingestion.staging._detect_original_projection")
@patch("src.api.routes.ingestion.staging.load_authorized_integrity_link")
def test_layer_name_is_none_for_non_api_import_type(
self,
mock_load: MagicMock,
mock_detect_proj: MagicMock,
mock_resolve_cols: MagicMock,
mock_table: MagicMock,
mock_select: MagicMock,
mock_get_schema: MagicMock,
self, staging_metadata_deps: SimpleNamespace
) -> None:
"""layer_name is None in response for non-API import types."""
mock_link = MagicMock()
Expand All @@ -234,20 +212,9 @@ def test_layer_name_is_none_for_non_api_import_type(
mock_link.source_layer = None
mock_link.integrity_transformation = None
mock_link.final_table_name = None
mock_load.return_value = (mock_link, MagicMock())
mock_resolve_cols.return_value = ([], None)
mock_detect_proj.return_value = None
staging_metadata_deps.load.return_value = (mock_link, MagicMock())

data_session = MagicMock()
data_session.scalar.return_value = 0

result = get_staging_metadata(
data_session=data_session,
datafeeder_session=MagicMock(),
geo_ctx=MagicMock(),
integrity_link_id=str(uuid4()),
group_ids=[],
)
result = _call_get_staging_metadata()

assert result.layer_name is None

Expand Down
Loading
Loading