diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e72f1131..7ccfe12c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.14.0" + ".": "1.15.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index b4d46dde..1ac83c1a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 36 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/browserbase-e82c289d3d3aabd936a476cf81630587ffe46ab9e9bdebced4b31b8c2f9bc08f.yml -openapi_spec_hash: 473121b283812a3dfd866afe9b61dc7d -config_hash: 1b24ea9fa13645b16b74aa794dbc8190 +configured_endpoints: 38 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/browserbase-98cc68ad9afa71355baf2b31f305a5e2f3a315fd311c96659405eff96b8f2b1e.yml +openapi_spec_hash: 71dfbc1021a33dd7fc9d82844965b1b3 +config_hash: 6209a285dd5980f5c418fe6575723aef diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b1cbfc1..76e15c3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.15.0 (2026-07-14) + +Full Changelog: [v1.14.0...v1.15.0](https://github.com/browserbase/sdk-python/compare/v1.14.0...v1.15.0) + +### Features + +* **api:** manual updates ([39fee02](https://github.com/browserbase/sdk-python/commit/39fee028ec9f64b472dcbe2cac94f044bed79a4b)) + ## 1.14.0 (2026-07-02) Full Changelog: [v1.13.0...v1.14.0](https://github.com/browserbase/sdk-python/compare/v1.13.0...v1.14.0) diff --git a/api.md b/api.md index 39c108f5..8e9c89ba 100644 --- a/api.md +++ b/api.md @@ -130,7 +130,24 @@ from browserbase.types.sessions import SessionRecording, RecordingRetrieveRespon Methods: -- client.sessions.recording.retrieve(id) -> RecordingRetrieveResponse +- client.sessions.recording.retrieve(id) -> RecordingRetrieveResponse + +### Downloads + +Types: + +```python +from browserbase.types.sessions.recording import ( + RecordingDownload, + DownloadCreateResponse, + DownloadListResponse, +) +``` + +Methods: + +- client.sessions.recording.downloads.create(id) -> DownloadCreateResponse +- client.sessions.recording.downloads.list(id) -> DownloadListResponse ## Uploads diff --git a/pyproject.toml b/pyproject.toml index 542ff757..86985708 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "browserbase" -version = "1.14.0" +version = "1.15.0" description = "The official Python library for the Browserbase API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/browserbase/_version.py b/src/browserbase/_version.py index 4df49f49..cb52e162 100644 --- a/src/browserbase/_version.py +++ b/src/browserbase/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "browserbase" -__version__ = "1.14.0" # x-release-please-version +__version__ = "1.15.0" # x-release-please-version diff --git a/src/browserbase/resources/sessions/recording/__init__.py b/src/browserbase/resources/sessions/recording/__init__.py new file mode 100644 index 00000000..99f9205b --- /dev/null +++ b/src/browserbase/resources/sessions/recording/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .downloads import ( + DownloadsResource, + AsyncDownloadsResource, + DownloadsResourceWithRawResponse, + AsyncDownloadsResourceWithRawResponse, + DownloadsResourceWithStreamingResponse, + AsyncDownloadsResourceWithStreamingResponse, +) +from .recording import ( + RecordingResource, + AsyncRecordingResource, + RecordingResourceWithRawResponse, + AsyncRecordingResourceWithRawResponse, + RecordingResourceWithStreamingResponse, + AsyncRecordingResourceWithStreamingResponse, +) + +__all__ = [ + "DownloadsResource", + "AsyncDownloadsResource", + "DownloadsResourceWithRawResponse", + "AsyncDownloadsResourceWithRawResponse", + "DownloadsResourceWithStreamingResponse", + "AsyncDownloadsResourceWithStreamingResponse", + "RecordingResource", + "AsyncRecordingResource", + "RecordingResourceWithRawResponse", + "AsyncRecordingResourceWithRawResponse", + "RecordingResourceWithStreamingResponse", + "AsyncRecordingResourceWithStreamingResponse", +] diff --git a/src/browserbase/resources/sessions/recording/downloads.py b/src/browserbase/resources/sessions/recording/downloads.py new file mode 100644 index 00000000..04cd51bb --- /dev/null +++ b/src/browserbase/resources/sessions/recording/downloads.py @@ -0,0 +1,253 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import path_template +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.sessions.recording.download_list_response import DownloadListResponse +from ....types.sessions.recording.download_create_response import DownloadCreateResponse + +__all__ = ["DownloadsResource", "AsyncDownloadsResource"] + + +class DownloadsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DownloadsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/browserbase/sdk-python#accessing-raw-response-data-eg-headers + """ + return DownloadsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DownloadsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/browserbase/sdk-python#with_streaming_response + """ + return DownloadsResourceWithStreamingResponse(self) + + def create( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DownloadCreateResponse: + """Requests one downloadable MP4 per recorded page of a session. + + Assembly runs + asynchronously and every page returns as `PENDING`. Re-posting re-enqueues all + pages and retries any that failed. Poll the GET endpoint for per-page status + and, on standard (non-BYOS) projects, download URLs. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + path_template("/v1/sessions/{id}/recording/downloads", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DownloadCreateResponse, + ) + + def list( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DownloadListResponse: + """ + Returns the per-page download status for a session, with a short-lived signed + URL for each completed page on standard (non-BYOS) projects. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + path_template("/v1/sessions/{id}/recording/downloads", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DownloadListResponse, + ) + + +class AsyncDownloadsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDownloadsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/browserbase/sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncDownloadsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDownloadsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/browserbase/sdk-python#with_streaming_response + """ + return AsyncDownloadsResourceWithStreamingResponse(self) + + async def create( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DownloadCreateResponse: + """Requests one downloadable MP4 per recorded page of a session. + + Assembly runs + asynchronously and every page returns as `PENDING`. Re-posting re-enqueues all + pages and retries any that failed. Poll the GET endpoint for per-page status + and, on standard (non-BYOS) projects, download URLs. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + path_template("/v1/sessions/{id}/recording/downloads", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DownloadCreateResponse, + ) + + async def list( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DownloadListResponse: + """ + Returns the per-page download status for a session, with a short-lived signed + URL for each completed page on standard (non-BYOS) projects. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + path_template("/v1/sessions/{id}/recording/downloads", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DownloadListResponse, + ) + + +class DownloadsResourceWithRawResponse: + def __init__(self, downloads: DownloadsResource) -> None: + self._downloads = downloads + + self.create = to_raw_response_wrapper( + downloads.create, + ) + self.list = to_raw_response_wrapper( + downloads.list, + ) + + +class AsyncDownloadsResourceWithRawResponse: + def __init__(self, downloads: AsyncDownloadsResource) -> None: + self._downloads = downloads + + self.create = async_to_raw_response_wrapper( + downloads.create, + ) + self.list = async_to_raw_response_wrapper( + downloads.list, + ) + + +class DownloadsResourceWithStreamingResponse: + def __init__(self, downloads: DownloadsResource) -> None: + self._downloads = downloads + + self.create = to_streamed_response_wrapper( + downloads.create, + ) + self.list = to_streamed_response_wrapper( + downloads.list, + ) + + +class AsyncDownloadsResourceWithStreamingResponse: + def __init__(self, downloads: AsyncDownloadsResource) -> None: + self._downloads = downloads + + self.create = async_to_streamed_response_wrapper( + downloads.create, + ) + self.list = async_to_streamed_response_wrapper( + downloads.list, + ) diff --git a/src/browserbase/resources/sessions/recording.py b/src/browserbase/resources/sessions/recording/recording.py similarity index 78% rename from src/browserbase/resources/sessions/recording.py rename to src/browserbase/resources/sessions/recording/recording.py index 57e2d44e..18394e0d 100644 --- a/src/browserbase/resources/sessions/recording.py +++ b/src/browserbase/resources/sessions/recording/recording.py @@ -4,23 +4,35 @@ import httpx -from ..._types import Body, Query, Headers, NotGiven, not_given -from ..._utils import path_template -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import path_template +from .downloads import ( + DownloadsResource, + AsyncDownloadsResource, + DownloadsResourceWithRawResponse, + AsyncDownloadsResourceWithRawResponse, + DownloadsResourceWithStreamingResponse, + AsyncDownloadsResourceWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options -from ...types.sessions.recording_retrieve_response import RecordingRetrieveResponse +from ...._base_client import make_request_options +from ....types.sessions.recording_retrieve_response import RecordingRetrieveResponse __all__ = ["RecordingResource", "AsyncRecordingResource"] class RecordingResource(SyncAPIResource): + @cached_property + def downloads(self) -> DownloadsResource: + return DownloadsResource(self._client) + @cached_property def with_raw_response(self) -> RecordingResourceWithRawResponse: """ @@ -75,6 +87,10 @@ def retrieve( class AsyncRecordingResource(AsyncAPIResource): + @cached_property + def downloads(self) -> AsyncDownloadsResource: + return AsyncDownloadsResource(self._client) + @cached_property def with_raw_response(self) -> AsyncRecordingResourceWithRawResponse: """ @@ -136,6 +152,10 @@ def __init__(self, recording: RecordingResource) -> None: recording.retrieve, ) + @cached_property + def downloads(self) -> DownloadsResourceWithRawResponse: + return DownloadsResourceWithRawResponse(self._recording.downloads) + class AsyncRecordingResourceWithRawResponse: def __init__(self, recording: AsyncRecordingResource) -> None: @@ -145,6 +165,10 @@ def __init__(self, recording: AsyncRecordingResource) -> None: recording.retrieve, ) + @cached_property + def downloads(self) -> AsyncDownloadsResourceWithRawResponse: + return AsyncDownloadsResourceWithRawResponse(self._recording.downloads) + class RecordingResourceWithStreamingResponse: def __init__(self, recording: RecordingResource) -> None: @@ -154,6 +178,10 @@ def __init__(self, recording: RecordingResource) -> None: recording.retrieve, ) + @cached_property + def downloads(self) -> DownloadsResourceWithStreamingResponse: + return DownloadsResourceWithStreamingResponse(self._recording.downloads) + class AsyncRecordingResourceWithStreamingResponse: def __init__(self, recording: AsyncRecordingResource) -> None: @@ -162,3 +190,7 @@ def __init__(self, recording: AsyncRecordingResource) -> None: self.retrieve = async_to_streamed_response_wrapper( recording.retrieve, ) + + @cached_property + def downloads(self) -> AsyncDownloadsResourceWithStreamingResponse: + return AsyncDownloadsResourceWithStreamingResponse(self._recording.downloads) diff --git a/src/browserbase/resources/sessions/sessions.py b/src/browserbase/resources/sessions/sessions.py index 8cf81349..4a2210c0 100644 --- a/src/browserbase/resources/sessions/sessions.py +++ b/src/browserbase/resources/sessions/sessions.py @@ -43,14 +43,6 @@ DownloadsResourceWithStreamingResponse, AsyncDownloadsResourceWithStreamingResponse, ) -from .recording import ( - RecordingResource, - AsyncRecordingResource, - RecordingResourceWithRawResponse, - AsyncRecordingResourceWithRawResponse, - RecordingResourceWithStreamingResponse, - AsyncRecordingResourceWithStreamingResponse, -) from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( to_raw_response_wrapper, @@ -60,6 +52,14 @@ ) from ..._base_client import make_request_options from ...types.session import Session +from .recording.recording import ( + RecordingResource, + AsyncRecordingResource, + RecordingResourceWithRawResponse, + AsyncRecordingResourceWithRawResponse, + RecordingResourceWithStreamingResponse, + AsyncRecordingResourceWithStreamingResponse, +) from ...types.session_live_urls import SessionLiveURLs from ...types.session_list_response import SessionListResponse from ...types.session_create_response import SessionCreateResponse diff --git a/src/browserbase/types/sessions/recording/__init__.py b/src/browserbase/types/sessions/recording/__init__.py new file mode 100644 index 00000000..ca016d7f --- /dev/null +++ b/src/browserbase/types/sessions/recording/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .recording_download import RecordingDownload as RecordingDownload +from .download_list_response import DownloadListResponse as DownloadListResponse +from .download_create_response import DownloadCreateResponse as DownloadCreateResponse diff --git a/src/browserbase/types/sessions/recording/download_create_response.py b/src/browserbase/types/sessions/recording/download_create_response.py new file mode 100644 index 00000000..74815964 --- /dev/null +++ b/src/browserbase/types/sessions/recording/download_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ...._models import BaseModel +from .recording_download import RecordingDownload + +__all__ = ["DownloadCreateResponse"] + + +class DownloadCreateResponse(BaseModel): + downloads: List[RecordingDownload] diff --git a/src/browserbase/types/sessions/recording/download_list_response.py b/src/browserbase/types/sessions/recording/download_list_response.py new file mode 100644 index 00000000..2eb3b4a0 --- /dev/null +++ b/src/browserbase/types/sessions/recording/download_list_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ...._models import BaseModel +from .recording_download import RecordingDownload + +__all__ = ["DownloadListResponse"] + + +class DownloadListResponse(BaseModel): + downloads: List[RecordingDownload] diff --git a/src/browserbase/types/sessions/recording/recording_download.py b/src/browserbase/types/sessions/recording/recording_download.py new file mode 100644 index 00000000..c6931e44 --- /dev/null +++ b/src/browserbase/types/sessions/recording/recording_download.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel + +__all__ = ["RecordingDownload"] + + +class RecordingDownload(BaseModel): + page_id: str = FieldInfo(alias="pageId") + """Recorded page (tab) within the session, e.g. "0", "1".""" + + status: Literal["NOT_REQUESTED", "PENDING", "COMPLETED", "FAILED"] + """Per-page MP4 assembly state. + + `NOT_REQUESTED`: no download has been requested for the session yet. `PENDING`: + assembly is enqueued or in progress. `COMPLETED`: the MP4 is ready. `FAILED`: + assembly failed; POST again to retry. + """ + + completed_at: Optional[datetime] = FieldInfo(alias="completedAt", default=None) + """When the MP4 was created. + + Present only when COMPLETED on a standard (non-BYOS) project. + """ + + download_url: Optional[str] = FieldInfo(alias="downloadUrl", default=None) + """Short-lived signed CDN URL, re-minted each GET. + + Present only when COMPLETED on a standard (non-BYOS) project. + """ diff --git a/tests/api_resources/sessions/recording/__init__.py b/tests/api_resources/sessions/recording/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/sessions/recording/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/sessions/recording/test_downloads.py b/tests/api_resources/sessions/recording/test_downloads.py new file mode 100644 index 00000000..411cc83c --- /dev/null +++ b/tests/api_resources/sessions/recording/test_downloads.py @@ -0,0 +1,176 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from browserbase import Browserbase, AsyncBrowserbase +from tests.utils import assert_matches_type +from browserbase.types.sessions.recording import DownloadListResponse, DownloadCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDownloads: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Browserbase) -> None: + download = client.sessions.recording.downloads.create( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(DownloadCreateResponse, download, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Browserbase) -> None: + response = client.sessions.recording.downloads.with_raw_response.create( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + download = response.parse() + assert_matches_type(DownloadCreateResponse, download, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Browserbase) -> None: + with client.sessions.recording.downloads.with_streaming_response.create( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + download = response.parse() + assert_matches_type(DownloadCreateResponse, download, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: Browserbase) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.sessions.recording.downloads.with_raw_response.create( + "", + ) + + @parametrize + def test_method_list(self, client: Browserbase) -> None: + download = client.sessions.recording.downloads.list( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(DownloadListResponse, download, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Browserbase) -> None: + response = client.sessions.recording.downloads.with_raw_response.list( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + download = response.parse() + assert_matches_type(DownloadListResponse, download, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Browserbase) -> None: + with client.sessions.recording.downloads.with_streaming_response.list( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + download = response.parse() + assert_matches_type(DownloadListResponse, download, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Browserbase) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.sessions.recording.downloads.with_raw_response.list( + "", + ) + + +class TestAsyncDownloads: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncBrowserbase) -> None: + download = await async_client.sessions.recording.downloads.create( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(DownloadCreateResponse, download, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncBrowserbase) -> None: + response = await async_client.sessions.recording.downloads.with_raw_response.create( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + download = await response.parse() + assert_matches_type(DownloadCreateResponse, download, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncBrowserbase) -> None: + async with async_client.sessions.recording.downloads.with_streaming_response.create( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + download = await response.parse() + assert_matches_type(DownloadCreateResponse, download, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncBrowserbase) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.sessions.recording.downloads.with_raw_response.create( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncBrowserbase) -> None: + download = await async_client.sessions.recording.downloads.list( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(DownloadListResponse, download, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncBrowserbase) -> None: + response = await async_client.sessions.recording.downloads.with_raw_response.list( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + download = await response.parse() + assert_matches_type(DownloadListResponse, download, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncBrowserbase) -> None: + async with async_client.sessions.recording.downloads.with_streaming_response.list( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + download = await response.parse() + assert_matches_type(DownloadListResponse, download, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncBrowserbase) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.sessions.recording.downloads.with_raw_response.list( + "", + )