keep a precise failure precise, and state one redirect policy
Two problems of the same shape: information about *why* something failed being replaced by something vaguer. get_dataset_geojson wrapped the JSON parse, the metadata read, the CRS resolution and the canonicalisation in one try and reported all of it as "Stored dataset is not valid JSON" with a 500. An operator whose dataset had an unusable CRS was sent to inspect a file that parses perfectly well, and the canonicaliser's own AppError — with its code and its status — never reached them. Only the parse is now inside that handler; everything after it keeps the error it raised, and a genuine bug becomes a distinct 500 rather than a mislabelled client error. A guard finds the same shape elsewhere: catching Exception around a call into another component and relabelling what it reported. Wrapping one's own private helper stays legitimate and the guard says so. The redirect policy was split without anyone saying so. Two acquisition services rejected every redirect through a hand-rolled opener, while eight allowed a same-origin one through the shared guard — and only the latter checked where the response came from. Both live in the guard now, and the strict path uses the rejecting handler rather than the guard's after-the-fact check: objecting to response.url means urllib already opened the connection and read the body, which for a metadata endpoint is the whole attack. That was a weakening I introduced in this same commit's first draft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2906,8 +2906,19 @@ class DatasetService:
|
||||
raise AppError(code="DATASET_FILE_MISSING", message="Stored file missing", status_code=404)
|
||||
|
||||
raw = load_dataset_text(dataset.storage_path)
|
||||
# Only the parse can be "not valid JSON". Everything after it fails for
|
||||
# its own reasons and must say so, or an operator is sent to inspect a
|
||||
# file that parses perfectly well.
|
||||
try:
|
||||
payload = json.loads(raw)
|
||||
except Exception as exc:
|
||||
raise AppError(
|
||||
code="INVALID_GEOJSON",
|
||||
message="Stored dataset is not valid JSON",
|
||||
status_code=500,
|
||||
) from exc
|
||||
|
||||
try:
|
||||
metadata_value = getattr(dataset, "metadata_json", None)
|
||||
metadata = metadata_value if isinstance(metadata_value, dict) else {}
|
||||
provenance_value = getattr(dataset, "provenance_metadata", None)
|
||||
@@ -2933,8 +2944,17 @@ class DatasetService:
|
||||
)
|
||||
)
|
||||
return VectorFeatureService.canonicalize_geojson_payload(payload, source_crs=str(source_crs))
|
||||
except AppError:
|
||||
# The canonicaliser's diagnosis is more precise than anything this
|
||||
# layer could substitute for it.
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise AppError(code="INVALID_GEOJSON", message="Stored dataset is not valid JSON", status_code=500) from exc
|
||||
raise AppError(
|
||||
code="DATASET_GEOJSON_UNREADABLE",
|
||||
message="The stored dataset could not be read as canonical GeoJSON",
|
||||
details={"dataset_id": str(dataset.id), "error_type": type(exc).__name__},
|
||||
status_code=500,
|
||||
) from exc
|
||||
|
||||
@staticmethod
|
||||
def inspect_vector_dataset(db: Session, dataset_id: UUID) -> dict[str, Any]:
|
||||
|
||||
Reference in New Issue
Block a user