#!/usr/bin/env bash set -euo pipefail BASE_URL="${1:-${GE_INTEL_BASE_URL:-http://localhost:1202}}" TMP_DIR="$(mktemp -d)" trap 'rm -rf "${TMP_DIR}"' EXIT if ! command -v curl >/dev/null 2>&1; then echo "curl is required for workbench interaction verification" >&2 exit 1 fi if [ -n "${PYTHON_BIN:-}" ]; then PYTHON_BIN="${PYTHON_BIN}" else PYTHON_BIN="" for candidate in python3 python.exe python; do if command -v "${candidate}" >/dev/null 2>&1 && "${candidate}" -c "import json, sys" >/dev/null 2>&1; then PYTHON_BIN="${candidate}" break fi done fi if [ -z "${PYTHON_BIN}" ]; then echo "A Python interpreter is required for JSON parsing" >&2 exit 1 fi require_json_data() { local file_path="$1" "${PYTHON_BIN}" - "$file_path" <<'PY' import json import sys with open(sys.argv[1], "r", encoding="utf-8") as handle: payload = json.load(handle) if "data" not in payload: raise SystemExit("Response is not a canonical GeoIntel data envelope") PY } json_field() { local file_path="$1" local expression="$2" "${PYTHON_BIN}" - "$file_path" "$expression" <<'PY' import json import sys path, expression = sys.argv[1], sys.argv[2] with open(path, "r", encoding="utf-8") as handle: payload = json.load(handle) value = payload for part in expression.split("."): if part: value = value[part] print(value) PY } echo "== GeoIntel workbench interaction backing-state verification ==" echo "Base URL: ${BASE_URL}" curl -fsS -X POST "${BASE_URL%/}/api/v1/demo/workflow" > "${TMP_DIR}/demo.json" require_json_data "${TMP_DIR}/demo.json" project_id="$(json_field "${TMP_DIR}/demo.json" "data.project_id")" area_id="$(json_field "${TMP_DIR}/demo.json" "data.area_id")" candidate_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.candidate_dataset_id")" reference_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.reference_dataset_id")" raster_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.raster_dataset_id")" quality_check_id="$(json_field "${TMP_DIR}/demo.json" "data.quality_check_id")" curl -fsS "${BASE_URL%/}/api/v1/projects" > "${TMP_DIR}/projects.json" require_json_data "${TMP_DIR}/projects.json" curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/areas" > "${TMP_DIR}/areas.json" require_json_data "${TMP_DIR}/areas.json" curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/datasets" > "${TMP_DIR}/datasets.json" require_json_data "${TMP_DIR}/datasets.json" curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/quality-checks" > "${TMP_DIR}/quality_checks.json" require_json_data "${TMP_DIR}/quality_checks.json" "${PYTHON_BIN}" - \ "${TMP_DIR}/projects.json" \ "${TMP_DIR}/areas.json" \ "${TMP_DIR}/datasets.json" \ "${TMP_DIR}/quality_checks.json" \ "${project_id}" \ "${area_id}" \ "${candidate_dataset_id}" \ "${reference_dataset_id}" \ "${raster_dataset_id}" \ "${quality_check_id}" <<'PY' import json import sys projects_path, areas_path, datasets_path, quality_path = sys.argv[1:5] project_id, area_id, candidate_id, reference_id, raster_id, quality_check_id = sys.argv[5:11] def load(path): with open(path, "r", encoding="utf-8") as handle: return json.load(handle)["data"]["items"] projects = load(projects_path) areas = load(areas_path) datasets = load(datasets_path) checks = load(quality_path) project = next((item for item in projects if item["id"] == project_id), None) if not project or project.get("name") != "GeoIntel Demo - Building QA": raise SystemExit("Project switch backing state is missing the demo project") area = next((item for item in areas if item["id"] == area_id), None) if not area or area.get("name") != "Demo AOI - Geel buildings": raise SystemExit("Area selection backing state is missing the demo AOI") if not area.get("geometry"): raise SystemExit("Area selection backing state has no map geometry") dataset_ids = {item["id"]: item for item in datasets} candidate = dataset_ids.get(candidate_id) reference = dataset_ids.get(reference_id) raster = dataset_ids.get(raster_id) if not candidate: raise SystemExit("candidate dataset backing state is missing") if not reference: raise SystemExit("reference dataset backing state is missing") if not raster: raise SystemExit("raster dataset backing state is missing") if candidate.get("status") != "ready" or reference.get("status") != "ready" or raster.get("status") != "ready": raise SystemExit("Dataset selection backing state is not 3/3 ready") if reference.get("dataset_role") != "reference": raise SystemExit("reference dataset backing state lost its reference role") if raster.get("dataset_type") != "raster" or raster.get("source_name") != "fixture": raise SystemExit("raster dataset backing state lost its fixture raster metadata") quality_check = next((item for item in checks if item["id"] == quality_check_id), None) if not quality_check or quality_check.get("status") != "ok": raise SystemExit("QA refresh backing state is missing the seeded ok quality check") metric_keys = {metric["metric_key"] for metric in quality_check.get("metrics", [])} if not {"precision", "recall", "f1", "mean_iou"}.issubset(metric_keys): raise SystemExit("QA refresh backing state is missing core metrics") PY curl -fsS -X POST "${BASE_URL%/}/api/v1/exports/metadata" \ -H "Content-Type: application/json" \ -d "{\"project_id\":\"${project_id}\"}" > "${TMP_DIR}/metadata_export.json" require_json_data "${TMP_DIR}/metadata_export.json" metadata_export_id="$(json_field "${TMP_DIR}/metadata_export.json" "data.export_id")" curl -fsS "${BASE_URL%/}/api/v1/exports/projects/${project_id}/exports" > "${TMP_DIR}/exports.json" require_json_data "${TMP_DIR}/exports.json" "${PYTHON_BIN}" - "${TMP_DIR}/exports.json" "${metadata_export_id}" <<'PY' import json import sys path, export_id = sys.argv[1], sys.argv[2] with open(path, "r", encoding="utf-8") as handle: items = json.load(handle)["data"]["items"] export = next((item for item in items if item["id"] == export_id), None) if not export: raise SystemExit("Export refresh backing state does not list the new metadata export") if export.get("export_type") != "project_metadata_json": raise SystemExit(f"Unexpected export type: {export.get('export_type')}") PY echo "Workbench interaction backing-state verification passed" echo "Project switch: GeoIntel Demo - Building QA" echo "Area selection: Demo AOI - Geel buildings" echo "Dataset selection: candidate, reference and raster fixture datasets ready" echo "QA refresh: seeded metrics available" echo "Export refresh: metadata export listed"