From 27a6aa5231747ac6c6921ee78ce4ee651d31ed7a Mon Sep 17 00:00:00 2001 From: stainless-bot Date: Thu, 21 Nov 2024 03:56:39 +0000 Subject: [PATCH] feat(r2_bucket): add `cors` support --- .stats.yml | 2 +- api.md | 14 + .../resources/r2/buckets/__init__.py | 14 + .../resources/r2/buckets/buckets.py | 32 ++ src/cloudflare/resources/r2/buckets/cors.py | 454 ++++++++++++++++++ src/cloudflare/types/r2/buckets/__init__.py | 2 + .../types/r2/buckets/cors_get_response.py | 59 +++ .../types/r2/buckets/cors_update_params.py | 65 +++ tests/api_resources/r2/buckets/test_cors.py | 388 +++++++++++++++ 9 files changed, 1029 insertions(+), 1 deletion(-) create mode 100644 src/cloudflare/resources/r2/buckets/cors.py create mode 100644 src/cloudflare/types/r2/buckets/cors_get_response.py create mode 100644 src/cloudflare/types/r2/buckets/cors_update_params.py create mode 100644 tests/api_resources/r2/buckets/test_cors.py 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", + )