refactor(db)!: adopt project-routed pooled databases

Reorganize WNDB by responsibility and remove legacy scheme endpoints.\n\nRoute analysis and time-series access through project pools, preserve transactional realtime replacement, and refresh GIS materialized views after writes.\n\nAdd database architecture documentation, live pooling coverage, API contract updates, and executable container verification.\n\nBREAKING CHANGE: legacy scheme APIs and flat app.native.wndb module imports are removed.
This commit is contained in:
2026-08-25 18:35:05 +08:00
parent fdbcc5c033
commit fa188af0b1
181 changed files with 8446 additions and 33546 deletions
+1 -98
View File
@@ -10,12 +10,10 @@ from fastapi import APIRouter, FastAPI, Query
from fastapi.routing import APIRoute
from fastapi.testclient import TestClient
from app.api.v1.endpoints import schemes as schemes_endpoint
from app.api.v1.endpoints import simulation as simulation_endpoint
from app.api.pagination import PaginatedList
from app.api.v1.rest_router import api_router, build_rest_router
from app.api.v1.router import api_router as source_api_router
from app.auth.metadata_dependencies import get_current_metadata_user
from app.auth.project_dependencies import (
ProjectContext,
get_project_business_routing,
@@ -173,12 +171,6 @@ def test_rest_contract_uses_header_project_context() -> None:
assert "network" not in schema.get("properties", {})
assert "network_name" not in schema.get("properties", {})
placement_schema = document["components"]["schemas"][
"PressureSensorPlacementRest"
]
assert "name" not in placement_schema["properties"]
assert "username" not in placement_schema["properties"]
assert "/api/v1/burst-analysis" not in document["paths"]
assert "/api/v1/getpipeproperties/" not in document["paths"]
@@ -294,54 +286,10 @@ def test_sensor_placement_excel_export_is_post() -> None:
if isinstance(route, APIRoute)
}
assert methods_by_path[
"/sensor-placement-schemes/{scheme_id}/exports/excel"
"/sensor-placement-runs/{run_id}/exports/excel"
] == {"POST"}
def test_rest_runtime_consumes_injected_project_context(monkeypatch) -> None:
captured: dict[str, object] = {}
def fake_get_all_schemes(network, scheme_type=None, query_date=None):
captured.update(
network=network,
scheme_type=scheme_type,
query_date=query_date,
business_dsn=get_project_pgconn_string(network),
)
return [{"scheme_name": "burst_case", "scheme_type": scheme_type}]
monkeypatch.setattr(
schemes_endpoint,
"get_all_schemes",
fake_get_all_schemes,
)
app = FastAPI(redirect_slashes=False)
app.include_router(api_router, prefix="/api/v1")
project_context = ProjectContext(
project_id=uuid4(),
project_code="fengyang",
user_id=uuid4(),
project_role="viewer",
)
_override_project_routing(app, project_context)
response = TestClient(app, raise_server_exceptions=False).get(
"/api/v1/schemes",
params={"scheme_type": "burst_analysis"},
)
assert response.status_code == 200
assert captured == {
"network": "fengyang",
"scheme_type": "burst_analysis",
"query_date": None,
"business_dsn": "postgresql://user:password@biz/fengyang",
}
assert response.json()["items"] == [
{"scheme_name": "burst_case", "scheme_type": "burst_analysis"}
]
def test_rest_runtime_wraps_handler_paginated_list() -> None:
source_router = APIRouter()
@@ -370,51 +318,6 @@ def test_rest_runtime_wraps_handler_paginated_list() -> None:
}
def test_sensor_placement_body_uses_authenticated_project_and_user(
monkeypatch,
) -> None:
captured: dict[str, object] = {}
def fake_pressure_sensor_placement_kmeans(**kwargs):
captured.update(kwargs)
monkeypatch.setattr(
simulation_endpoint,
"pressure_sensor_placement_kmeans",
fake_pressure_sensor_placement_kmeans,
)
app = FastAPI(redirect_slashes=False)
app.include_router(api_router, prefix="/api/v1")
project_context = ProjectContext(
project_id=uuid4(),
project_code="project_a",
user_id=uuid4(),
project_role="member",
)
_override_project_routing(app, project_context)
app.dependency_overrides[get_current_metadata_user] = lambda: type(
"User", (), {"username": "alice"}
)()
response = TestClient(app, raise_server_exceptions=False).post(
"/api/v1/pressure-sensor-placement-kmeans",
json={
"scheme_name": "placement_01",
"sensor_number": 5,
"min_diameter": 100,
},
)
assert response.status_code == 200
assert captured == {
"name": "project_a",
"scheme_name": "placement_01",
"sensor_number": 5,
"min_diameter": 100,
"username": "alice",
}
def test_rest_runtime_json_encodes_untyped_datetime_response() -> None:
source_router = APIRouter()