AutoRpa/codes/rpa_yisaiqi/callback_client.py

94 lines
2.7 KiB
Python

"""HTTP client for YiSaiQi callbacks into the state center."""
from __future__ import annotations
import json
from typing import Any, Callable
from urllib.error import HTTPError
from urllib.parse import quote
from urllib.request import Request, urlopen
JsonDict = dict[str, Any]
Opener = Callable[[Request, float], Any]
class StateCenterError(RuntimeError):
def __init__(self, status: int, detail: Any):
super().__init__(f"state center returned {status}: {detail}")
self.status = status
self.detail = detail
class StateCenterClient:
def __init__(
self,
base_url: str,
token: str,
*,
timeout: float = 15,
opener: Opener | None = None,
):
self.base_url = base_url.rstrip("/")
self.token = token
self.timeout = timeout
self._opener = opener or _urlopen_with_timeout
def start_node(
self,
job_id: str,
node_code: str,
*,
attempt_no: int = 1,
operator: str = "agent",
) -> JsonDict:
return self._post(
f"/api/jobs/{_path_part(job_id)}/nodes/{_path_part(node_code)}/start",
{
"attemptNo": attempt_no,
"executorPlatform": "iS-RPA",
"operator": operator,
},
)
def complete_node(
self,
job_id: str,
node_code: str,
payload: JsonDict,
) -> JsonDict:
return self._post(f"/api/jobs/{_path_part(job_id)}/nodes/{_path_part(node_code)}/complete", payload)
def fail_node(
self,
job_id: str,
node_code: str,
payload: JsonDict,
) -> JsonDict:
return self._post(f"/api/jobs/{_path_part(job_id)}/nodes/{_path_part(node_code)}/fail", payload)
def _post(self, path: str, payload: JsonDict) -> JsonDict:
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
request = Request(
f"{self.base_url}{path}",
data=data,
headers={
"Content-Type": "application/json",
"X-Agent-Token": self.token,
},
method="POST",
)
try:
with self._opener(request, self.timeout) as response:
raw = response.read().decode("utf-8")
except HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
raise StateCenterError(exc.code, detail) from exc
return json.loads(raw) if raw else {}
def _path_part(value: str) -> str:
return quote(value, safe="")
def _urlopen_with_timeout(request: Request, timeout: float) -> Any:
return urlopen(request, timeout=timeout)