diff --git a/.stats.yml b/.stats.yml
index 71dc4743573..8a906983a5c 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,2 +1,2 @@
-configured_endpoints: 1448
+configured_endpoints: 1451
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-4acaaed718bd08d16e3866d5ad032fbf2bbfeb978df2cf5164edb81fe41e4f89.yml
diff --git a/api.md b/api.md
index 692bfb6d3d5..d8ca8a5e09d 100644
--- a/api.md
+++ b/api.md
@@ -4586,6 +4586,20 @@ Methods:
- client.r2.buckets.lifecycle.update(bucket_name, \*, account_id, \*\*params) -> object
- client.r2.buckets.lifecycle.get(bucket_name, \*, account_id) -> LifecycleGetResponse
+### CORS
+
+Types:
+
+```python
+from cloudflare.types.r2.buckets import CORSUpdateResponse, CORSDeleteResponse, CORSGetResponse
+```
+
+Methods:
+
+- client.r2.buckets.cors.update(bucket_name, \*, account_id, \*\*params) -> object
+- client.r2.buckets.cors.delete(bucket_name, \*, account_id) -> object
+- client.r2.buckets.cors.get(bucket_name, \*, account_id) -> CORSGetResponse
+
### Domains
#### Custom
diff --git a/src/cloudflare/resources/r2/buckets/__init__.py b/src/cloudflare/resources/r2/buckets/__init__.py
index d6667f2b1e4..9de75b3eb9e 100644
--- a/src/cloudflare/resources/r2/buckets/__init__.py
+++ b/src/cloudflare/resources/r2/buckets/__init__.py
@@ -1,5 +1,13 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from .cors import (
+ CORSResource,
+ AsyncCORSResource,
+ CORSResourceWithRawResponse,
+ AsyncCORSResourceWithRawResponse,
+ CORSResourceWithStreamingResponse,
+ AsyncCORSResourceWithStreamingResponse,
+)
from .sippy import (
SippyResource,
AsyncSippyResource,
@@ -48,6 +56,12 @@
"AsyncLifecycleResourceWithRawResponse",
"LifecycleResourceWithStreamingResponse",
"AsyncLifecycleResourceWithStreamingResponse",
+ "CORSResource",
+ "AsyncCORSResource",
+ "CORSResourceWithRawResponse",
+ "AsyncCORSResourceWithRawResponse",
+ "CORSResourceWithStreamingResponse",
+ "AsyncCORSResourceWithStreamingResponse",
"DomainsResource",
"AsyncDomainsResource",
"DomainsResourceWithRawResponse",
diff --git a/src/cloudflare/resources/r2/buckets/buckets.py b/src/cloudflare/resources/r2/buckets/buckets.py
index cc59ac8c146..173f59d81a8 100644
--- a/src/cloudflare/resources/r2/buckets/buckets.py
+++ b/src/cloudflare/resources/r2/buckets/buckets.py
@@ -7,6 +7,14 @@
import httpx
+from .cors import (
+ CORSResource,
+ AsyncCORSResource,
+ CORSResourceWithRawResponse,
+ AsyncCORSResourceWithRawResponse,
+ CORSResourceWithStreamingResponse,
+ AsyncCORSResourceWithStreamingResponse,
+)
from .sippy import (
SippyResource,
AsyncSippyResource,
@@ -70,6 +78,10 @@ class BucketsResource(SyncAPIResource):
def lifecycle(self) -> LifecycleResource:
return LifecycleResource(self._client)
+ @cached_property
+ def cors(self) -> CORSResource:
+ return CORSResource(self._client)
+
@cached_property
def domains(self) -> DomainsResource:
return DomainsResource(self._client)
@@ -357,6 +369,10 @@ class AsyncBucketsResource(AsyncAPIResource):
def lifecycle(self) -> AsyncLifecycleResource:
return AsyncLifecycleResource(self._client)
+ @cached_property
+ def cors(self) -> AsyncCORSResource:
+ return AsyncCORSResource(self._client)
+
@cached_property
def domains(self) -> AsyncDomainsResource:
return AsyncDomainsResource(self._client)
@@ -660,6 +676,10 @@ def __init__(self, buckets: BucketsResource) -> None:
def lifecycle(self) -> LifecycleResourceWithRawResponse:
return LifecycleResourceWithRawResponse(self._buckets.lifecycle)
+ @cached_property
+ def cors(self) -> CORSResourceWithRawResponse:
+ return CORSResourceWithRawResponse(self._buckets.cors)
+
@cached_property
def domains(self) -> DomainsResourceWithRawResponse:
return DomainsResourceWithRawResponse(self._buckets.domains)
@@ -694,6 +714,10 @@ def __init__(self, buckets: AsyncBucketsResource) -> None:
def lifecycle(self) -> AsyncLifecycleResourceWithRawResponse:
return AsyncLifecycleResourceWithRawResponse(self._buckets.lifecycle)
+ @cached_property
+ def cors(self) -> AsyncCORSResourceWithRawResponse:
+ return AsyncCORSResourceWithRawResponse(self._buckets.cors)
+
@cached_property
def domains(self) -> AsyncDomainsResourceWithRawResponse:
return AsyncDomainsResourceWithRawResponse(self._buckets.domains)
@@ -728,6 +752,10 @@ def __init__(self, buckets: BucketsResource) -> None:
def lifecycle(self) -> LifecycleResourceWithStreamingResponse:
return LifecycleResourceWithStreamingResponse(self._buckets.lifecycle)
+ @cached_property
+ def cors(self) -> CORSResourceWithStreamingResponse:
+ return CORSResourceWithStreamingResponse(self._buckets.cors)
+
@cached_property
def domains(self) -> DomainsResourceWithStreamingResponse:
return DomainsResourceWithStreamingResponse(self._buckets.domains)
@@ -762,6 +790,10 @@ def __init__(self, buckets: AsyncBucketsResource) -> None:
def lifecycle(self) -> AsyncLifecycleResourceWithStreamingResponse:
return AsyncLifecycleResourceWithStreamingResponse(self._buckets.lifecycle)
+ @cached_property
+ def cors(self) -> AsyncCORSResourceWithStreamingResponse:
+ return AsyncCORSResourceWithStreamingResponse(self._buckets.cors)
+
@cached_property
def domains(self) -> AsyncDomainsResourceWithStreamingResponse:
return AsyncDomainsResourceWithStreamingResponse(self._buckets.domains)
diff --git a/src/cloudflare/resources/r2/buckets/cors.py b/src/cloudflare/resources/r2/buckets/cors.py
new file mode 100644
index 00000000000..b028f6feec6
--- /dev/null
+++ b/src/cloudflare/resources/r2/buckets/cors.py
@@ -0,0 +1,454 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Type, Iterable, cast
+from typing_extensions import Literal
+
+import httpx
+
+from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ...._utils import (
+ is_given,
+ maybe_transform,
+ strip_not_given,
+ 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 ...._wrappers import ResultWrapper
+from ...._base_client import make_request_options
+from ....types.r2.buckets import cors_update_params
+from ....types.r2.buckets.cors_get_response import CORSGetResponse
+
+__all__ = ["CORSResource", "AsyncCORSResource"]
+
+
+class CORSResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> CORSResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return the
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return CORSResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> CORSResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return CORSResourceWithStreamingResponse(self)
+
+ def update(
+ self,
+ bucket_name: str,
+ *,
+ account_id: str,
+ rules: Iterable[cors_update_params.Rule] | NotGiven = NOT_GIVEN,
+ cf_r2_jurisdiction: Literal["default", "eu", "fedramp"] | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> object:
+ """
+ Set the CORS policy for a bucket
+
+ Args:
+ account_id: Account ID
+
+ bucket_name: Name of the bucket
+
+ cf_r2_jurisdiction: The bucket jurisdiction
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not bucket_name:
+ raise ValueError(f"Expected a non-empty value for `bucket_name` but received {bucket_name!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"cf-r2-jurisdiction": str(cf_r2_jurisdiction) if is_given(cf_r2_jurisdiction) else NOT_GIVEN}
+ ),
+ **(extra_headers or {}),
+ }
+ return self._put(
+ f"/accounts/{account_id}/r2/buckets/{bucket_name}/cors",
+ body=maybe_transform({"rules": rules}, cors_update_params.CORSUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[object]._unwrapper,
+ ),
+ cast_to=cast(Type[object], ResultWrapper[object]),
+ )
+
+ def delete(
+ self,
+ bucket_name: str,
+ *,
+ account_id: str,
+ cf_r2_jurisdiction: Literal["default", "eu", "fedramp"] | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> object:
+ """
+ Delete the CORS policy for a bucket
+
+ Args:
+ account_id: Account ID
+
+ bucket_name: Name of the bucket
+
+ cf_r2_jurisdiction: The bucket jurisdiction
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not bucket_name:
+ raise ValueError(f"Expected a non-empty value for `bucket_name` but received {bucket_name!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"cf-r2-jurisdiction": str(cf_r2_jurisdiction) if is_given(cf_r2_jurisdiction) else NOT_GIVEN}
+ ),
+ **(extra_headers or {}),
+ }
+ return self._delete(
+ f"/accounts/{account_id}/r2/buckets/{bucket_name}/cors",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[object]._unwrapper,
+ ),
+ cast_to=cast(Type[object], ResultWrapper[object]),
+ )
+
+ def get(
+ self,
+ bucket_name: str,
+ *,
+ account_id: str,
+ cf_r2_jurisdiction: Literal["default", "eu", "fedramp"] | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> CORSGetResponse:
+ """
+ Get the CORS policy for a bucket
+
+ Args:
+ account_id: Account ID
+
+ bucket_name: Name of the bucket
+
+ cf_r2_jurisdiction: The bucket jurisdiction
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not bucket_name:
+ raise ValueError(f"Expected a non-empty value for `bucket_name` but received {bucket_name!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"cf-r2-jurisdiction": str(cf_r2_jurisdiction) if is_given(cf_r2_jurisdiction) else NOT_GIVEN}
+ ),
+ **(extra_headers or {}),
+ }
+ return self._get(
+ f"/accounts/{account_id}/r2/buckets/{bucket_name}/cors",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[CORSGetResponse]._unwrapper,
+ ),
+ cast_to=cast(Type[CORSGetResponse], ResultWrapper[CORSGetResponse]),
+ )
+
+
+class AsyncCORSResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncCORSResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return the
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncCORSResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncCORSResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return AsyncCORSResourceWithStreamingResponse(self)
+
+ async def update(
+ self,
+ bucket_name: str,
+ *,
+ account_id: str,
+ rules: Iterable[cors_update_params.Rule] | NotGiven = NOT_GIVEN,
+ cf_r2_jurisdiction: Literal["default", "eu", "fedramp"] | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> object:
+ """
+ Set the CORS policy for a bucket
+
+ Args:
+ account_id: Account ID
+
+ bucket_name: Name of the bucket
+
+ cf_r2_jurisdiction: The bucket jurisdiction
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not bucket_name:
+ raise ValueError(f"Expected a non-empty value for `bucket_name` but received {bucket_name!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"cf-r2-jurisdiction": str(cf_r2_jurisdiction) if is_given(cf_r2_jurisdiction) else NOT_GIVEN}
+ ),
+ **(extra_headers or {}),
+ }
+ return await self._put(
+ f"/accounts/{account_id}/r2/buckets/{bucket_name}/cors",
+ body=await async_maybe_transform({"rules": rules}, cors_update_params.CORSUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[object]._unwrapper,
+ ),
+ cast_to=cast(Type[object], ResultWrapper[object]),
+ )
+
+ async def delete(
+ self,
+ bucket_name: str,
+ *,
+ account_id: str,
+ cf_r2_jurisdiction: Literal["default", "eu", "fedramp"] | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> object:
+ """
+ Delete the CORS policy for a bucket
+
+ Args:
+ account_id: Account ID
+
+ bucket_name: Name of the bucket
+
+ cf_r2_jurisdiction: The bucket jurisdiction
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not bucket_name:
+ raise ValueError(f"Expected a non-empty value for `bucket_name` but received {bucket_name!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"cf-r2-jurisdiction": str(cf_r2_jurisdiction) if is_given(cf_r2_jurisdiction) else NOT_GIVEN}
+ ),
+ **(extra_headers or {}),
+ }
+ return await self._delete(
+ f"/accounts/{account_id}/r2/buckets/{bucket_name}/cors",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[object]._unwrapper,
+ ),
+ cast_to=cast(Type[object], ResultWrapper[object]),
+ )
+
+ async def get(
+ self,
+ bucket_name: str,
+ *,
+ account_id: str,
+ cf_r2_jurisdiction: Literal["default", "eu", "fedramp"] | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> CORSGetResponse:
+ """
+ Get the CORS policy for a bucket
+
+ Args:
+ account_id: Account ID
+
+ bucket_name: Name of the bucket
+
+ cf_r2_jurisdiction: The bucket jurisdiction
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not bucket_name:
+ raise ValueError(f"Expected a non-empty value for `bucket_name` but received {bucket_name!r}")
+ extra_headers = {
+ **strip_not_given(
+ {"cf-r2-jurisdiction": str(cf_r2_jurisdiction) if is_given(cf_r2_jurisdiction) else NOT_GIVEN}
+ ),
+ **(extra_headers or {}),
+ }
+ return await self._get(
+ f"/accounts/{account_id}/r2/buckets/{bucket_name}/cors",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[CORSGetResponse]._unwrapper,
+ ),
+ cast_to=cast(Type[CORSGetResponse], ResultWrapper[CORSGetResponse]),
+ )
+
+
+class CORSResourceWithRawResponse:
+ def __init__(self, cors: CORSResource) -> None:
+ self._cors = cors
+
+ self.update = to_raw_response_wrapper(
+ cors.update,
+ )
+ self.delete = to_raw_response_wrapper(
+ cors.delete,
+ )
+ self.get = to_raw_response_wrapper(
+ cors.get,
+ )
+
+
+class AsyncCORSResourceWithRawResponse:
+ def __init__(self, cors: AsyncCORSResource) -> None:
+ self._cors = cors
+
+ self.update = async_to_raw_response_wrapper(
+ cors.update,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ cors.delete,
+ )
+ self.get = async_to_raw_response_wrapper(
+ cors.get,
+ )
+
+
+class CORSResourceWithStreamingResponse:
+ def __init__(self, cors: CORSResource) -> None:
+ self._cors = cors
+
+ self.update = to_streamed_response_wrapper(
+ cors.update,
+ )
+ self.delete = to_streamed_response_wrapper(
+ cors.delete,
+ )
+ self.get = to_streamed_response_wrapper(
+ cors.get,
+ )
+
+
+class AsyncCORSResourceWithStreamingResponse:
+ def __init__(self, cors: AsyncCORSResource) -> None:
+ self._cors = cors
+
+ self.update = async_to_streamed_response_wrapper(
+ cors.update,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ cors.delete,
+ )
+ self.get = async_to_streamed_response_wrapper(
+ cors.get,
+ )
diff --git a/src/cloudflare/types/r2/buckets/__init__.py b/src/cloudflare/types/r2/buckets/__init__.py
index fc2a16aed13..099dd207b74 100644
--- a/src/cloudflare/types/r2/buckets/__init__.py
+++ b/src/cloudflare/types/r2/buckets/__init__.py
@@ -4,6 +4,8 @@
from .sippy import Sippy as Sippy
from .provider import Provider as Provider
+from .cors_get_response import CORSGetResponse as CORSGetResponse
+from .cors_update_params import CORSUpdateParams as CORSUpdateParams
from .sippy_update_params import SippyUpdateParams as SippyUpdateParams
from .sippy_delete_response import SippyDeleteResponse as SippyDeleteResponse
from .lifecycle_get_response import LifecycleGetResponse as LifecycleGetResponse
diff --git a/src/cloudflare/types/r2/buckets/cors_get_response.py b/src/cloudflare/types/r2/buckets/cors_get_response.py
new file mode 100644
index 00000000000..f1a7697976a
--- /dev/null
+++ b/src/cloudflare/types/r2/buckets/cors_get_response.py
@@ -0,0 +1,59 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ...._models import BaseModel
+
+__all__ = ["CORSGetResponse", "Rule", "RuleAllowed"]
+
+
+class RuleAllowed(BaseModel):
+ methods: List[Literal["GET", "PUT", "POST", "DELETE", "HEAD"]]
+ """
+ Specifies the value for the Access-Control-Allow-Methods header R2 sets when
+ requesting objects in a bucket from a browser.
+ """
+
+ origins: List[str]
+ """
+ Specifies the value for the Access-Control-Allow-Origin header R2 sets when
+ requesting objects in a bucket from a browser.
+ """
+
+ headers: Optional[List[str]] = None
+ """
+ Specifies the value for the Access-Control-Allow-Headers header R2 sets when
+ requesting objects in this bucket from a browser. Cross-origin requests that
+ include custom headers (e.g. x-user-id) should specify these headers as
+ AllowedHeaders.
+ """
+
+
+class Rule(BaseModel):
+ allowed: RuleAllowed
+ """Object specifying allowed origins, methods and headers for this CORS rule."""
+
+ id: Optional[str] = None
+ """Identifier for this rule"""
+
+ expose_headers: Optional[List[str]] = FieldInfo(alias="exposeHeaders", default=None)
+ """
+ Specifies the headers that can be exposed back, and accessed by, the JavaScript
+ making the cross-origin request. If you need to access headers beyond the
+ safelisted response headers, such as Content-Encoding or cf-cache-status, you
+ must specify it here.
+ """
+
+ max_age_seconds: Optional[float] = FieldInfo(alias="maxAgeSeconds", default=None)
+ """
+ Specifies the amount of time (in seconds) browsers are allowed to cache CORS
+ preflight responses. Browsers may limit this to 2 hours or less, even if the
+ maximum value (86400) is specified.
+ """
+
+
+class CORSGetResponse(BaseModel):
+ rules: Optional[List[Rule]] = None
diff --git a/src/cloudflare/types/r2/buckets/cors_update_params.py b/src/cloudflare/types/r2/buckets/cors_update_params.py
new file mode 100644
index 00000000000..3b88a5fea57
--- /dev/null
+++ b/src/cloudflare/types/r2/buckets/cors_update_params.py
@@ -0,0 +1,65 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List, Iterable
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from ...._utils import PropertyInfo
+
+__all__ = ["CORSUpdateParams", "Rule", "RuleAllowed"]
+
+
+class CORSUpdateParams(TypedDict, total=False):
+ account_id: Required[str]
+ """Account ID"""
+
+ rules: Iterable[Rule]
+
+ cf_r2_jurisdiction: Annotated[Literal["default", "eu", "fedramp"], PropertyInfo(alias="cf-r2-jurisdiction")]
+ """The bucket jurisdiction"""
+
+
+class RuleAllowed(TypedDict, total=False):
+ methods: Required[List[Literal["GET", "PUT", "POST", "DELETE", "HEAD"]]]
+ """
+ Specifies the value for the Access-Control-Allow-Methods header R2 sets when
+ requesting objects in a bucket from a browser.
+ """
+
+ origins: Required[List[str]]
+ """
+ Specifies the value for the Access-Control-Allow-Origin header R2 sets when
+ requesting objects in a bucket from a browser.
+ """
+
+ headers: List[str]
+ """
+ Specifies the value for the Access-Control-Allow-Headers header R2 sets when
+ requesting objects in this bucket from a browser. Cross-origin requests that
+ include custom headers (e.g. x-user-id) should specify these headers as
+ AllowedHeaders.
+ """
+
+
+class Rule(TypedDict, total=False):
+ allowed: Required[RuleAllowed]
+ """Object specifying allowed origins, methods and headers for this CORS rule."""
+
+ id: str
+ """Identifier for this rule"""
+
+ expose_headers: Annotated[List[str], PropertyInfo(alias="exposeHeaders")]
+ """
+ Specifies the headers that can be exposed back, and accessed by, the JavaScript
+ making the cross-origin request. If you need to access headers beyond the
+ safelisted response headers, such as Content-Encoding or cf-cache-status, you
+ must specify it here.
+ """
+
+ max_age_seconds: Annotated[float, PropertyInfo(alias="maxAgeSeconds")]
+ """
+ Specifies the amount of time (in seconds) browsers are allowed to cache CORS
+ preflight responses. Browsers may limit this to 2 hours or less, even if the
+ maximum value (86400) is specified.
+ """
diff --git a/tests/api_resources/r2/buckets/test_cors.py b/tests/api_resources/r2/buckets/test_cors.py
new file mode 100644
index 00000000000..4fc14846492
--- /dev/null
+++ b/tests/api_resources/r2/buckets/test_cors.py
@@ -0,0 +1,388 @@
+# 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 cloudflare import Cloudflare, AsyncCloudflare
+from tests.utils import assert_matches_type
+from cloudflare.types.r2.buckets import CORSGetResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestCORS:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_update(self, client: Cloudflare) -> None:
+ cors = client.r2.buckets.cors.update(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Cloudflare) -> None:
+ cors = client.r2.buckets.cors.update(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ rules=[
+ {
+ "allowed": {
+ "methods": ["GET"],
+ "origins": ["http://localhost:3000"],
+ "headers": ["x-requested-by"],
+ },
+ "id": "Allow Local Development",
+ "expose_headers": ["Content-Encoding"],
+ "max_age_seconds": 3600,
+ }
+ ],
+ cf_r2_jurisdiction="default",
+ )
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Cloudflare) -> None:
+ response = client.r2.buckets.cors.with_raw_response.update(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ cors = response.parse()
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Cloudflare) -> None:
+ with client.r2.buckets.cors.with_streaming_response.update(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ cors = response.parse()
+ assert_matches_type(object, cors, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.r2.buckets.cors.with_raw_response.update(
+ bucket_name="example-bucket",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bucket_name` but received ''"):
+ client.r2.buckets.cors.with_raw_response.update(
+ bucket_name="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ @parametrize
+ def test_method_delete(self, client: Cloudflare) -> None:
+ cors = client.r2.buckets.cors.delete(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ def test_method_delete_with_all_params(self, client: Cloudflare) -> None:
+ cors = client.r2.buckets.cors.delete(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ cf_r2_jurisdiction="default",
+ )
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Cloudflare) -> None:
+ response = client.r2.buckets.cors.with_raw_response.delete(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ cors = response.parse()
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Cloudflare) -> None:
+ with client.r2.buckets.cors.with_streaming_response.delete(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ cors = response.parse()
+ assert_matches_type(object, cors, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.r2.buckets.cors.with_raw_response.delete(
+ bucket_name="example-bucket",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bucket_name` but received ''"):
+ client.r2.buckets.cors.with_raw_response.delete(
+ bucket_name="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ @parametrize
+ def test_method_get(self, client: Cloudflare) -> None:
+ cors = client.r2.buckets.cors.get(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(CORSGetResponse, cors, path=["response"])
+
+ @parametrize
+ def test_method_get_with_all_params(self, client: Cloudflare) -> None:
+ cors = client.r2.buckets.cors.get(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ cf_r2_jurisdiction="default",
+ )
+ assert_matches_type(CORSGetResponse, cors, path=["response"])
+
+ @parametrize
+ def test_raw_response_get(self, client: Cloudflare) -> None:
+ response = client.r2.buckets.cors.with_raw_response.get(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ cors = response.parse()
+ assert_matches_type(CORSGetResponse, cors, path=["response"])
+
+ @parametrize
+ def test_streaming_response_get(self, client: Cloudflare) -> None:
+ with client.r2.buckets.cors.with_streaming_response.get(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ cors = response.parse()
+ assert_matches_type(CORSGetResponse, cors, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_get(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.r2.buckets.cors.with_raw_response.get(
+ bucket_name="example-bucket",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bucket_name` but received ''"):
+ client.r2.buckets.cors.with_raw_response.get(
+ bucket_name="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+
+class TestAsyncCORS:
+ parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncCloudflare) -> None:
+ cors = await async_client.r2.buckets.cors.update(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ cors = await async_client.r2.buckets.cors.update(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ rules=[
+ {
+ "allowed": {
+ "methods": ["GET"],
+ "origins": ["http://localhost:3000"],
+ "headers": ["x-requested-by"],
+ },
+ "id": "Allow Local Development",
+ "expose_headers": ["Content-Encoding"],
+ "max_age_seconds": 3600,
+ }
+ ],
+ cf_r2_jurisdiction="default",
+ )
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.r2.buckets.cors.with_raw_response.update(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ cors = await response.parse()
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.r2.buckets.cors.with_streaming_response.update(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ cors = await response.parse()
+ assert_matches_type(object, cors, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.r2.buckets.cors.with_raw_response.update(
+ bucket_name="example-bucket",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bucket_name` but received ''"):
+ await async_client.r2.buckets.cors.with_raw_response.update(
+ bucket_name="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncCloudflare) -> None:
+ cors = await async_client.r2.buckets.cors.delete(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ async def test_method_delete_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ cors = await async_client.r2.buckets.cors.delete(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ cf_r2_jurisdiction="default",
+ )
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.r2.buckets.cors.with_raw_response.delete(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ cors = await response.parse()
+ assert_matches_type(object, cors, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.r2.buckets.cors.with_streaming_response.delete(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ cors = await response.parse()
+ assert_matches_type(object, cors, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.r2.buckets.cors.with_raw_response.delete(
+ bucket_name="example-bucket",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bucket_name` but received ''"):
+ await async_client.r2.buckets.cors.with_raw_response.delete(
+ bucket_name="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ @parametrize
+ async def test_method_get(self, async_client: AsyncCloudflare) -> None:
+ cors = await async_client.r2.buckets.cors.get(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(CORSGetResponse, cors, path=["response"])
+
+ @parametrize
+ async def test_method_get_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ cors = await async_client.r2.buckets.cors.get(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ cf_r2_jurisdiction="default",
+ )
+ assert_matches_type(CORSGetResponse, cors, path=["response"])
+
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.r2.buckets.cors.with_raw_response.get(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ cors = await response.parse()
+ assert_matches_type(CORSGetResponse, cors, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.r2.buckets.cors.with_streaming_response.get(
+ bucket_name="example-bucket",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ cors = await response.parse()
+ assert_matches_type(CORSGetResponse, cors, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_get(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.r2.buckets.cors.with_raw_response.get(
+ bucket_name="example-bucket",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bucket_name` but received ''"):
+ await async_client.r2.buckets.cors.with_raw_response.get(
+ bucket_name="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )