"""Inactive ARR1 compatibility adapters; never imported by the ARR2 Web runtime.""" from __future__ import annotations import os import tempfile import uuid from dataclasses import dataclass from pathlib import Path from typing import Any, Dict, Protocol from arr_ingestion.contracts import IngestionError from arr_ingestion.direct_contracts import SubmissionGrant from arr_ingestion.repository import JobRegistration from arr_processing.callbacks import AgentResultWriteback from arr_processing.contracts import ProcessingRequest from arr_processing.errors import ProcessingError from arr_processing.runner import processing_dispatch_idempotency_key from arr_storage.store import ManagedObjectStore from arr_web.contracts import PortalError, validate_upload_filename, validate_xml_payload class AgentResultCoordinator(Protocol): def accept(self, raw_signed_result: bytes) -> Dict[str, Any]: ... class ProcessingStarter(Protocol): def start(self, request: ProcessingRequest) -> Any: ... class DirectJobRepository(Protocol): def register_job(self, registration: JobRegistration) -> None: ... def issue_grant( self, job_id: str, attempt_no: int, *, ttl_seconds: int ) -> SubmissionGrant: ... @dataclass class ProcessingAgentResultCoordinator: writeback: AgentResultWriteback def accept(self, raw_signed_result: bytes) -> Dict[str, Any]: try: return self.writeback.accept(raw_signed_result).to_dict() except (IngestionError, ProcessingError) as error: code = error.code if code in {"PROCESSING_SIGNATURE_INVALID", "PROCESSING_SIGNATURE_EXPIRED"}: status, message = 401, "Agent 结果签名无效或已过期" elif code.endswith("_NOT_FOUND") or code in { "JOB_NOT_FOUND", "PROCESSING_JOB_NOT_FOUND", }: status, message = 404, "Agent 结果对应的业务任务不存在" elif "CONFLICT" in code or code in { "JOB_TERMINAL", "PROCESSING_RESULT_MISMATCH", }: status, message = 409, "Agent 结果与现有业务任务冲突" elif getattr(error, "retryable", False) or code.startswith("DATABASE_"): status, message = 503, "Agent 结果暂时无法写入,请使用相同回调重试" else: status, message = 422, "Agent 结果未通过业务验收" raise PortalError(code, message, status) from None @dataclass class ObjectStoreUploadCoordinator: """Frozen ARR1 upload/grant/remote-dispatch adapter for compatibility tests.""" object_store: ManagedObjectStore ingestion_repository: DirectJobRepository processing_starter: ProcessingStarter processor_version: str rule_set_sha256: str submission_grant_ttl_seconds: int = 900 def submit(self, original_filename: str, payload: bytes) -> Dict[str, Any]: uploaded_filename = validate_upload_filename(original_filename) validate_xml_payload(payload) job_id = "arrjob-" + uuid.uuid4().hex attempt_no = 1 with tempfile.TemporaryDirectory(prefix="arr-web-upload-") as temporary: source_path = Path(temporary) / "source.xml" descriptor = os.open(source_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) try: with os.fdopen(descriptor, "wb") as target: descriptor = -1 target.write(payload) target.flush() os.fsync(target.fileno()) finally: if descriptor >= 0: os.close(descriptor) stored = self.object_store.upload_committed( job_id=job_id, attempt_no=attempt_no, role="source_xml", source=source_path, original_filename="source.xml", ) source_file_id = "arr_file_" + uuid.uuid4().hex request_without_grant = ProcessingRequest( job_id=job_id, attempt_no=attempt_no, source_file_id=source_file_id, processor_version=self.processor_version, rule_set_sha256=self.rule_set_sha256, ) self.ingestion_repository.register_job( JobRegistration( job_id=job_id, source=stored.to_artifact_ref(), processor_version=self.processor_version, rule_set_sha256=self.rule_set_sha256, attempt_no=attempt_no, idempotency_key=processing_dispatch_idempotency_key(request_without_grant), uploaded_filename=uploaded_filename, ) ) grant = self.ingestion_repository.issue_grant( job_id, attempt_no, ttl_seconds=self.submission_grant_ttl_seconds ) request = ProcessingRequest( job_id=job_id, attempt_no=attempt_no, source_file_id=source_file_id, processor_version=self.processor_version, rule_set_sha256=self.rule_set_sha256, submission_grant=grant.submission_grant, ) record = self.processing_starter.start(request) return { "job_id": job_id, "status": str(getattr(record, "remote_status", "queued")), "attempt_no": attempt_no, "source_sha256": stored.sha256, "source_byte_size": stored.byte_size, }