diff --git a/robosystems_client/api/extensions_robo_ledger/get_report_bundle_download_url.py b/robosystems_client/api/extensions_robo_ledger/get_report_bundle_download_url.py new file mode 100644 index 0000000..e5c4061 --- /dev/null +++ b/robosystems_client/api/extensions_robo_ledger/get_report_bundle_download_url.py @@ -0,0 +1,259 @@ +from http import HTTPStatus +from typing import Any +from urllib.parse import quote + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.get_report_bundle_download_url_report_bundle_download_response import ( + GetReportBundleDownloadUrlReportBundleDownloadResponse, +) +from ...models.http_validation_error import HTTPValidationError +from ...types import UNSET, Response, Unset + + +def _get_kwargs( + graph_id: str, + report_id: str, + *, + format_: str | Unset = "jsonld", + expires_in: int | Unset = 300, +) -> dict[str, Any]: + + params: dict[str, Any] = {} + + params["format"] = format_ + + params["expires_in"] = expires_in + + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} + + _kwargs: dict[str, Any] = { + "method": "get", + "url": "/extensions/roboledger/{graph_id}/reports/{report_id}/download".format( + graph_id=quote(str(graph_id), safe=""), + report_id=quote(str(report_id), safe=""), + ), + "params": params, + } + + return _kwargs + + +def _parse_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> ( + GetReportBundleDownloadUrlReportBundleDownloadResponse | HTTPValidationError | None +): + if response.status_code == 200: + response_200 = GetReportBundleDownloadUrlReportBundleDownloadResponse.from_dict( + response.json() + ) + + return response_200 + + if response.status_code == 422: + response_422 = HTTPValidationError.from_dict(response.json()) + + return response_422 + + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[ + GetReportBundleDownloadUrlReportBundleDownloadResponse | HTTPValidationError +]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + graph_id: str, + report_id: str, + *, + client: AuthenticatedClient, + format_: str | Unset = "jsonld", + expires_in: int | Unset = 300, +) -> Response[ + GetReportBundleDownloadUrlReportBundleDownloadResponse | HTTPValidationError +]: + """Download Report bundle + + Return the published Report's serialization bundle. ``format=jsonld`` (default) returns a JSON + envelope containing a short-lived presigned URL to the stamped JSON-LD bundle in S3. + ``format=xbrl-2.1`` rebuilds the bundle on-demand and streams an XBRL 2.1 zip directly. 404 when the + Report has no stamped bundle (published before the serialization feature shipped — JSON-LD only). + + Args: + graph_id (str): + report_id (str): Report identifier (rpt_-prefixed ULID). + format_ (str | Unset): Serialization flavor. ``jsonld`` returns a presigned URL to the + stored JSON-LD bundle; ``xbrl-2.1`` streams a freshly-emitted XBRL zip directly. Other RDF + / XBRL flavors slot in as their producers ship. Default: 'jsonld'. + expires_in (int | Unset): Presigned URL lifetime in seconds (min 60, max 3600). Ignored + for XBRL flavors (streamed directly, no URL). Default: 300. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[GetReportBundleDownloadUrlReportBundleDownloadResponse | HTTPValidationError] + """ + + kwargs = _get_kwargs( + graph_id=graph_id, + report_id=report_id, + format_=format_, + expires_in=expires_in, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + graph_id: str, + report_id: str, + *, + client: AuthenticatedClient, + format_: str | Unset = "jsonld", + expires_in: int | Unset = 300, +) -> ( + GetReportBundleDownloadUrlReportBundleDownloadResponse | HTTPValidationError | None +): + """Download Report bundle + + Return the published Report's serialization bundle. ``format=jsonld`` (default) returns a JSON + envelope containing a short-lived presigned URL to the stamped JSON-LD bundle in S3. + ``format=xbrl-2.1`` rebuilds the bundle on-demand and streams an XBRL 2.1 zip directly. 404 when the + Report has no stamped bundle (published before the serialization feature shipped — JSON-LD only). + + Args: + graph_id (str): + report_id (str): Report identifier (rpt_-prefixed ULID). + format_ (str | Unset): Serialization flavor. ``jsonld`` returns a presigned URL to the + stored JSON-LD bundle; ``xbrl-2.1`` streams a freshly-emitted XBRL zip directly. Other RDF + / XBRL flavors slot in as their producers ship. Default: 'jsonld'. + expires_in (int | Unset): Presigned URL lifetime in seconds (min 60, max 3600). Ignored + for XBRL flavors (streamed directly, no URL). Default: 300. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + GetReportBundleDownloadUrlReportBundleDownloadResponse | HTTPValidationError + """ + + return sync_detailed( + graph_id=graph_id, + report_id=report_id, + client=client, + format_=format_, + expires_in=expires_in, + ).parsed + + +async def asyncio_detailed( + graph_id: str, + report_id: str, + *, + client: AuthenticatedClient, + format_: str | Unset = "jsonld", + expires_in: int | Unset = 300, +) -> Response[ + GetReportBundleDownloadUrlReportBundleDownloadResponse | HTTPValidationError +]: + """Download Report bundle + + Return the published Report's serialization bundle. ``format=jsonld`` (default) returns a JSON + envelope containing a short-lived presigned URL to the stamped JSON-LD bundle in S3. + ``format=xbrl-2.1`` rebuilds the bundle on-demand and streams an XBRL 2.1 zip directly. 404 when the + Report has no stamped bundle (published before the serialization feature shipped — JSON-LD only). + + Args: + graph_id (str): + report_id (str): Report identifier (rpt_-prefixed ULID). + format_ (str | Unset): Serialization flavor. ``jsonld`` returns a presigned URL to the + stored JSON-LD bundle; ``xbrl-2.1`` streams a freshly-emitted XBRL zip directly. Other RDF + / XBRL flavors slot in as their producers ship. Default: 'jsonld'. + expires_in (int | Unset): Presigned URL lifetime in seconds (min 60, max 3600). Ignored + for XBRL flavors (streamed directly, no URL). Default: 300. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[GetReportBundleDownloadUrlReportBundleDownloadResponse | HTTPValidationError] + """ + + kwargs = _get_kwargs( + graph_id=graph_id, + report_id=report_id, + format_=format_, + expires_in=expires_in, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + graph_id: str, + report_id: str, + *, + client: AuthenticatedClient, + format_: str | Unset = "jsonld", + expires_in: int | Unset = 300, +) -> ( + GetReportBundleDownloadUrlReportBundleDownloadResponse | HTTPValidationError | None +): + """Download Report bundle + + Return the published Report's serialization bundle. ``format=jsonld`` (default) returns a JSON + envelope containing a short-lived presigned URL to the stamped JSON-LD bundle in S3. + ``format=xbrl-2.1`` rebuilds the bundle on-demand and streams an XBRL 2.1 zip directly. 404 when the + Report has no stamped bundle (published before the serialization feature shipped — JSON-LD only). + + Args: + graph_id (str): + report_id (str): Report identifier (rpt_-prefixed ULID). + format_ (str | Unset): Serialization flavor. ``jsonld`` returns a presigned URL to the + stored JSON-LD bundle; ``xbrl-2.1`` streams a freshly-emitted XBRL zip directly. Other RDF + / XBRL flavors slot in as their producers ship. Default: 'jsonld'. + expires_in (int | Unset): Presigned URL lifetime in seconds (min 60, max 3600). Ignored + for XBRL flavors (streamed directly, no URL). Default: 300. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + GetReportBundleDownloadUrlReportBundleDownloadResponse | HTTPValidationError + """ + + return ( + await asyncio_detailed( + graph_id=graph_id, + report_id=report_id, + client=client, + format_=format_, + expires_in=expires_in, + ) + ).parsed diff --git a/robosystems_client/clients/__init__.py b/robosystems_client/clients/__init__.py index 9868147..b991bb3 100644 --- a/robosystems_client/clients/__init__.py +++ b/robosystems_client/clients/__init__.py @@ -48,7 +48,7 @@ GraphInfo, ) from .investor_client import InvestorClient -from .ledger_client import LedgerClient +from .ledger_client import LedgerClient, ReportBundleDownload from .library_client import LIBRARY_GRAPH_ID, LibraryClient from .facade import ( RoboSystemsClients, @@ -165,6 +165,7 @@ "MaterializationResult", # Ledger Client "LedgerClient", + "ReportBundleDownload", # Investor Client "InvestorClient", # Library Client diff --git a/robosystems_client/clients/ledger_client.py b/robosystems_client/clients/ledger_client.py index 1d0dce1..46961d6 100644 --- a/robosystems_client/clients/ledger_client.py +++ b/robosystems_client/clients/ledger_client.py @@ -1793,8 +1793,16 @@ def download_report_bundle( and (when ``to`` is set) the path written. Raises: - RuntimeError: backend returned non-2xx or the presigned URL - could not be followed. + RuntimeError: backend returned non-2xx, the presigned URL + could not be followed, or no API key is configured on the + client. + httpx.TimeoutException: the request exceeded ``self.timeout`` + (passed through unwrapped so callers with their own + retry / backoff strategy can distinguish a timeout from a + generic failure). + httpx.RequestError: any other transport-level failure + (DNS, connection refused, TLS); not wrapped so the original + networking context surfaces in tracebacks. """ if not self.token: raise RuntimeError("No API key provided. Set X-API-Key in headers.") diff --git a/robosystems_client/models/__init__.py b/robosystems_client/models/__init__.py index b45a50a..64b60b5 100644 --- a/robosystems_client/models/__init__.py +++ b/robosystems_client/models/__init__.py @@ -208,6 +208,9 @@ from .get_operation_status_response_getoperationstatus import ( GetOperationStatusResponseGetoperationstatus, ) +from .get_report_bundle_download_url_report_bundle_download_response import ( + GetReportBundleDownloadUrlReportBundleDownloadResponse, +) from .graph_capacity_response import GraphCapacityResponse from .graph_info import GraphInfo from .graph_limits_response import GraphLimitsResponse @@ -851,6 +854,7 @@ "GetCurrentAuthUserResponseGetcurrentauthuser", "GetFileInfoResponse", "GetOperationStatusResponseGetoperationstatus", + "GetReportBundleDownloadUrlReportBundleDownloadResponse", "GraphCapacityResponse", "GraphInfo", "GraphLimitsResponse", diff --git a/robosystems_client/models/get_report_bundle_download_url_report_bundle_download_response.py b/robosystems_client/models/get_report_bundle_download_url_report_bundle_download_response.py new file mode 100644 index 0000000..c6eb91a --- /dev/null +++ b/robosystems_client/models/get_report_bundle_download_url_report_bundle_download_response.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +import datetime +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field +from dateutil.parser import isoparse + +T = TypeVar("T", bound="GetReportBundleDownloadUrlReportBundleDownloadResponse") + + +@_attrs_define +class GetReportBundleDownloadUrlReportBundleDownloadResponse: + """Presigned-URL response for a Report bundle download. + + Mirrors :class:`BackupDownloadUrlResponse` in shape — the frontend + treats both the same way (fetch, follow URL, GET the artifact). + + Only returned for RDF-family flavors (JSON-LD) where the artifact + is stored in S3. XBRL flavors stream the binary content directly + in the response body (no JSON wrapper). + + Attributes: + download_url (str): Presigned URL that streams the bundle directly from S3. + expires_at (datetime.datetime): UTC timestamp at which the presigned URL stops working. + content_type (str): MIME type of the artifact behind the URL. + format_ (str): Serialization flavor delivered by this URL — matches the ``format`` query parameter. + generation_count (int): Bundle generation number stamped on the Report. + """ + + download_url: str + expires_at: datetime.datetime + content_type: str + format_: str + generation_count: int + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + download_url = self.download_url + + expires_at = self.expires_at.isoformat() + + content_type = self.content_type + + format_ = self.format_ + + generation_count = self.generation_count + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "download_url": download_url, + "expires_at": expires_at, + "content_type": content_type, + "format": format_, + "generation_count": generation_count, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + download_url = d.pop("download_url") + + expires_at = isoparse(d.pop("expires_at")) + + content_type = d.pop("content_type") + + format_ = d.pop("format") + + generation_count = d.pop("generation_count") + + get_report_bundle_download_url_report_bundle_download_response = cls( + download_url=download_url, + expires_at=expires_at, + content_type=content_type, + format_=format_, + generation_count=generation_count, + ) + + get_report_bundle_download_url_report_bundle_download_response.additional_properties = d + return get_report_bundle_download_url_report_bundle_download_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties