"""CMIS Browser Binding preflight runner.""" from __future__ import annotations import json from pathlib import Path from typing import Any from urllib.error import HTTPError, URLError from urllib.request import Request, urlopen def run(context: dict[str, Any]) -> dict[str, Any]: target = context["target_profile"] endpoint = _browser_endpoint(target) if endpoint is None: return { "result": "fail", "observations": [ "Target profile does not declare a CMIS Browser Binding endpoint." ], "facts": { "endpoint_found": False, }, "artifact_refs": [], } timeout = _timeout_seconds(context) artifact_refs: list[str] = [] request = Request( endpoint["url"], headers={ "Accept": "application/json, */*;q=0.1", "User-Agent": "guide-board-open-cmis-tck-preflight/0.1.0", }, ) try: with urlopen(request, timeout=timeout) as response: status_code = response.status content_type = response.headers.get("Content-Type", "") headers = dict(response.headers.items()) body = response.read(1024 * 1024) artifact_refs = _write_response_artifacts( context, status_code, content_type, headers, body, ) except HTTPError as exc: body = exc.read(1024 * 1024) content_type = exc.headers.get("Content-Type", "") artifact_refs = _write_response_artifacts( context, exc.code, content_type, dict(exc.headers.items()), body, ) return { "result": "infrastructure_error", "observations": [ f"CMIS Browser Binding endpoint returned HTTP {exc.code}." ], "facts": { "endpoint_found": True, "url": endpoint["url"], "http_status": exc.code, "content_type": content_type, }, "artifact_refs": artifact_refs, } except URLError as exc: return { "result": "infrastructure_error", "observations": [ f"CMIS Browser Binding endpoint is not reachable: {exc.reason}." ], "facts": { "endpoint_found": True, "url": endpoint["url"], "error": str(exc.reason), }, "artifact_refs": artifact_refs, } except TimeoutError: return { "result": "infrastructure_error", "observations": [ f"CMIS Browser Binding endpoint did not respond within {timeout} seconds." ], "facts": { "endpoint_found": True, "url": endpoint["url"], "timeout_seconds": timeout, }, "artifact_refs": artifact_refs, } facts: dict[str, Any] = { "endpoint_found": True, "url": endpoint["url"], "binding": endpoint["binding"], "http_status": status_code, "content_type": content_type, } parsed = _parse_json(body) if parsed is None: facts["json_detected"] = False return { "result": "warning", "observations": [ "CMIS Browser Binding endpoint is reachable but did not return parseable JSON." ], "facts": facts, "artifact_refs": artifact_refs, } facts["json_detected"] = True facts.update(_repository_facts(parsed)) return { "result": "pass", "observations": [ "CMIS Browser Binding endpoint is reachable and returned parseable JSON." ], "facts": facts, "artifact_refs": artifact_refs, } def _browser_endpoint(target: dict[str, Any]) -> dict[str, Any] | None: for endpoint in target.get("endpoints", []): if endpoint.get("binding") == "cmis-browser": return endpoint return None def _timeout_seconds(context: dict[str, Any]) -> float: runtime_policy = context["assessment_profile"].get("runtime_policy", {}) configured = runtime_policy.get("timeout_seconds", 5) if not isinstance(configured, (int, float)): return 5.0 return max(1.0, min(float(configured), 10.0)) def _parse_json(body: bytes) -> Any: try: return json.loads(body.decode("utf-8")) except (UnicodeDecodeError, json.JSONDecodeError): return None def _repository_facts(value: Any) -> dict[str, Any]: if not isinstance(value, dict): return {"repository_shape": "unknown"} if "repositoryId" in value: return { "repository_shape": "single-repository-info", "repository_ids": [value["repositoryId"]], "cmis_version_supported": value.get("cmisVersionSupported"), "capabilities_present": isinstance(value.get("capabilities"), dict), } repository_ids = [] for key, child in value.items(): if isinstance(child, dict) and ( "repositoryId" in child or "repositoryName" in child ): repository_ids.append(str(child.get("repositoryId", key))) if repository_ids: return { "repository_shape": "repository-map", "repository_ids": repository_ids, } return { "repository_shape": "object", "top_level_keys": sorted(str(key) for key in value.keys())[:20], } def _write_response_artifacts( context: dict[str, Any], status_code: int, content_type: str, headers: dict[str, str], body: bytes, ) -> list[str]: run_dir = Path(context["run_dir"]) artifact_dir = run_dir / "artifacts" / "open-cmis-tck" / "preflight" artifact_dir.mkdir(parents=True, exist_ok=True) response_ref = "artifacts/open-cmis-tck/preflight/response-body.bin" metadata_ref = "artifacts/open-cmis-tck/preflight/response-metadata.json" (run_dir / response_ref).write_bytes(body) (run_dir / metadata_ref).write_text( json.dumps( { "status_code": status_code, "content_type": content_type, "headers": headers, "byte_count": len(body), }, indent=2, sort_keys=True, ) + "\n", encoding="utf-8", ) return [metadata_ref, response_ref]