generated from coulomb/repo-seed
first extension execution path
This commit is contained in:
@@ -12,7 +12,16 @@
|
||||
],
|
||||
"check_groups": [],
|
||||
"preflight_runner": null,
|
||||
"runner_entrypoints": [],
|
||||
"runner_entrypoints": [
|
||||
{
|
||||
"id": "replace-with-runner-id",
|
||||
"kind": "external",
|
||||
"module_path": null,
|
||||
"callable": null,
|
||||
"command": null,
|
||||
"description": "Describe how this runner is provided."
|
||||
}
|
||||
],
|
||||
"normalizers": [],
|
||||
"mappings": [],
|
||||
"report_fragments": [],
|
||||
|
||||
@@ -59,7 +59,22 @@
|
||||
],
|
||||
"preflight_runner": "cmis-browser-preflight",
|
||||
"runner_entrypoints": [
|
||||
"opencmis-tck"
|
||||
{
|
||||
"id": "cmis-browser-preflight",
|
||||
"kind": "python_module",
|
||||
"module_path": "src/open_cmis_tck/preflight.py",
|
||||
"callable": "run",
|
||||
"command": null,
|
||||
"description": "Checks whether the configured CMIS Browser Binding endpoint is reachable and returns parseable repository metadata."
|
||||
},
|
||||
{
|
||||
"id": "opencmis-tck",
|
||||
"kind": "external",
|
||||
"module_path": null,
|
||||
"callable": null,
|
||||
"command": null,
|
||||
"description": "Placeholder for the Java/Maven Apache Chemistry OpenCMIS TCK runner."
|
||||
}
|
||||
],
|
||||
"normalizers": [
|
||||
"opencmis-result-normalizer"
|
||||
|
||||
161
extensions/open-cmis-tck/src/open_cmis_tck/preflight.py
Normal file
161
extensions/open-cmis-tck/src/open_cmis_tck/preflight.py
Normal file
@@ -0,0 +1,161 @@
|
||||
"""CMIS Browser Binding preflight runner."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
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)
|
||||
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", "")
|
||||
body = response.read(1024 * 1024)
|
||||
except HTTPError as exc:
|
||||
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,
|
||||
},
|
||||
"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": [],
|
||||
}
|
||||
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": [],
|
||||
}
|
||||
|
||||
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": [],
|
||||
}
|
||||
|
||||
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": [],
|
||||
}
|
||||
|
||||
|
||||
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],
|
||||
}
|
||||
@@ -91,7 +91,7 @@ Acceptance:
|
||||
|
||||
```task
|
||||
id: OPEN-CMIS-TCK-WP-0001-T003
|
||||
status: todo
|
||||
status: in_progress
|
||||
priority: high
|
||||
state_hub_task_id: "6d45885b-78a4-4e8b-8fcc-b8d6488e703b"
|
||||
```
|
||||
@@ -103,6 +103,13 @@ Acceptance:
|
||||
- Unsupported optional capabilities can be accepted as expected gaps.
|
||||
- Preflight output is captured as structured JSON.
|
||||
|
||||
Progress:
|
||||
|
||||
- The first CMIS Browser Binding preflight runner checks endpoint reachability
|
||||
and parseable JSON repository metadata through the guide-board runner bridge.
|
||||
- Capability flag normalization remains to be expanded after a live target sample
|
||||
is captured.
|
||||
|
||||
## D1.4 - OpenCMIS TCK Runner Wrapper
|
||||
|
||||
```task
|
||||
|
||||
Reference in New Issue
Block a user