feat: move report artifacts to OSS storage
This commit is contained in:
@@ -18,6 +18,7 @@ from pathlib import Path, PurePosixPath
|
||||
from typing import Any, Callable, Deque, Dict, List, Mapping, Optional, Protocol, Tuple
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from arr_storage.store import ManagedObjectStore
|
||||
from arr_web.contracts import PortalError, validate_month
|
||||
from arr_web.downloads import ArtifactDescriptor, MAX_DOWNLOAD_BYTES
|
||||
from company_reports.contracts import (
|
||||
@@ -197,6 +198,7 @@ class PersistentCompanyReportCoordinator:
|
||||
*,
|
||||
jobs_root: Optional[Path] = None,
|
||||
now: Optional[Callable[[], datetime]] = None,
|
||||
object_store: Optional[ManagedObjectStore] = None,
|
||||
) -> None:
|
||||
self._project_root = project_root.resolve()
|
||||
self._output_root = output_root.resolve()
|
||||
@@ -213,6 +215,7 @@ class PersistentCompanyReportCoordinator:
|
||||
self._jobs_root.mkdir(parents=True, exist_ok=True, mode=0o700)
|
||||
os.chmod(self._jobs_root, 0o700)
|
||||
self._executor = executor
|
||||
self._object_store = object_store
|
||||
self._now = now or (lambda: datetime.now(COMPANY_REPORT_TIME_ZONE))
|
||||
self._condition = threading.Condition(threading.RLock())
|
||||
self._pending: Deque[str] = deque()
|
||||
@@ -612,6 +615,46 @@ class PersistentCompanyReportCoordinator:
|
||||
or SHA256_RE.fullmatch(sha256) is None
|
||||
):
|
||||
raise ValueError("artifact metadata is invalid")
|
||||
storage_provider = str(value.get("storage_provider") or "local")
|
||||
bucket_alias = str(value.get("bucket_alias") or "arr-project-root")
|
||||
byte_size_value = value.get("byte_size")
|
||||
mime_type = str(value.get("mime_type") or XLSX_MIME)
|
||||
if storage_provider in {"oss", "s3"}:
|
||||
if self._object_store is None:
|
||||
raise ValueError("OSS artifact reader is unavailable")
|
||||
if (
|
||||
isinstance(byte_size_value, bool)
|
||||
or not isinstance(byte_size_value, int)
|
||||
or byte_size_value <= 0
|
||||
or mime_type != XLSX_MIME
|
||||
):
|
||||
raise ValueError("OSS artifact identity is invalid")
|
||||
try:
|
||||
stored = self._object_store.inspect_committed(
|
||||
storage_key,
|
||||
expected_filename,
|
||||
)
|
||||
except Exception:
|
||||
raise ValueError("OSS artifact identity is unavailable") from None
|
||||
if (
|
||||
stored.role != "company_report"
|
||||
or stored.sha256 != sha256
|
||||
or stored.byte_size != byte_size_value
|
||||
or stored.mime_type != mime_type
|
||||
):
|
||||
raise ValueError("OSS artifact identity does not match")
|
||||
descriptor = ArtifactDescriptor(
|
||||
file_kind="company_ten_day_xlsx",
|
||||
original_filename=expected_filename,
|
||||
storage_key=storage_key,
|
||||
sha256=sha256,
|
||||
byte_size=byte_size_value,
|
||||
mime_type=mime_type,
|
||||
storage_provider=storage_provider,
|
||||
bucket_alias=bucket_alias,
|
||||
)
|
||||
descriptor.validate()
|
||||
return descriptor
|
||||
storage = PurePosixPath(storage_key)
|
||||
if storage.is_absolute() or not storage.parts or ".." in storage.parts or "." in storage.parts:
|
||||
raise ValueError("artifact path is invalid")
|
||||
@@ -637,6 +680,8 @@ class PersistentCompanyReportCoordinator:
|
||||
sha256=sha256,
|
||||
byte_size=metadata.st_size,
|
||||
mime_type=XLSX_MIME,
|
||||
storage_provider=storage_provider,
|
||||
bucket_alias=bucket_alias,
|
||||
)
|
||||
descriptor.validate()
|
||||
return descriptor
|
||||
|
||||
Reference in New Issue
Block a user