"""HTTP clients for update-dim-tables dependencies.""" from typing import TypedDict from src import config class TransferJob(TypedDict): """Subset of transfer job fields required by this lambda.""" destination_vendor_id: int destination_subaccount_id: int | None revenue_cutoff_date: str class TransferProduct(TypedDict): """Subset of product snapshot fields required by this lambda.""" release_id: int def _expect_json_dict( response_name: str, job_id: int, status_code: int, body: str, payload: object ) -> dict[object, object]: if status_code != 200: raise RuntimeError(f"{response_name} failed for job {job_id}: status={status_code} body={body}") if not isinstance(payload, dict): raise RuntimeError( f"{response_name} returned unexpected payload type for job {job_id}: {type(payload).__name__}" ) return payload def get_transfer_job(job_id: int) -> TransferJob: """GET /transfer/job/{job_id} on ows-project-manager.""" response = config.ows_client.get("ows-project-manager", path=f"/transfer/job/{job_id}") try: payload = response.json() except ValueError as exc: raise RuntimeError(f"get_transfer_job returned non-JSON response for job {job_id}") from exc payload_dict = _expect_json_dict("get_transfer_job", job_id, response.status_code, response.text, payload) destination_vendor_id = payload_dict.get("destination_vendor_id") if type(destination_vendor_id) is not int: raise RuntimeError( f"get_transfer_job returned invalid 'destination_vendor_id' for job {job_id}: {destination_vendor_id!r}" ) destination_subaccount_id = payload_dict.get("destination_subaccount_id") if destination_subaccount_id is not None and type(destination_subaccount_id) is not int: raise RuntimeError( f"get_transfer_job returned invalid 'destination_subaccount_id' for job {job_id}: {destination_subaccount_id!r}" ) revenue_cutoff_date = payload_dict.get("revenue_cutoff_date") if not isinstance(revenue_cutoff_date, str) or not revenue_cutoff_date: raise RuntimeError( f"get_transfer_job returned missing or null 'revenue_cutoff_date' for job {job_id}: {revenue_cutoff_date!r}" ) return { "destination_vendor_id": destination_vendor_id, "destination_subaccount_id": destination_subaccount_id, "revenue_cutoff_date": revenue_cutoff_date, } def get_transfer_job_products(job_id: int, destination_vendor_id: int) -> list[TransferProduct]: """GET /transfer/job/{job_id}/products on ows-project-manager.""" response = config.ows_client.get( "ows-project-manager", path=f"/transfer/job/{job_id}/products", headers={ "Grass-Account-Type": "vendor", "Grass-Account-Id": str(destination_vendor_id), }, ) if response.status_code != 200: raise RuntimeError( f"get_transfer_job_products failed for job {job_id}: status={response.status_code} body={response.text}" ) try: payload = response.json() except ValueError as exc: raise RuntimeError(f"get_transfer_job_products returned non-JSON response for job {job_id}") from exc if not isinstance(payload, list): raise RuntimeError( f"get_transfer_job_products returned unexpected payload type for job {job_id}: {type(payload).__name__}" ) products: list[TransferProduct] = [] for index, item in enumerate(payload): if not isinstance(item, dict): raise RuntimeError( f"get_transfer_job_products returned non-object item at index {index} for job {job_id}: {item!r}" ) release_id = item.get("release_id") if type(release_id) is not int: raise RuntimeError( f"get_transfer_job_products returned invalid 'release_id' at index {index} for job {job_id}: {release_id!r}" ) products.append({"release_id": release_id}) return products