From 8b3ce06fc776731c78c6efc9b1bf9deac1162d86 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 18:27:07 +0000 Subject: [PATCH 1/4] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index b78433dccc4..0a70b93fc67 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 1749 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-b15b44e0efd207de48e7e74e742b0b4b190c74f12a941a1a0ef59a51656a5224.yml openapi_spec_hash: 83243c9ee06f88d0fa91e9b185d8a42e -config_hash: d0ab46f06dbe6f6e33d86a3ede15ac44 +config_hash: f3028048c6dc3559115fdd749755dee2 From c8ae1c4a73cc8d5804dfcfb059b102eb2443ca86 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 18:58:31 +0000 Subject: [PATCH 2/4] chore(internal): update conftest.py --- tests/conftest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 90d867fb43f..9ec5ce03747 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,5 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from __future__ import annotations import os From 8917497520e4a1fcc97681685175ab4107e3041c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 20:02:21 +0000 Subject: [PATCH 3/4] fix(api): Update zone subscription paths --- .stats.yml | 4 +- api.md | 8 + src/cloudflare/resources/zones/__init__.py | 14 + .../resources/zones/subscriptions.py | 414 ++++++++++++++++++ src/cloudflare/resources/zones/zones.py | 32 ++ src/cloudflare/types/zones/__init__.py | 2 + .../types/zones/subscription_create_params.py | 20 + .../types/zones/subscription_update_params.py | 20 + .../api_resources/zones/test_subscriptions.py | 318 ++++++++++++++ 9 files changed, 830 insertions(+), 2 deletions(-) create mode 100644 src/cloudflare/resources/zones/subscriptions.py create mode 100644 src/cloudflare/types/zones/subscription_create_params.py create mode 100644 src/cloudflare/types/zones/subscription_update_params.py create mode 100644 tests/api_resources/zones/test_subscriptions.py diff --git a/.stats.yml b/.stats.yml index 0a70b93fc67..590d127c2cb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 1749 +configured_endpoints: 1752 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-b15b44e0efd207de48e7e74e742b0b4b190c74f12a941a1a0ef59a51656a5224.yml openapi_spec_hash: 83243c9ee06f88d0fa91e9b185d8a42e -config_hash: f3028048c6dc3559115fdd749755dee2 +config_hash: 8601d43fd5ccaf9e3d08f26748a5a63a diff --git a/api.md b/api.md index afec60c6f85..04738525ed2 100644 --- a/api.md +++ b/api.md @@ -427,6 +427,14 @@ Methods: - client.zones.holds.edit(\*, zone_id, \*\*params) -> ZoneHold - client.zones.holds.get(\*, zone_id) -> ZoneHold +## Subscriptions + +Methods: + +- client.zones.subscriptions.create(\*, zone_id, \*\*params) -> Subscription +- client.zones.subscriptions.update(\*, zone_id, \*\*params) -> Subscription +- client.zones.subscriptions.get(\*, zone_id) -> Subscription + ## Plans Types: diff --git a/src/cloudflare/resources/zones/__init__.py b/src/cloudflare/resources/zones/__init__.py index 54ff508e151..cbe662019bf 100644 --- a/src/cloudflare/resources/zones/__init__.py +++ b/src/cloudflare/resources/zones/__init__.py @@ -40,6 +40,14 @@ RatePlansResourceWithStreamingResponse, AsyncRatePlansResourceWithStreamingResponse, ) +from .subscriptions import ( + SubscriptionsResource, + AsyncSubscriptionsResource, + SubscriptionsResourceWithRawResponse, + AsyncSubscriptionsResourceWithRawResponse, + SubscriptionsResourceWithStreamingResponse, + AsyncSubscriptionsResourceWithStreamingResponse, +) from .activation_check import ( ActivationCheckResource, AsyncActivationCheckResource, @@ -82,6 +90,12 @@ "AsyncHoldsResourceWithRawResponse", "HoldsResourceWithStreamingResponse", "AsyncHoldsResourceWithStreamingResponse", + "SubscriptionsResource", + "AsyncSubscriptionsResource", + "SubscriptionsResourceWithRawResponse", + "AsyncSubscriptionsResourceWithRawResponse", + "SubscriptionsResourceWithStreamingResponse", + "AsyncSubscriptionsResourceWithStreamingResponse", "PlansResource", "AsyncPlansResource", "PlansResourceWithRawResponse", diff --git a/src/cloudflare/resources/zones/subscriptions.py b/src/cloudflare/resources/zones/subscriptions.py new file mode 100644 index 00000000000..d03a486b207 --- /dev/null +++ b/src/cloudflare/resources/zones/subscriptions.py @@ -0,0 +1,414 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Type, cast +from typing_extensions import Literal + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import 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 ..._wrappers import ResultWrapper +from ...types.zones import subscription_create_params, subscription_update_params +from ..._base_client import make_request_options +from ...types.shared.subscription import Subscription +from ...types.shared_params.rate_plan import RatePlan + +__all__ = ["SubscriptionsResource", "AsyncSubscriptionsResource"] + + +class SubscriptionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SubscriptionsResourceWithRawResponse: + """ + 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/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers + """ + return SubscriptionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SubscriptionsResourceWithStreamingResponse: + """ + 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 SubscriptionsResourceWithStreamingResponse(self) + + def create( + self, + *, + zone_id: str, + frequency: Literal["weekly", "monthly", "quarterly", "yearly"] | NotGiven = NOT_GIVEN, + rate_plan: RatePlan | 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, + ) -> Subscription: + """ + Create a zone subscription, either plan or add-ons. + + Args: + zone_id: Subscription identifier tag. + + frequency: How often the subscription is renewed automatically. + + rate_plan: The rate plan applied to the subscription. + + 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 zone_id: + raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}") + return self._post( + f"/zones/{zone_id}/subscription", + body=maybe_transform( + { + "frequency": frequency, + "rate_plan": rate_plan, + }, + subscription_create_params.SubscriptionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Subscription]._unwrapper, + ), + cast_to=cast(Type[Subscription], ResultWrapper[Subscription]), + ) + + def update( + self, + *, + zone_id: str, + frequency: Literal["weekly", "monthly", "quarterly", "yearly"] | NotGiven = NOT_GIVEN, + rate_plan: RatePlan | 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, + ) -> Subscription: + """ + Updates zone subscriptions, either plan or add-ons. + + Args: + zone_id: Subscription identifier tag. + + frequency: How often the subscription is renewed automatically. + + rate_plan: The rate plan applied to the subscription. + + 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 zone_id: + raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}") + return self._put( + f"/zones/{zone_id}/subscription", + body=maybe_transform( + { + "frequency": frequency, + "rate_plan": rate_plan, + }, + subscription_update_params.SubscriptionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Subscription]._unwrapper, + ), + cast_to=cast(Type[Subscription], ResultWrapper[Subscription]), + ) + + def get( + self, + *, + zone_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, + ) -> Subscription: + """ + Lists zone subscription details. + + Args: + zone_id: Subscription identifier tag. + + 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 zone_id: + raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}") + return self._get( + f"/zones/{zone_id}/subscription", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Subscription]._unwrapper, + ), + cast_to=cast(Type[Subscription], ResultWrapper[Subscription]), + ) + + +class AsyncSubscriptionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSubscriptionsResourceWithRawResponse: + """ + 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/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers + """ + return AsyncSubscriptionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSubscriptionsResourceWithStreamingResponse: + """ + 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 AsyncSubscriptionsResourceWithStreamingResponse(self) + + async def create( + self, + *, + zone_id: str, + frequency: Literal["weekly", "monthly", "quarterly", "yearly"] | NotGiven = NOT_GIVEN, + rate_plan: RatePlan | 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, + ) -> Subscription: + """ + Create a zone subscription, either plan or add-ons. + + Args: + zone_id: Subscription identifier tag. + + frequency: How often the subscription is renewed automatically. + + rate_plan: The rate plan applied to the subscription. + + 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 zone_id: + raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}") + return await self._post( + f"/zones/{zone_id}/subscription", + body=await async_maybe_transform( + { + "frequency": frequency, + "rate_plan": rate_plan, + }, + subscription_create_params.SubscriptionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Subscription]._unwrapper, + ), + cast_to=cast(Type[Subscription], ResultWrapper[Subscription]), + ) + + async def update( + self, + *, + zone_id: str, + frequency: Literal["weekly", "monthly", "quarterly", "yearly"] | NotGiven = NOT_GIVEN, + rate_plan: RatePlan | 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, + ) -> Subscription: + """ + Updates zone subscriptions, either plan or add-ons. + + Args: + zone_id: Subscription identifier tag. + + frequency: How often the subscription is renewed automatically. + + rate_plan: The rate plan applied to the subscription. + + 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 zone_id: + raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}") + return await self._put( + f"/zones/{zone_id}/subscription", + body=await async_maybe_transform( + { + "frequency": frequency, + "rate_plan": rate_plan, + }, + subscription_update_params.SubscriptionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Subscription]._unwrapper, + ), + cast_to=cast(Type[Subscription], ResultWrapper[Subscription]), + ) + + async def get( + self, + *, + zone_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, + ) -> Subscription: + """ + Lists zone subscription details. + + Args: + zone_id: Subscription identifier tag. + + 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 zone_id: + raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}") + return await self._get( + f"/zones/{zone_id}/subscription", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Subscription]._unwrapper, + ), + cast_to=cast(Type[Subscription], ResultWrapper[Subscription]), + ) + + +class SubscriptionsResourceWithRawResponse: + def __init__(self, subscriptions: SubscriptionsResource) -> None: + self._subscriptions = subscriptions + + self.create = to_raw_response_wrapper( + subscriptions.create, + ) + self.update = to_raw_response_wrapper( + subscriptions.update, + ) + self.get = to_raw_response_wrapper( + subscriptions.get, + ) + + +class AsyncSubscriptionsResourceWithRawResponse: + def __init__(self, subscriptions: AsyncSubscriptionsResource) -> None: + self._subscriptions = subscriptions + + self.create = async_to_raw_response_wrapper( + subscriptions.create, + ) + self.update = async_to_raw_response_wrapper( + subscriptions.update, + ) + self.get = async_to_raw_response_wrapper( + subscriptions.get, + ) + + +class SubscriptionsResourceWithStreamingResponse: + def __init__(self, subscriptions: SubscriptionsResource) -> None: + self._subscriptions = subscriptions + + self.create = to_streamed_response_wrapper( + subscriptions.create, + ) + self.update = to_streamed_response_wrapper( + subscriptions.update, + ) + self.get = to_streamed_response_wrapper( + subscriptions.get, + ) + + +class AsyncSubscriptionsResourceWithStreamingResponse: + def __init__(self, subscriptions: AsyncSubscriptionsResource) -> None: + self._subscriptions = subscriptions + + self.create = async_to_streamed_response_wrapper( + subscriptions.create, + ) + self.update = async_to_streamed_response_wrapper( + subscriptions.update, + ) + self.get = async_to_streamed_response_wrapper( + subscriptions.get, + ) diff --git a/src/cloudflare/resources/zones/zones.py b/src/cloudflare/resources/zones/zones.py index e49acb06fa5..d89a8773961 100644 --- a/src/cloudflare/resources/zones/zones.py +++ b/src/cloudflare/resources/zones/zones.py @@ -52,6 +52,14 @@ from ..._wrappers import ResultWrapper from ...pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray from ...types.zones import zone_edit_params, zone_list_params, zone_create_params +from .subscriptions import ( + SubscriptionsResource, + AsyncSubscriptionsResource, + SubscriptionsResourceWithRawResponse, + AsyncSubscriptionsResourceWithRawResponse, + SubscriptionsResourceWithStreamingResponse, + AsyncSubscriptionsResourceWithStreamingResponse, +) from ..._base_client import AsyncPaginator, make_request_options from .activation_check import ( ActivationCheckResource, @@ -93,6 +101,10 @@ def custom_nameservers(self) -> CustomNameserversResource: def holds(self) -> HoldsResource: return HoldsResource(self._client) + @cached_property + def subscriptions(self) -> SubscriptionsResource: + return SubscriptionsResource(self._client) + @cached_property def plans(self) -> PlansResource: return PlansResource(self._client) @@ -409,6 +421,10 @@ def custom_nameservers(self) -> AsyncCustomNameserversResource: def holds(self) -> AsyncHoldsResource: return AsyncHoldsResource(self._client) + @cached_property + def subscriptions(self) -> AsyncSubscriptionsResource: + return AsyncSubscriptionsResource(self._client) + @cached_property def plans(self) -> AsyncPlansResource: return AsyncPlansResource(self._client) @@ -744,6 +760,10 @@ def custom_nameservers(self) -> CustomNameserversResourceWithRawResponse: def holds(self) -> HoldsResourceWithRawResponse: return HoldsResourceWithRawResponse(self._zones.holds) + @cached_property + def subscriptions(self) -> SubscriptionsResourceWithRawResponse: + return SubscriptionsResourceWithRawResponse(self._zones.subscriptions) + @cached_property def plans(self) -> PlansResourceWithRawResponse: return PlansResourceWithRawResponse(self._zones.plans) @@ -789,6 +809,10 @@ def custom_nameservers(self) -> AsyncCustomNameserversResourceWithRawResponse: def holds(self) -> AsyncHoldsResourceWithRawResponse: return AsyncHoldsResourceWithRawResponse(self._zones.holds) + @cached_property + def subscriptions(self) -> AsyncSubscriptionsResourceWithRawResponse: + return AsyncSubscriptionsResourceWithRawResponse(self._zones.subscriptions) + @cached_property def plans(self) -> AsyncPlansResourceWithRawResponse: return AsyncPlansResourceWithRawResponse(self._zones.plans) @@ -834,6 +858,10 @@ def custom_nameservers(self) -> CustomNameserversResourceWithStreamingResponse: def holds(self) -> HoldsResourceWithStreamingResponse: return HoldsResourceWithStreamingResponse(self._zones.holds) + @cached_property + def subscriptions(self) -> SubscriptionsResourceWithStreamingResponse: + return SubscriptionsResourceWithStreamingResponse(self._zones.subscriptions) + @cached_property def plans(self) -> PlansResourceWithStreamingResponse: return PlansResourceWithStreamingResponse(self._zones.plans) @@ -879,6 +907,10 @@ def custom_nameservers(self) -> AsyncCustomNameserversResourceWithStreamingRespo def holds(self) -> AsyncHoldsResourceWithStreamingResponse: return AsyncHoldsResourceWithStreamingResponse(self._zones.holds) + @cached_property + def subscriptions(self) -> AsyncSubscriptionsResourceWithStreamingResponse: + return AsyncSubscriptionsResourceWithStreamingResponse(self._zones.subscriptions) + @cached_property def plans(self) -> AsyncPlansResourceWithStreamingResponse: return AsyncPlansResourceWithStreamingResponse(self._zones.plans) diff --git a/src/cloudflare/types/zones/__init__.py b/src/cloudflare/types/zones/__init__.py index 7e056c15dca..ae81d5bb32b 100644 --- a/src/cloudflare/types/zones/__init__.py +++ b/src/cloudflare/types/zones/__init__.py @@ -74,6 +74,8 @@ from .automatic_https_rewrites import AutomaticHTTPSRewrites as AutomaticHTTPSRewrites from .opportunistic_encryption import OpportunisticEncryption as OpportunisticEncryption from .response_buffering_param import ResponseBufferingParam as ResponseBufferingParam +from .subscription_create_params import SubscriptionCreateParams as SubscriptionCreateParams +from .subscription_update_params import SubscriptionUpdateParams as SubscriptionUpdateParams from .origin_error_page_pass_thru import OriginErrorPagePassThru as OriginErrorPagePassThru from .sort_query_string_for_cache import SortQueryStringForCache as SortQueryStringForCache from .true_client_ip_header_param import TrueClientIPHeaderParam as TrueClientIPHeaderParam diff --git a/src/cloudflare/types/zones/subscription_create_params.py b/src/cloudflare/types/zones/subscription_create_params.py new file mode 100644 index 00000000000..6c6a5212fa2 --- /dev/null +++ b/src/cloudflare/types/zones/subscription_create_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from ..shared_params.rate_plan import RatePlan + +__all__ = ["SubscriptionCreateParams"] + + +class SubscriptionCreateParams(TypedDict, total=False): + zone_id: Required[str] + """Subscription identifier tag.""" + + frequency: Literal["weekly", "monthly", "quarterly", "yearly"] + """How often the subscription is renewed automatically.""" + + rate_plan: RatePlan + """The rate plan applied to the subscription.""" diff --git a/src/cloudflare/types/zones/subscription_update_params.py b/src/cloudflare/types/zones/subscription_update_params.py new file mode 100644 index 00000000000..6c8e2157932 --- /dev/null +++ b/src/cloudflare/types/zones/subscription_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from ..shared_params.rate_plan import RatePlan + +__all__ = ["SubscriptionUpdateParams"] + + +class SubscriptionUpdateParams(TypedDict, total=False): + zone_id: Required[str] + """Subscription identifier tag.""" + + frequency: Literal["weekly", "monthly", "quarterly", "yearly"] + """How often the subscription is renewed automatically.""" + + rate_plan: RatePlan + """The rate plan applied to the subscription.""" diff --git a/tests/api_resources/zones/test_subscriptions.py b/tests/api_resources/zones/test_subscriptions.py new file mode 100644 index 00000000000..f99e737ef56 --- /dev/null +++ b/tests/api_resources/zones/test_subscriptions.py @@ -0,0 +1,318 @@ +# 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.shared import Subscription + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSubscriptions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Cloudflare) -> None: + subscription = client.zones.subscriptions.create( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Cloudflare) -> None: + subscription = client.zones.subscriptions.create( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + frequency="monthly", + rate_plan={ + "id": "free", + "currency": "USD", + "externally_managed": False, + "is_contract": False, + "public_name": "Business Plan", + "scope": "zone", + "sets": ["string"], + }, + ) + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Cloudflare) -> None: + response = client.zones.subscriptions.with_raw_response.create( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription = response.parse() + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Cloudflare) -> None: + with client.zones.subscriptions.with_streaming_response.create( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription = response.parse() + assert_matches_type(Subscription, subscription, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"): + client.zones.subscriptions.with_raw_response.create( + zone_id="", + ) + + @parametrize + def test_method_update(self, client: Cloudflare) -> None: + subscription = client.zones.subscriptions.update( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: Cloudflare) -> None: + subscription = client.zones.subscriptions.update( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + frequency="monthly", + rate_plan={ + "id": "free", + "currency": "USD", + "externally_managed": False, + "is_contract": False, + "public_name": "Business Plan", + "scope": "zone", + "sets": ["string"], + }, + ) + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: Cloudflare) -> None: + response = client.zones.subscriptions.with_raw_response.update( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription = response.parse() + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: Cloudflare) -> None: + with client.zones.subscriptions.with_streaming_response.update( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription = response.parse() + assert_matches_type(Subscription, subscription, 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 `zone_id` but received ''"): + client.zones.subscriptions.with_raw_response.update( + zone_id="", + ) + + @parametrize + def test_method_get(self, client: Cloudflare) -> None: + subscription = client.zones.subscriptions.get( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Cloudflare) -> None: + response = client.zones.subscriptions.with_raw_response.get( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription = response.parse() + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Cloudflare) -> None: + with client.zones.subscriptions.with_streaming_response.get( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription = response.parse() + assert_matches_type(Subscription, subscription, 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 `zone_id` but received ''"): + client.zones.subscriptions.with_raw_response.get( + zone_id="", + ) + + +class TestAsyncSubscriptions: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncCloudflare) -> None: + subscription = await async_client.zones.subscriptions.create( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncCloudflare) -> None: + subscription = await async_client.zones.subscriptions.create( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + frequency="monthly", + rate_plan={ + "id": "free", + "currency": "USD", + "externally_managed": False, + "is_contract": False, + "public_name": "Business Plan", + "scope": "zone", + "sets": ["string"], + }, + ) + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None: + response = await async_client.zones.subscriptions.with_raw_response.create( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription = await response.parse() + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncCloudflare) -> None: + async with async_client.zones.subscriptions.with_streaming_response.create( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription = await response.parse() + assert_matches_type(Subscription, subscription, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"): + await async_client.zones.subscriptions.with_raw_response.create( + zone_id="", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncCloudflare) -> None: + subscription = await async_client.zones.subscriptions.update( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncCloudflare) -> None: + subscription = await async_client.zones.subscriptions.update( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + frequency="monthly", + rate_plan={ + "id": "free", + "currency": "USD", + "externally_managed": False, + "is_contract": False, + "public_name": "Business Plan", + "scope": "zone", + "sets": ["string"], + }, + ) + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncCloudflare) -> None: + response = await async_client.zones.subscriptions.with_raw_response.update( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription = await response.parse() + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncCloudflare) -> None: + async with async_client.zones.subscriptions.with_streaming_response.update( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription = await response.parse() + assert_matches_type(Subscription, subscription, 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 `zone_id` but received ''"): + await async_client.zones.subscriptions.with_raw_response.update( + zone_id="", + ) + + @parametrize + async def test_method_get(self, async_client: AsyncCloudflare) -> None: + subscription = await async_client.zones.subscriptions.get( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None: + response = await async_client.zones.subscriptions.with_raw_response.get( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + subscription = await response.parse() + assert_matches_type(Subscription, subscription, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None: + async with async_client.zones.subscriptions.with_streaming_response.get( + zone_id="506e3185e9c882d175a2d0cb0093d9f2", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + subscription = await response.parse() + assert_matches_type(Subscription, subscription, 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 `zone_id` but received ''"): + await async_client.zones.subscriptions.with_raw_response.get( + zone_id="", + ) From 1a32133afbe7684bb0f8534514a56ff83bf1b45d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 20:02:49 +0000 Subject: [PATCH 4/4] release: 4.3.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/cloudflare/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 29102aeb503..025a1cbcb11 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "4.3.0" + ".": "4.3.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 88488216652..e63418f4e7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 4.3.1 (2025-06-16) + +Full Changelog: [v4.3.0...v4.3.1](https://github.com/cloudflare/cloudflare-python/compare/v4.3.0...v4.3.1) + +### Bug Fixes + +* **api:** Update zone subscription paths ([8917497](https://github.com/cloudflare/cloudflare-python/commit/8917497520e4a1fcc97681685175ab4107e3041c)) + + +### Chores + +* **internal:** update conftest.py ([c8ae1c4](https://github.com/cloudflare/cloudflare-python/commit/c8ae1c4a73cc8d5804dfcfb059b102eb2443ca86)) + ## 4.3.0 (2025-06-16) Full Changelog: [v4.2.0...v4.3.0](https://github.com/cloudflare/cloudflare-python/compare/v4.2.0...v4.3.0) diff --git a/pyproject.toml b/pyproject.toml index 857f3da92b9..b1f6854f5f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cloudflare" -version = "4.3.0" +version = "4.3.1" description = "The official Python library for the cloudflare API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/cloudflare/_version.py b/src/cloudflare/_version.py index 4f0b5539ef2..0d479c2e651 100644 --- a/src/cloudflare/_version.py +++ b/src/cloudflare/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "cloudflare" -__version__ = "4.3.0" # x-release-please-version +__version__ = "4.3.1" # x-release-please-version