From a6961b7cb4aabe0beb476b357daa229b1416256a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 20:11:56 +0000 Subject: [PATCH 1/4] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f40d116..0e228e1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 23 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/browserbase-b2831c9c836f039762834825afdc20569587a825d29ac5c3748c78b009bf059b.yml -openapi_spec_hash: dd85a934900cb6583f12ebf6117be884 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/browserbase-064acee5b1a935d704b776ae56c254b445b6d8314beccf2f9c6d7068d30a324e.yml +openapi_spec_hash: a9fae77266f1f3c17fabc2c69ae1d165 config_hash: 40fbac80e24faaa0dc19e93368bcd821 From effc74e8ad794d62472baf66fcd620af2d6688df Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 18:46:50 +0000 Subject: [PATCH 2/4] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0e228e1..635b017 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 23 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/browserbase-064acee5b1a935d704b776ae56c254b445b6d8314beccf2f9c6d7068d30a324e.yml -openapi_spec_hash: a9fae77266f1f3c17fabc2c69ae1d165 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/browserbase-e558ae375c808495dd9e1ed1e305f8933d9418d380c395412764530d74d74895.yml +openapi_spec_hash: 518fdefff1eabc4bb8a3b54ddf7fa293 config_hash: 40fbac80e24faaa0dc19e93368bcd821 From 5ed9746938237ae4eaa78f8652a9b576e6be0e7c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 19:56:15 +0000 Subject: [PATCH 3/4] feat: [CORE-2194][apps/api] Add BYOC (cert) CRUD to SDKs --- .stats.yml | 6 +- README.md | 2 +- api.md | 15 + src/browserbase/_client.py | 39 +- src/browserbase/resources/__init__.py | 14 + src/browserbase/resources/certificates.py | 389 ++++++++++++++++++ src/browserbase/types/__init__.py | 3 + src/browserbase/types/certificate.py | 20 + .../types/certificate_create_params.py | 13 + .../types/certificate_list_response.py | 10 + tests/api_resources/test_certificates.py | 288 +++++++++++++ 11 files changed, 794 insertions(+), 5 deletions(-) create mode 100644 src/browserbase/resources/certificates.py create mode 100644 src/browserbase/types/certificate.py create mode 100644 src/browserbase/types/certificate_create_params.py create mode 100644 src/browserbase/types/certificate_list_response.py create mode 100644 tests/api_resources/test_certificates.py diff --git a/.stats.yml b/.stats.yml index 635b017..17cb18b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 23 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/browserbase-e558ae375c808495dd9e1ed1e305f8933d9418d380c395412764530d74d74895.yml +configured_endpoints: 27 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/browserbase-9bab373fc62ce19147560ed3ec29fe09ad59d9a5b406d9ed21a22f15a511d9cb.yml openapi_spec_hash: 518fdefff1eabc4bb8a3b54ddf7fa293 -config_hash: 40fbac80e24faaa0dc19e93368bcd821 +config_hash: d4b0c534eaf7665ea25168e0e824c9d3 diff --git a/README.md b/README.md index 715a907..743defe 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ from browserbase import Browserbase client = Browserbase() -client.extensions.create( +client.certificates.create( file=Path("/path/to/file"), ) ``` diff --git a/api.md b/api.md index 581574a..c3a3f4e 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,18 @@ +# Certificates + +Types: + +```python +from browserbase.types import Certificate, CertificateListResponse +``` + +Methods: + +- client.certificates.create(\*\*params) -> Certificate +- client.certificates.retrieve(id) -> Certificate +- client.certificates.list() -> CertificateListResponse +- client.certificates.delete(id) -> None + # Contexts Types: diff --git a/src/browserbase/_client.py b/src/browserbase/_client.py index b64caef..d86814d 100644 --- a/src/browserbase/_client.py +++ b/src/browserbase/_client.py @@ -35,12 +35,13 @@ ) if TYPE_CHECKING: - from .resources import search, contexts, projects, sessions, fetch_api, extensions + from .resources import search, contexts, projects, sessions, fetch_api, extensions, certificates from .resources.search import SearchResource, AsyncSearchResource from .resources.contexts import ContextsResource, AsyncContextsResource from .resources.projects import ProjectsResource, AsyncProjectsResource from .resources.fetch_api import FetchAPIResource, AsyncFetchAPIResource from .resources.extensions import ExtensionsResource, AsyncExtensionsResource + from .resources.certificates import CertificatesResource, AsyncCertificatesResource from .resources.sessions.sessions import SessionsResource, AsyncSessionsResource __all__ = [ @@ -119,6 +120,12 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + @cached_property + def certificates(self) -> CertificatesResource: + from .resources.certificates import CertificatesResource + + return CertificatesResource(self) + @cached_property def contexts(self) -> ContextsResource: from .resources.contexts import ContextsResource @@ -332,6 +339,12 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + @cached_property + def certificates(self) -> AsyncCertificatesResource: + from .resources.certificates import AsyncCertificatesResource + + return AsyncCertificatesResource(self) + @cached_property def contexts(self) -> AsyncContextsResource: from .resources.contexts import AsyncContextsResource @@ -487,6 +500,12 @@ class BrowserbaseWithRawResponse: def __init__(self, client: Browserbase) -> None: self._client = client + @cached_property + def certificates(self) -> certificates.CertificatesResourceWithRawResponse: + from .resources.certificates import CertificatesResourceWithRawResponse + + return CertificatesResourceWithRawResponse(self._client.certificates) + @cached_property def contexts(self) -> contexts.ContextsResourceWithRawResponse: from .resources.contexts import ContextsResourceWithRawResponse @@ -530,6 +549,12 @@ class AsyncBrowserbaseWithRawResponse: def __init__(self, client: AsyncBrowserbase) -> None: self._client = client + @cached_property + def certificates(self) -> certificates.AsyncCertificatesResourceWithRawResponse: + from .resources.certificates import AsyncCertificatesResourceWithRawResponse + + return AsyncCertificatesResourceWithRawResponse(self._client.certificates) + @cached_property def contexts(self) -> contexts.AsyncContextsResourceWithRawResponse: from .resources.contexts import AsyncContextsResourceWithRawResponse @@ -573,6 +598,12 @@ class BrowserbaseWithStreamedResponse: def __init__(self, client: Browserbase) -> None: self._client = client + @cached_property + def certificates(self) -> certificates.CertificatesResourceWithStreamingResponse: + from .resources.certificates import CertificatesResourceWithStreamingResponse + + return CertificatesResourceWithStreamingResponse(self._client.certificates) + @cached_property def contexts(self) -> contexts.ContextsResourceWithStreamingResponse: from .resources.contexts import ContextsResourceWithStreamingResponse @@ -616,6 +647,12 @@ class AsyncBrowserbaseWithStreamedResponse: def __init__(self, client: AsyncBrowserbase) -> None: self._client = client + @cached_property + def certificates(self) -> certificates.AsyncCertificatesResourceWithStreamingResponse: + from .resources.certificates import AsyncCertificatesResourceWithStreamingResponse + + return AsyncCertificatesResourceWithStreamingResponse(self._client.certificates) + @cached_property def contexts(self) -> contexts.AsyncContextsResourceWithStreamingResponse: from .resources.contexts import AsyncContextsResourceWithStreamingResponse diff --git a/src/browserbase/resources/__init__.py b/src/browserbase/resources/__init__.py index 83a8788..6bd0988 100644 --- a/src/browserbase/resources/__init__.py +++ b/src/browserbase/resources/__init__.py @@ -48,8 +48,22 @@ ExtensionsResourceWithStreamingResponse, AsyncExtensionsResourceWithStreamingResponse, ) +from .certificates import ( + CertificatesResource, + AsyncCertificatesResource, + CertificatesResourceWithRawResponse, + AsyncCertificatesResourceWithRawResponse, + CertificatesResourceWithStreamingResponse, + AsyncCertificatesResourceWithStreamingResponse, +) __all__ = [ + "CertificatesResource", + "AsyncCertificatesResource", + "CertificatesResourceWithRawResponse", + "AsyncCertificatesResourceWithRawResponse", + "CertificatesResourceWithStreamingResponse", + "AsyncCertificatesResourceWithStreamingResponse", "ContextsResource", "AsyncContextsResource", "ContextsResourceWithRawResponse", diff --git a/src/browserbase/resources/certificates.py b/src/browserbase/resources/certificates.py new file mode 100644 index 0000000..a617e77 --- /dev/null +++ b/src/browserbase/resources/certificates.py @@ -0,0 +1,389 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Mapping, cast + +import httpx + +from ..types import certificate_create_params +from .._files import deepcopy_with_paths +from .._types import Body, Query, Headers, NoneType, NotGiven, FileTypes, not_given +from .._utils import extract_files, path_template, maybe_transform, async_maybe_transform +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.certificate import Certificate +from ..types.certificate_list_response import CertificateListResponse + +__all__ = ["CertificatesResource", "AsyncCertificatesResource"] + + +class CertificatesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CertificatesResourceWithRawResponse: + """ + 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 CertificatesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CertificatesResourceWithStreamingResponse: + """ + 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 CertificatesResourceWithStreamingResponse(self) + + def create( + self, + *, + file: FileTypes, + # 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, + ) -> Certificate: + """ + Upload a Certificate + + 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 + """ + body = deepcopy_with_paths({"file": file}, [["file"]]) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/v1/certificates", + body=maybe_transform(body, certificate_create_params.CertificateCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Certificate, + ) + + def retrieve( + 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, + ) -> Certificate: + """ + Get a Certificate + + 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/certificates/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Certificate, + ) + + def list( + self, + *, + # 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, + ) -> CertificateListResponse: + """List Certificates""" + return self._get( + "/v1/certificates", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CertificateListResponse, + ) + + def delete( + 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, + ) -> None: + """ + Delete a Certificate + + 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}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + path_template("/v1/certificates/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncCertificatesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCertificatesResourceWithRawResponse: + """ + 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 AsyncCertificatesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCertificatesResourceWithStreamingResponse: + """ + 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 AsyncCertificatesResourceWithStreamingResponse(self) + + async def create( + self, + *, + file: FileTypes, + # 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, + ) -> Certificate: + """ + Upload a Certificate + + 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 + """ + body = deepcopy_with_paths({"file": file}, [["file"]]) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/v1/certificates", + body=await async_maybe_transform(body, certificate_create_params.CertificateCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Certificate, + ) + + async def retrieve( + 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, + ) -> Certificate: + """ + Get a Certificate + + 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/certificates/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Certificate, + ) + + async def list( + self, + *, + # 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, + ) -> CertificateListResponse: + """List Certificates""" + return await self._get( + "/v1/certificates", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CertificateListResponse, + ) + + async def delete( + 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, + ) -> None: + """ + Delete a Certificate + + 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}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + path_template("/v1/certificates/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class CertificatesResourceWithRawResponse: + def __init__(self, certificates: CertificatesResource) -> None: + self._certificates = certificates + + self.create = to_raw_response_wrapper( + certificates.create, + ) + self.retrieve = to_raw_response_wrapper( + certificates.retrieve, + ) + self.list = to_raw_response_wrapper( + certificates.list, + ) + self.delete = to_raw_response_wrapper( + certificates.delete, + ) + + +class AsyncCertificatesResourceWithRawResponse: + def __init__(self, certificates: AsyncCertificatesResource) -> None: + self._certificates = certificates + + self.create = async_to_raw_response_wrapper( + certificates.create, + ) + self.retrieve = async_to_raw_response_wrapper( + certificates.retrieve, + ) + self.list = async_to_raw_response_wrapper( + certificates.list, + ) + self.delete = async_to_raw_response_wrapper( + certificates.delete, + ) + + +class CertificatesResourceWithStreamingResponse: + def __init__(self, certificates: CertificatesResource) -> None: + self._certificates = certificates + + self.create = to_streamed_response_wrapper( + certificates.create, + ) + self.retrieve = to_streamed_response_wrapper( + certificates.retrieve, + ) + self.list = to_streamed_response_wrapper( + certificates.list, + ) + self.delete = to_streamed_response_wrapper( + certificates.delete, + ) + + +class AsyncCertificatesResourceWithStreamingResponse: + def __init__(self, certificates: AsyncCertificatesResource) -> None: + self._certificates = certificates + + self.create = async_to_streamed_response_wrapper( + certificates.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + certificates.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + certificates.list, + ) + self.delete = async_to_streamed_response_wrapper( + certificates.delete, + ) diff --git a/src/browserbase/types/__init__.py b/src/browserbase/types/__init__.py index 0a9a3b8..72d472a 100644 --- a/src/browserbase/types/__init__.py +++ b/src/browserbase/types/__init__.py @@ -6,6 +6,7 @@ from .project import Project as Project from .session import Session as Session from .extension import Extension as Extension +from .certificate import Certificate as Certificate from .project_usage import ProjectUsage as ProjectUsage from .search_web_params import SearchWebParams as SearchWebParams from .session_live_urls import SessionLiveURLs as SessionLiveURLs @@ -21,5 +22,7 @@ from .extension_create_params import ExtensionCreateParams as ExtensionCreateParams from .fetch_api_create_params import FetchAPICreateParams as FetchAPICreateParams from .session_create_response import SessionCreateResponse as SessionCreateResponse +from .certificate_create_params import CertificateCreateParams as CertificateCreateParams +from .certificate_list_response import CertificateListResponse as CertificateListResponse from .fetch_api_create_response import FetchAPICreateResponse as FetchAPICreateResponse from .session_retrieve_response import SessionRetrieveResponse as SessionRetrieveResponse diff --git a/src/browserbase/types/certificate.py b/src/browserbase/types/certificate.py new file mode 100644 index 0000000..0f59e10 --- /dev/null +++ b/src/browserbase/types/certificate.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["Certificate"] + + +class Certificate(BaseModel): + id: str + + created_at: datetime = FieldInfo(alias="createdAt") + + project_id: str = FieldInfo(alias="projectId") + """The Project ID linked to the uploaded Certificate.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") diff --git a/src/browserbase/types/certificate_create_params.py b/src/browserbase/types/certificate_create_params.py new file mode 100644 index 0000000..577c0e0 --- /dev/null +++ b/src/browserbase/types/certificate_create_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes + +__all__ = ["CertificateCreateParams"] + + +class CertificateCreateParams(TypedDict, total=False): + file: Required[FileTypes] diff --git a/src/browserbase/types/certificate_list_response.py b/src/browserbase/types/certificate_list_response.py new file mode 100644 index 0000000..7a8a8f0 --- /dev/null +++ b/src/browserbase/types/certificate_list_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .certificate import Certificate + +__all__ = ["CertificateListResponse"] + +CertificateListResponse: TypeAlias = List[Certificate] diff --git a/tests/api_resources/test_certificates.py b/tests/api_resources/test_certificates.py new file mode 100644 index 0000000..aaa767e --- /dev/null +++ b/tests/api_resources/test_certificates.py @@ -0,0 +1,288 @@ +# 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 import Certificate, CertificateListResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCertificates: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Browserbase) -> None: + certificate = client.certificates.create( + file=b"Example data", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Browserbase) -> None: + response = client.certificates.with_raw_response.create( + file=b"Example data", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Browserbase) -> None: + with client.certificates.with_streaming_response.create( + file=b"Example data", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: Browserbase) -> None: + certificate = client.certificates.retrieve( + "id", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Browserbase) -> None: + response = client.certificates.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Browserbase) -> None: + with client.certificates.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Browserbase) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.certificates.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: Browserbase) -> None: + certificate = client.certificates.list() + assert_matches_type(CertificateListResponse, certificate, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Browserbase) -> None: + response = client.certificates.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(CertificateListResponse, certificate, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Browserbase) -> None: + with client.certificates.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(CertificateListResponse, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: Browserbase) -> None: + certificate = client.certificates.delete( + "id", + ) + assert certificate is None + + @parametrize + def test_raw_response_delete(self, client: Browserbase) -> None: + response = client.certificates.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert certificate is None + + @parametrize + def test_streaming_response_delete(self, client: Browserbase) -> None: + with client.certificates.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert certificate is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: Browserbase) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.certificates.with_raw_response.delete( + "", + ) + + +class TestAsyncCertificates: + 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: + certificate = await async_client.certificates.create( + file=b"Example data", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncBrowserbase) -> None: + response = await async_client.certificates.with_raw_response.create( + file=b"Example data", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = await response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncBrowserbase) -> None: + async with async_client.certificates.with_streaming_response.create( + file=b"Example data", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncBrowserbase) -> None: + certificate = await async_client.certificates.retrieve( + "id", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncBrowserbase) -> None: + response = await async_client.certificates.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = await response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncBrowserbase) -> None: + async with async_client.certificates.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncBrowserbase) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.certificates.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncBrowserbase) -> None: + certificate = await async_client.certificates.list() + assert_matches_type(CertificateListResponse, certificate, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncBrowserbase) -> None: + response = await async_client.certificates.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = await response.parse() + assert_matches_type(CertificateListResponse, certificate, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncBrowserbase) -> None: + async with async_client.certificates.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(CertificateListResponse, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncBrowserbase) -> None: + certificate = await async_client.certificates.delete( + "id", + ) + assert certificate is None + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncBrowserbase) -> None: + response = await async_client.certificates.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = await response.parse() + assert certificate is None + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncBrowserbase) -> None: + async with async_client.certificates.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert certificate is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncBrowserbase) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.certificates.with_raw_response.delete( + "", + ) From 77928bba161d742f1932c32ca9fe0148e1ee1f40 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 19:56:31 +0000 Subject: [PATCH 4/4] release: 1.12.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/browserbase/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index caf1487..de0960a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.11.0" + ".": "1.12.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 859bfcc..3a9193f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.12.0 (2026-06-05) + +Full Changelog: [v1.11.0...v1.12.0](https://github.com/browserbase/sdk-python/compare/v1.11.0...v1.12.0) + +### Features + +* [CORE-2194][apps/api] Add BYOC (cert) CRUD to SDKs ([5ed9746](https://github.com/browserbase/sdk-python/commit/5ed9746938237ae4eaa78f8652a9b576e6be0e7c)) + ## 1.11.0 (2026-05-20) Full Changelog: [v1.10.0...v1.11.0](https://github.com/browserbase/sdk-python/compare/v1.10.0...v1.11.0) diff --git a/pyproject.toml b/pyproject.toml index 3338ab4..129b166 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "browserbase" -version = "1.11.0" +version = "1.12.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 1184ad6..156b586 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.11.0" # x-release-please-version +__version__ = "1.12.0" # x-release-please-version