"""S3 storage for validation results.""" import json from functools import cache from typing import Any import boto3 import config @cache def _client() -> Any: return boto3.client("s3", region_name=config.AWS_REGION) def load_from_s3(s3_uri: str) -> dict[str, Any]: """Fetch and deserialize a JSON object from S3.""" without_scheme = s3_uri.removeprefix("s3://") bucket, _, key = without_scheme.partition("/") response = _client().get_object( Bucket=bucket, Key=key, ExpectedBucketOwner=config.EXPECTED_BUCKET_OWNER ) return json.loads(response["Body"].read()) def build_object_key(prefix: str, output_s3_path: str) -> str: """Compose the object key under the prepare-payload prefix.""" return f"{prefix.rstrip('/')}/{output_s3_path.rstrip('/')}.json".lstrip("/") def store_result(output_s3_path: str, result: dict[str, Any]) -> str: """Write the validation result to S3 and return its s3:// URL.""" if not config.RESULTS_BUCKET: raise ValueError("RESULTS_BUCKET is not configured") if not config.RESULTS_BUCKET_PREFIX: raise ValueError("RESULTS_BUCKET_PREFIX is not configured") key = build_object_key(config.RESULTS_BUCKET_PREFIX, output_s3_path) put_kwargs: dict[str, Any] = { "Bucket": config.RESULTS_BUCKET, "Key": key, "Body": json.dumps(result).encode("utf-8"), "ContentType": "application/json", } if config.EXPECTED_BUCKET_OWNER: put_kwargs["ExpectedBucketOwner"] = config.EXPECTED_BUCKET_OWNER _client().put_object(**put_kwargs) return f"s3://{config.RESULTS_BUCKET}/{key}"