72 lines
3.5 KiB
Python
72 lines
3.5 KiB
Python
"""Explicit accepted-source composition; no default source or credential loader.
|
|
|
|
Supplying components does not certify Oracle report semantics. A reviewed
|
|
launcher owns that acceptance and the reader factory's credential lifetime.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Callable
|
|
|
|
from arr_web.arr_download_executor import ARRMappingValidator, ARRSourceAdapter, CapturedARRExecutor
|
|
from arr_web.arr_downloads import PersistentARRDownloads
|
|
from arr_web.arr_data_runtime import DirectARRSource, compose_direct_arr_downloads
|
|
from arr_web.processing_runtime import ProcessingInputRuntime
|
|
from integrations.ohip.audit_arr_capture import protected_read
|
|
from integrations.ohip.capture_job import atomic_json, fingerprint, job_lock, private_directory, sync_directory
|
|
from integrations.ohip.collect_arr_source import APPLICATION, SERVICE, require, strict_json
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CapturedARRSource:
|
|
root: Path
|
|
hotel_id: str
|
|
adapter_contract: str
|
|
adapter: ARRSourceAdapter
|
|
mapping_validator: ARRMappingValidator
|
|
reader_factory: Callable
|
|
capture_version: str = "v2"
|
|
max_profiles: int | None = None
|
|
page_size: int = 100
|
|
max_pages: int = 100
|
|
max_records: int = 10000
|
|
|
|
|
|
def compose_arr_downloads(source: CapturedARRSource | DirectARRSource, processing: ProcessingInputRuntime) -> PersistentARRDownloads:
|
|
if isinstance(source, DirectARRSource):
|
|
return compose_direct_arr_downloads(source, processing)
|
|
root = Path(source.root).absolute()
|
|
# Constructor validates contracts, path and bounds before creating a queue.
|
|
# Reuse upload dependencies, including the exact loaded processing policy.
|
|
executor = CapturedARRExecutor(
|
|
root=root / "acquisition", hotel_id=source.hotel_id, adapter_contract=source.adapter_contract,
|
|
adapter=source.adapter, mapping_validator=source.mapping_validator,
|
|
reader_factory=source.reader_factory, capture_version=source.capture_version,
|
|
max_profiles=source.max_profiles, page_size=source.page_size,
|
|
max_pages=source.max_pages, max_records=source.max_records,
|
|
policy=processing.policy, object_store=processing.object_store,
|
|
repository=processing.repository, ingestion=processing.ingestion, processor=processing.processor,
|
|
)
|
|
private_directory(root)
|
|
sync_directory(root.parent)
|
|
identity = {
|
|
"version": "arr-download-runtime/v1", "hotel_id": source.hotel_id,
|
|
"service_url": SERVICE, "application_id": APPLICATION,
|
|
"adapter_contract": source.adapter_contract, "capture_version": source.capture_version,
|
|
"max_profiles": source.max_profiles, "page_size": source.page_size,
|
|
"max_pages": source.max_pages, "max_records": source.max_records,
|
|
"processor_version": processing.policy.processor_version,
|
|
"rule_set_sha256": processing.policy.rule_set_sha256,
|
|
}
|
|
# Pin before dispatch, including jobs queued before their first capture. A
|
|
# deployment cannot silently rebind those jobs to a different hotel/policy.
|
|
with job_lock(root):
|
|
pin = root / "source.json"
|
|
if not pin.exists():
|
|
require(not (root / "queue").exists() and not (root / "acquisition").exists(),
|
|
"unbound_download_runtime_state")
|
|
atomic_json(pin, identity, replace=False)
|
|
require(fingerprint(strict_json(protected_read(pin, 65536))) == fingerprint(identity),
|
|
"download_runtime_source_conflict")
|
|
return PersistentARRDownloads(root / "queue", executor)
|