From 35e41526484ece3b85c3ad46b19e6b48b1a90fc4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 23:46:57 +0000 Subject: [PATCH 01/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index ca71746c0ec..0f220bf6e15 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2404 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-874436f83bd9c383144c69da47c4b767bb9c6f4f2bb4945af58cf3b6015f0f62.yml -openapi_spec_hash: beaf9a654991bf65d642e05c03460e4c +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-fb021f40a5abbad7169d39096c9b01f68874398756b6b7b50cc31a09643eec21.yml +openapi_spec_hash: 143c2791ffafd5757d615715c07ba7dc config_hash: 2f529580a17438fc62cd0b47db41b6f1 From 7255f4b0b3b57d862e596b2b7483449d7ef97484 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 07:29:11 +0000 Subject: [PATCH 02/29] chore(api): update composite API spec --- .stats.yml | 4 +- .../email_security/investigate/move.py | 70 ++++++++++++++++++- .../investigate/move_bulk_params.py | 4 ++ .../investigate/move_create_params.py | 4 ++ .../email_security/investigate/test_move.py | 22 ++++++ 5 files changed, 100 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0f220bf6e15..1b4bb2538e6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2404 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-fb021f40a5abbad7169d39096c9b01f68874398756b6b7b50cc31a09643eec21.yml -openapi_spec_hash: 143c2791ffafd5757d615715c07ba7dc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-ccdcfb940281d6680dc124b744d0da4f9a95582b6131ab8368ae6f01686b8036.yml +openapi_spec_hash: 05a000c7550306f31d4aa116f8c4b07a config_hash: 2f529580a17438fc62cd0b47db41b6f1 diff --git a/src/cloudflare/resources/email_security/investigate/move.py b/src/cloudflare/resources/email_security/investigate/move.py index 8fa530641c6..d23739406cb 100644 --- a/src/cloudflare/resources/email_security/investigate/move.py +++ b/src/cloudflare/resources/email_security/investigate/move.py @@ -53,6 +53,19 @@ def create( destination: Literal[ "Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges" ], + expected_disposition: Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + | Omit = omit, # 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, @@ -89,7 +102,13 @@ def create( investigate_id=investigate_id, ), page=SyncSinglePage[MoveCreateResponse], - body=maybe_transform({"destination": destination}, move_create_params.MoveCreateParams), + body=maybe_transform( + { + "destination": destination, + "expected_disposition": expected_disposition, + }, + move_create_params.MoveCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -104,6 +123,19 @@ def bulk( destination: Literal[ "Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges" ], + expected_disposition: Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + | Omit = omit, ids: SequenceNotStr[str] | Omit = omit, postfix_ids: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -142,6 +174,7 @@ def bulk( body=maybe_transform( { "destination": destination, + "expected_disposition": expected_disposition, "ids": ids, "postfix_ids": postfix_ids, }, @@ -183,6 +216,19 @@ def create( destination: Literal[ "Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges" ], + expected_disposition: Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + | Omit = omit, # 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, @@ -219,7 +265,13 @@ def create( investigate_id=investigate_id, ), page=AsyncSinglePage[MoveCreateResponse], - body=maybe_transform({"destination": destination}, move_create_params.MoveCreateParams), + body=maybe_transform( + { + "destination": destination, + "expected_disposition": expected_disposition, + }, + move_create_params.MoveCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -234,6 +286,19 @@ def bulk( destination: Literal[ "Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges" ], + expected_disposition: Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + | Omit = omit, ids: SequenceNotStr[str] | Omit = omit, postfix_ids: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -272,6 +337,7 @@ def bulk( body=maybe_transform( { "destination": destination, + "expected_disposition": expected_disposition, "ids": ids, "postfix_ids": postfix_ids, }, diff --git a/src/cloudflare/types/email_security/investigate/move_bulk_params.py b/src/cloudflare/types/email_security/investigate/move_bulk_params.py index 35a0e5ac6d6..1942bb4ded6 100644 --- a/src/cloudflare/types/email_security/investigate/move_bulk_params.py +++ b/src/cloudflare/types/email_security/investigate/move_bulk_params.py @@ -17,6 +17,10 @@ class MoveBulkParams(TypedDict, total=False): Literal["Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"] ] + expected_disposition: Literal[ + "MALICIOUS", "MALICIOUS-BEC", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "ENCRYPTED", "EXTERNAL", "UNKNOWN", "NONE" + ] + ids: SequenceNotStr[str] """List of message IDs to move""" diff --git a/src/cloudflare/types/email_security/investigate/move_create_params.py b/src/cloudflare/types/email_security/investigate/move_create_params.py index 0e389b2ae93..fc7c88c605d 100644 --- a/src/cloudflare/types/email_security/investigate/move_create_params.py +++ b/src/cloudflare/types/email_security/investigate/move_create_params.py @@ -14,3 +14,7 @@ class MoveCreateParams(TypedDict, total=False): destination: Required[ Literal["Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"] ] + + expected_disposition: Literal[ + "MALICIOUS", "MALICIOUS-BEC", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "ENCRYPTED", "EXTERNAL", "UNKNOWN", "NONE" + ] diff --git a/tests/api_resources/email_security/investigate/test_move.py b/tests/api_resources/email_security/investigate/test_move.py index cf738cea491..19eadebcef0 100644 --- a/tests/api_resources/email_security/investigate/test_move.py +++ b/tests/api_resources/email_security/investigate/test_move.py @@ -30,6 +30,16 @@ def test_method_create(self, client: Cloudflare) -> None: ) assert_matches_type(SyncSinglePage[MoveCreateResponse], move, path=["response"]) + @parametrize + def test_method_create_with_all_params(self, client: Cloudflare) -> None: + move = client.email_security.investigate.move.create( + investigate_id="4Njp3P0STMz2c02Q-2024-01-05T10:00:00-12345678", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + destination="Inbox", + expected_disposition="MALICIOUS", + ) + assert_matches_type(SyncSinglePage[MoveCreateResponse], move, path=["response"]) + @parametrize def test_raw_response_create(self, client: Cloudflare) -> None: response = client.email_security.investigate.move.with_raw_response.create( @@ -87,6 +97,7 @@ def test_method_bulk_with_all_params(self, client: Cloudflare) -> None: move = client.email_security.investigate.move.bulk( account_id="023e105f4ecef8ad9ca31a8372d0c353", destination="Inbox", + expected_disposition="MALICIOUS", ids=["4Njp3P0STMz2c02Q-2024-01-05T10:00:00-12345678"], postfix_ids=["4Njp3P0STMz2c02Q"], ) @@ -141,6 +152,16 @@ async def test_method_create(self, async_client: AsyncCloudflare) -> None: ) assert_matches_type(AsyncSinglePage[MoveCreateResponse], move, path=["response"]) + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncCloudflare) -> None: + move = await async_client.email_security.investigate.move.create( + investigate_id="4Njp3P0STMz2c02Q-2024-01-05T10:00:00-12345678", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + destination="Inbox", + expected_disposition="MALICIOUS", + ) + assert_matches_type(AsyncSinglePage[MoveCreateResponse], move, path=["response"]) + @parametrize async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None: response = await async_client.email_security.investigate.move.with_raw_response.create( @@ -198,6 +219,7 @@ async def test_method_bulk_with_all_params(self, async_client: AsyncCloudflare) move = await async_client.email_security.investigate.move.bulk( account_id="023e105f4ecef8ad9ca31a8372d0c353", destination="Inbox", + expected_disposition="MALICIOUS", ids=["4Njp3P0STMz2c02Q-2024-01-05T10:00:00-12345678"], postfix_ids=["4Njp3P0STMz2c02Q"], ) From 171964c881255cb0820a8767dc6eccba8ef41432 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 10:05:55 +0000 Subject: [PATCH 03/29] chore(api): update composite API spec --- .stats.yml | 4 +- .../types/rulesets/rule_create_params.py | 68 +++++++++++++++++++ .../types/rulesets/rule_edit_params.py | 68 +++++++++++++++++++ .../types/rulesets/set_cache_settings_rule.py | 68 +++++++++++++++++++ .../rulesets/set_cache_settings_rule_param.py | 68 +++++++++++++++++++ tests/api_resources/rulesets/test_rules.py | 56 +++++++++++++++ 6 files changed, 330 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1b4bb2538e6..57f138060bb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2404 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-ccdcfb940281d6680dc124b744d0da4f9a95582b6131ab8368ae6f01686b8036.yml -openapi_spec_hash: 05a000c7550306f31d4aa116f8c4b07a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-dffae42e60784ca8228f71e302fcc65e835e71f92ab075e9dbf5bd050cb1d17f.yml +openapi_spec_hash: 847c9ce39141b8ec5d94dffc1ddcd032 config_hash: 2f529580a17438fc62cd0b47db41b6f1 diff --git a/src/cloudflare/types/rulesets/rule_create_params.py b/src/cloudflare/types/rulesets/rule_create_params.py index 6a8c82757e1..9c5b7d9ea6b 100644 --- a/src/cloudflare/types/rulesets/rule_create_params.py +++ b/src/cloudflare/types/rulesets/rule_create_params.py @@ -220,6 +220,9 @@ "SetCacheSettingsRuleActionParametersEdgeTTLStatusCodeTTLStatusCodeRange", "SetCacheSettingsRuleActionParametersServeStale", "SetCacheSettingsRuleActionParametersSharedDictionary", + "SetCacheSettingsRuleActionParametersVary", + "SetCacheSettingsRuleActionParametersVaryDefault", + "SetCacheSettingsRuleActionParametersVaryHeaders", "SetCacheSettingsRuleExposedCredentialCheck", "SetCacheSettingsRulePosition", "SetCacheSettingsRulePositionBeforePosition", @@ -3425,6 +3428,64 @@ class SetCacheSettingsRuleActionParametersSharedDictionary(TypedDict, total=Fals """ +class SetCacheSettingsRuleActionParametersVaryDefault(TypedDict, total=False): + """ + Controls how a single request header (or the default for all headers) contributes to the cache key. + """ + + action: Required[Literal["bypass", "passthrough", "normalize"]] + """How the header value is treated when building the cache key.""" + + languages: SequenceNotStr[str] + """The set of languages to normalize against. + + Only valid for the `accept-language` header. + """ + + media_types: SequenceNotStr[str] + """The set of media types to normalize against. + + Only valid for the `accept` header. + """ + + +class SetCacheSettingsRuleActionParametersVaryHeaders(TypedDict, total=False): + """ + Controls how a single request header (or the default for all headers) contributes to the cache key. + """ + + action: Required[Literal["bypass", "passthrough", "normalize"]] + """How the header value is treated when building the cache key.""" + + languages: SequenceNotStr[str] + """The set of languages to normalize against. + + Only valid for the `accept-language` header. + """ + + media_types: SequenceNotStr[str] + """The set of media types to normalize against. + + Only valid for the `accept` header. + """ + + +class SetCacheSettingsRuleActionParametersVary(TypedDict, total=False): + """Controls how cached responses vary based on request headers. + + At least one of `default` or `headers` must be set, and `default` is required when `headers` is set. + """ + + default: SetCacheSettingsRuleActionParametersVaryDefault + """ + Controls how a single request header (or the default for all headers) + contributes to the cache key. + """ + + headers: Dict[str, SetCacheSettingsRuleActionParametersVaryHeaders] + """A mapping of lowercase request header names to their vary configuration.""" + + class SetCacheSettingsRuleActionParameters(TypedDict, total=False): """The parameters configuring the rule's action.""" @@ -3500,6 +3561,13 @@ class SetCacheSettingsRuleActionParameters(TypedDict, total=False): strip_set_cookie: bool """Whether to strip Set-Cookie headers from the origin response before caching.""" + vary: SetCacheSettingsRuleActionParametersVary + """Controls how cached responses vary based on request headers. + + At least one of `default` or `headers` must be set, and `default` is required + when `headers` is set. + """ + class SetCacheSettingsRuleExposedCredentialCheck(TypedDict, total=False): """Configuration for exposed credential checking.""" diff --git a/src/cloudflare/types/rulesets/rule_edit_params.py b/src/cloudflare/types/rulesets/rule_edit_params.py index c914421bbdf..272ed9c3a2f 100644 --- a/src/cloudflare/types/rulesets/rule_edit_params.py +++ b/src/cloudflare/types/rulesets/rule_edit_params.py @@ -220,6 +220,9 @@ "SetCacheSettingsRuleActionParametersEdgeTTLStatusCodeTTLStatusCodeRange", "SetCacheSettingsRuleActionParametersServeStale", "SetCacheSettingsRuleActionParametersSharedDictionary", + "SetCacheSettingsRuleActionParametersVary", + "SetCacheSettingsRuleActionParametersVaryDefault", + "SetCacheSettingsRuleActionParametersVaryHeaders", "SetCacheSettingsRuleExposedCredentialCheck", "SetCacheSettingsRulePosition", "SetCacheSettingsRulePositionBeforePosition", @@ -3476,6 +3479,64 @@ class SetCacheSettingsRuleActionParametersSharedDictionary(TypedDict, total=Fals """ +class SetCacheSettingsRuleActionParametersVaryDefault(TypedDict, total=False): + """ + Controls how a single request header (or the default for all headers) contributes to the cache key. + """ + + action: Required[Literal["bypass", "passthrough", "normalize"]] + """How the header value is treated when building the cache key.""" + + languages: SequenceNotStr[str] + """The set of languages to normalize against. + + Only valid for the `accept-language` header. + """ + + media_types: SequenceNotStr[str] + """The set of media types to normalize against. + + Only valid for the `accept` header. + """ + + +class SetCacheSettingsRuleActionParametersVaryHeaders(TypedDict, total=False): + """ + Controls how a single request header (or the default for all headers) contributes to the cache key. + """ + + action: Required[Literal["bypass", "passthrough", "normalize"]] + """How the header value is treated when building the cache key.""" + + languages: SequenceNotStr[str] + """The set of languages to normalize against. + + Only valid for the `accept-language` header. + """ + + media_types: SequenceNotStr[str] + """The set of media types to normalize against. + + Only valid for the `accept` header. + """ + + +class SetCacheSettingsRuleActionParametersVary(TypedDict, total=False): + """Controls how cached responses vary based on request headers. + + At least one of `default` or `headers` must be set, and `default` is required when `headers` is set. + """ + + default: SetCacheSettingsRuleActionParametersVaryDefault + """ + Controls how a single request header (or the default for all headers) + contributes to the cache key. + """ + + headers: Dict[str, SetCacheSettingsRuleActionParametersVaryHeaders] + """A mapping of lowercase request header names to their vary configuration.""" + + class SetCacheSettingsRuleActionParameters(TypedDict, total=False): """The parameters configuring the rule's action.""" @@ -3551,6 +3612,13 @@ class SetCacheSettingsRuleActionParameters(TypedDict, total=False): strip_set_cookie: bool """Whether to strip Set-Cookie headers from the origin response before caching.""" + vary: SetCacheSettingsRuleActionParametersVary + """Controls how cached responses vary based on request headers. + + At least one of `default` or `headers` must be set, and `default` is required + when `headers` is set. + """ + class SetCacheSettingsRuleExposedCredentialCheck(TypedDict, total=False): """Configuration for exposed credential checking.""" diff --git a/src/cloudflare/types/rulesets/set_cache_settings_rule.py b/src/cloudflare/types/rulesets/set_cache_settings_rule.py index 8b1f40e5af7..c5c8c06032f 100644 --- a/src/cloudflare/types/rulesets/set_cache_settings_rule.py +++ b/src/cloudflare/types/rulesets/set_cache_settings_rule.py @@ -28,6 +28,9 @@ "ActionParametersEdgeTTLStatusCodeTTLStatusCodeRange", "ActionParametersServeStale", "ActionParametersSharedDictionary", + "ActionParametersVary", + "ActionParametersVaryDefault", + "ActionParametersVaryHeaders", "ExposedCredentialCheck", "Ratelimit", ] @@ -253,6 +256,64 @@ class ActionParametersSharedDictionary(BaseModel): """ +class ActionParametersVaryDefault(BaseModel): + """ + Controls how a single request header (or the default for all headers) contributes to the cache key. + """ + + action: Literal["bypass", "passthrough", "normalize"] + """How the header value is treated when building the cache key.""" + + languages: Optional[List[str]] = None + """The set of languages to normalize against. + + Only valid for the `accept-language` header. + """ + + media_types: Optional[List[str]] = None + """The set of media types to normalize against. + + Only valid for the `accept` header. + """ + + +class ActionParametersVaryHeaders(BaseModel): + """ + Controls how a single request header (or the default for all headers) contributes to the cache key. + """ + + action: Literal["bypass", "passthrough", "normalize"] + """How the header value is treated when building the cache key.""" + + languages: Optional[List[str]] = None + """The set of languages to normalize against. + + Only valid for the `accept-language` header. + """ + + media_types: Optional[List[str]] = None + """The set of media types to normalize against. + + Only valid for the `accept` header. + """ + + +class ActionParametersVary(BaseModel): + """Controls how cached responses vary based on request headers. + + At least one of `default` or `headers` must be set, and `default` is required when `headers` is set. + """ + + default: Optional[ActionParametersVaryDefault] = None + """ + Controls how a single request header (or the default for all headers) + contributes to the cache key. + """ + + headers: Optional[Dict[str, ActionParametersVaryHeaders]] = None + """A mapping of lowercase request header names to their vary configuration.""" + + class ActionParameters(BaseModel): """The parameters configuring the rule's action.""" @@ -328,6 +389,13 @@ class ActionParameters(BaseModel): strip_set_cookie: Optional[bool] = None """Whether to strip Set-Cookie headers from the origin response before caching.""" + vary: Optional[ActionParametersVary] = None + """Controls how cached responses vary based on request headers. + + At least one of `default` or `headers` must be set, and `default` is required + when `headers` is set. + """ + class ExposedCredentialCheck(BaseModel): """Configuration for exposed credential checking.""" diff --git a/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py b/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py index b223d45297d..3ed01b1a11e 100644 --- a/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py +++ b/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py @@ -27,6 +27,9 @@ "ActionParametersEdgeTTLStatusCodeTTLStatusCodeRange", "ActionParametersServeStale", "ActionParametersSharedDictionary", + "ActionParametersVary", + "ActionParametersVaryDefault", + "ActionParametersVaryHeaders", "ExposedCredentialCheck", "Ratelimit", ] @@ -260,6 +263,64 @@ class ActionParametersSharedDictionary(TypedDict, total=False): """ +class ActionParametersVaryDefault(TypedDict, total=False): + """ + Controls how a single request header (or the default for all headers) contributes to the cache key. + """ + + action: Required[Literal["bypass", "passthrough", "normalize"]] + """How the header value is treated when building the cache key.""" + + languages: SequenceNotStr[str] + """The set of languages to normalize against. + + Only valid for the `accept-language` header. + """ + + media_types: SequenceNotStr[str] + """The set of media types to normalize against. + + Only valid for the `accept` header. + """ + + +class ActionParametersVaryHeaders(TypedDict, total=False): + """ + Controls how a single request header (or the default for all headers) contributes to the cache key. + """ + + action: Required[Literal["bypass", "passthrough", "normalize"]] + """How the header value is treated when building the cache key.""" + + languages: SequenceNotStr[str] + """The set of languages to normalize against. + + Only valid for the `accept-language` header. + """ + + media_types: SequenceNotStr[str] + """The set of media types to normalize against. + + Only valid for the `accept` header. + """ + + +class ActionParametersVary(TypedDict, total=False): + """Controls how cached responses vary based on request headers. + + At least one of `default` or `headers` must be set, and `default` is required when `headers` is set. + """ + + default: ActionParametersVaryDefault + """ + Controls how a single request header (or the default for all headers) + contributes to the cache key. + """ + + headers: Dict[str, ActionParametersVaryHeaders] + """A mapping of lowercase request header names to their vary configuration.""" + + class ActionParameters(TypedDict, total=False): """The parameters configuring the rule's action.""" @@ -335,6 +396,13 @@ class ActionParameters(TypedDict, total=False): strip_set_cookie: bool """Whether to strip Set-Cookie headers from the origin response before caching.""" + vary: ActionParametersVary + """Controls how cached responses vary based on request headers. + + At least one of `default` or `headers` must be set, and `default` is required + when `headers` is set. + """ + class ExposedCredentialCheck(TypedDict, total=False): """Configuration for exposed credential checking.""" diff --git a/tests/api_resources/rulesets/test_rules.py b/tests/api_resources/rulesets/test_rules.py index 0270791a0b1..2b88ce437de 100644 --- a/tests/api_resources/rulesets/test_rules.py +++ b/tests/api_resources/rulesets/test_rules.py @@ -1688,6 +1688,20 @@ def test_method_create_with_all_params_overload_17(self, client: Cloudflare) -> "strip_etags": True, "strip_last_modified": True, "strip_set_cookie": True, + "vary": { + "default": { + "action": "normalize", + "languages": ["en"], + "media_types": ["image/webp"], + }, + "headers": { + "accept": { + "action": "normalize", + "languages": ["en"], + "media_types": ["image/webp", "image/png"], + } + }, + }, }, description="Configure settings for how the response is cached.", enabled=True, @@ -4042,6 +4056,20 @@ def test_method_edit_with_all_params_overload_17(self, client: Cloudflare) -> No "strip_etags": True, "strip_last_modified": True, "strip_set_cookie": True, + "vary": { + "default": { + "action": "normalize", + "languages": ["en"], + "media_types": ["image/webp"], + }, + "headers": { + "accept": { + "action": "normalize", + "languages": ["en"], + "media_types": ["image/webp", "image/png"], + } + }, + }, }, description="Configure settings for how the response is cached.", enabled=True, @@ -6149,6 +6177,20 @@ async def test_method_create_with_all_params_overload_17(self, async_client: Asy "strip_etags": True, "strip_last_modified": True, "strip_set_cookie": True, + "vary": { + "default": { + "action": "normalize", + "languages": ["en"], + "media_types": ["image/webp"], + }, + "headers": { + "accept": { + "action": "normalize", + "languages": ["en"], + "media_types": ["image/webp", "image/png"], + } + }, + }, }, description="Configure settings for how the response is cached.", enabled=True, @@ -8503,6 +8545,20 @@ async def test_method_edit_with_all_params_overload_17(self, async_client: Async "strip_etags": True, "strip_last_modified": True, "strip_set_cookie": True, + "vary": { + "default": { + "action": "normalize", + "languages": ["en"], + "media_types": ["image/webp"], + }, + "headers": { + "accept": { + "action": "normalize", + "languages": ["en"], + "media_types": ["image/webp", "image/png"], + } + }, + }, }, description="Configure settings for how the response is cached.", enabled=True, From 6fac09cd2f283f93ec9e7f7d4632b8cb2946607a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:22:59 +0000 Subject: [PATCH 04/29] chore(api): update composite API spec --- .stats.yml | 6 +- src/cloudflare/resources/email_routing/api.md | 1 - .../resources/email_routing/rules/rules.py | 129 +----------------- .../types/email_routing/__init__.py | 1 - .../types/email_routing/rule_list_params.py | 21 --- .../types/email_routing/settings.py | 6 + .../api_resources/email_routing/test_rules.py | 97 ------------- 7 files changed, 11 insertions(+), 250 deletions(-) delete mode 100644 src/cloudflare/types/email_routing/rule_list_params.py diff --git a/.stats.yml b/.stats.yml index 57f138060bb..48d073e0b5f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 2404 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-dffae42e60784ca8228f71e302fcc65e835e71f92ab075e9dbf5bd050cb1d17f.yml -openapi_spec_hash: 847c9ce39141b8ec5d94dffc1ddcd032 +configured_endpoints: 2403 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-3d3e9856df2bb12cc1de02ce9b62d6efe533e3b8f3c17f1a7177a1915f3d2bf6.yml +openapi_spec_hash: ba721c8e9fc329cb059935e94f7de823 config_hash: 2f529580a17438fc62cd0b47db41b6f1 diff --git a/src/cloudflare/resources/email_routing/api.md b/src/cloudflare/resources/email_routing/api.md index 26a1d76019a..60551e95244 100644 --- a/src/cloudflare/resources/email_routing/api.md +++ b/src/cloudflare/resources/email_routing/api.md @@ -39,7 +39,6 @@ Methods: - client.email_routing.rules.create(\*, zone_id, \*\*params) -> Optional[EmailRoutingRule] - client.email_routing.rules.update(rule_identifier, \*, zone_id, \*\*params) -> Optional[EmailRoutingRule] -- client.email_routing.rules.list(\*, zone_id, \*\*params) -> SyncV4PagePaginationArray[EmailRoutingRule] - client.email_routing.rules.delete(rule_identifier, \*, zone_id) -> Optional[EmailRoutingRule] - client.email_routing.rules.get(rule_identifier, \*, zone_id) -> Optional[EmailRoutingRule] diff --git a/src/cloudflare/resources/email_routing/rules/rules.py b/src/cloudflare/resources/email_routing/rules/rules.py index b38ce74c7a0..cff2f977c39 100644 --- a/src/cloudflare/resources/email_routing/rules/rules.py +++ b/src/cloudflare/resources/email_routing/rules/rules.py @@ -26,9 +26,8 @@ async_to_streamed_response_wrapper, ) from ...._wrappers import ResultWrapper -from ....pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray -from ...._base_client import AsyncPaginator, make_request_options -from ....types.email_routing import rule_list_params, rule_create_params, rule_update_params +from ...._base_client import make_request_options +from ....types.email_routing import rule_create_params, rule_update_params from ....types.email_routing.action_param import ActionParam from ....types.email_routing.matcher_param import MatcherParam from ....types.email_routing.email_routing_rule import EmailRoutingRule @@ -202,62 +201,6 @@ def update( cast_to=cast(Type[Optional[EmailRoutingRule]], ResultWrapper[EmailRoutingRule]), ) - def list( - self, - *, - zone_id: str, - enabled: Literal[True, False] | Omit = omit, - page: float | Omit = omit, - per_page: float | Omit = omit, - # 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, - ) -> SyncV4PagePaginationArray[EmailRoutingRule]: - """ - Lists existing routing rules. - - Args: - zone_id: Identifier. - - enabled: Filter by enabled routing rules. - - page: Page number of paginated results. - - per_page: Maximum number of results per page. - - 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_api_list( - path_template("/zones/{zone_id}/email/routing/rules", zone_id=zone_id), - page=SyncV4PagePaginationArray[EmailRoutingRule], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "enabled": enabled, - "page": page, - "per_page": per_page, - }, - rule_list_params.RuleListParams, - ), - ), - model=EmailRoutingRule, - ) - def delete( self, rule_identifier: str, @@ -521,62 +464,6 @@ async def update( cast_to=cast(Type[Optional[EmailRoutingRule]], ResultWrapper[EmailRoutingRule]), ) - def list( - self, - *, - zone_id: str, - enabled: Literal[True, False] | Omit = omit, - page: float | Omit = omit, - per_page: float | Omit = omit, - # 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, - ) -> AsyncPaginator[EmailRoutingRule, AsyncV4PagePaginationArray[EmailRoutingRule]]: - """ - Lists existing routing rules. - - Args: - zone_id: Identifier. - - enabled: Filter by enabled routing rules. - - page: Page number of paginated results. - - per_page: Maximum number of results per page. - - 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_api_list( - path_template("/zones/{zone_id}/email/routing/rules", zone_id=zone_id), - page=AsyncV4PagePaginationArray[EmailRoutingRule], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "enabled": enabled, - "page": page, - "per_page": per_page, - }, - rule_list_params.RuleListParams, - ), - ), - model=EmailRoutingRule, - ) - async def delete( self, rule_identifier: str, @@ -684,9 +571,6 @@ def __init__(self, rules: RulesResource) -> None: self.update = to_raw_response_wrapper( rules.update, ) - self.list = to_raw_response_wrapper( - rules.list, - ) self.delete = to_raw_response_wrapper( rules.delete, ) @@ -709,9 +593,6 @@ def __init__(self, rules: AsyncRulesResource) -> None: self.update = async_to_raw_response_wrapper( rules.update, ) - self.list = async_to_raw_response_wrapper( - rules.list, - ) self.delete = async_to_raw_response_wrapper( rules.delete, ) @@ -734,9 +615,6 @@ def __init__(self, rules: RulesResource) -> None: self.update = to_streamed_response_wrapper( rules.update, ) - self.list = to_streamed_response_wrapper( - rules.list, - ) self.delete = to_streamed_response_wrapper( rules.delete, ) @@ -759,9 +637,6 @@ def __init__(self, rules: AsyncRulesResource) -> None: self.update = async_to_streamed_response_wrapper( rules.update, ) - self.list = async_to_streamed_response_wrapper( - rules.list, - ) self.delete = async_to_streamed_response_wrapper( rules.delete, ) diff --git a/src/cloudflare/types/email_routing/__init__.py b/src/cloudflare/types/email_routing/__init__.py index 553bab1e508..9eaa27e5189 100644 --- a/src/cloudflare/types/email_routing/__init__.py +++ b/src/cloudflare/types/email_routing/__init__.py @@ -12,7 +12,6 @@ from .dns_get_params import DNSGetParams as DNSGetParams from .dns_edit_params import DNSEditParams as DNSEditParams from .dns_get_response import DNSGetResponse as DNSGetResponse -from .rule_list_params import RuleListParams as RuleListParams from .dns_create_params import DNSCreateParams as DNSCreateParams from .email_routing_rule import EmailRoutingRule as EmailRoutingRule from .rule_create_params import RuleCreateParams as RuleCreateParams diff --git a/src/cloudflare/types/email_routing/rule_list_params.py b/src/cloudflare/types/email_routing/rule_list_params.py deleted file mode 100644 index bb27b3d4885..00000000000 --- a/src/cloudflare/types/email_routing/rule_list_params.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["RuleListParams"] - - -class RuleListParams(TypedDict, total=False): - zone_id: Required[str] - """Identifier.""" - - enabled: Literal[True, False] - """Filter by enabled routing rules.""" - - page: float - """Page number of paginated results.""" - - per_page: float - """Maximum number of results per page.""" diff --git a/src/cloudflare/types/email_routing/settings.py b/src/cloudflare/types/email_routing/settings.py index 9688cfbe2d1..900c3cfce4d 100644 --- a/src/cloudflare/types/email_routing/settings.py +++ b/src/cloudflare/types/email_routing/settings.py @@ -31,6 +31,12 @@ class Settings(BaseModel): status: Optional[Literal["ready", "unconfigured", "misconfigured", "misconfigured/locked", "unlocked"]] = None """Show the state of your account, and the type or configuration error.""" + support_subaddress: Optional[Literal[True, False]] = None + """ + Whether subaddressing (plus-addressing) is honored when matching incoming mail + against routing rules. + """ + tag: Optional[str] = None """Email Routing settings tag. diff --git a/tests/api_resources/email_routing/test_rules.py b/tests/api_resources/email_routing/test_rules.py index 33b96b74828..03090d5bfc2 100644 --- a/tests/api_resources/email_routing/test_rules.py +++ b/tests/api_resources/email_routing/test_rules.py @@ -9,7 +9,6 @@ from cloudflare import Cloudflare, AsyncCloudflare from tests.utils import assert_matches_type -from cloudflare.pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray from cloudflare.types.email_routing import EmailRoutingRule base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -169,54 +168,6 @@ def test_path_params_update(self, client: Cloudflare) -> None: matchers=[{"type": "literal"}], ) - @parametrize - def test_method_list(self, client: Cloudflare) -> None: - rule = client.email_routing.rules.list( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) - assert_matches_type(SyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"]) - - @parametrize - def test_method_list_with_all_params(self, client: Cloudflare) -> None: - rule = client.email_routing.rules.list( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - enabled=True, - page=1, - per_page=5, - ) - assert_matches_type(SyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"]) - - @parametrize - def test_raw_response_list(self, client: Cloudflare) -> None: - response = client.email_routing.rules.with_raw_response.list( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - rule = response.parse() - assert_matches_type(SyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"]) - - @parametrize - def test_streaming_response_list(self, client: Cloudflare) -> None: - with client.email_routing.rules.with_streaming_response.list( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - rule = response.parse() - assert_matches_type(SyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_list(self, client: Cloudflare) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"): - client.email_routing.rules.with_raw_response.list( - zone_id="", - ) - @parametrize def test_method_delete(self, client: Cloudflare) -> None: rule = client.email_routing.rules.delete( @@ -470,54 +421,6 @@ async def test_path_params_update(self, async_client: AsyncCloudflare) -> None: matchers=[{"type": "literal"}], ) - @parametrize - async def test_method_list(self, async_client: AsyncCloudflare) -> None: - rule = await async_client.email_routing.rules.list( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) - assert_matches_type(AsyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"]) - - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None: - rule = await async_client.email_routing.rules.list( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - enabled=True, - page=1, - per_page=5, - ) - assert_matches_type(AsyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"]) - - @parametrize - async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None: - response = await async_client.email_routing.rules.with_raw_response.list( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - rule = await response.parse() - assert_matches_type(AsyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"]) - - @parametrize - async def test_streaming_response_list(self, async_client: AsyncCloudflare) -> None: - async with async_client.email_routing.rules.with_streaming_response.list( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - rule = await response.parse() - assert_matches_type(AsyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_list(self, async_client: AsyncCloudflare) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"): - await async_client.email_routing.rules.with_raw_response.list( - zone_id="", - ) - @parametrize async def test_method_delete(self, async_client: AsyncCloudflare) -> None: rule = await async_client.email_routing.rules.delete( From 66c8009e3ddd8352a049a2b4cedd9b2794d04eff Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 14:24:27 +0000 Subject: [PATCH 05/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 48d073e0b5f..77307acd813 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2403 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-3d3e9856df2bb12cc1de02ce9b62d6efe533e3b8f3c17f1a7177a1915f3d2bf6.yml -openapi_spec_hash: ba721c8e9fc329cb059935e94f7de823 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-61e07cf647d4d06c1be0372b305a2ac6d59f5dd04665dcccee0ab93e7f4bb21d.yml +openapi_spec_hash: f780d69e713934cfb42036b9ba1462f8 config_hash: 2f529580a17438fc62cd0b47db41b6f1 From c409a181803fff937769e48817bbd52082ea36c8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 15:05:25 +0000 Subject: [PATCH 06/29] feat: feat: add MoQ relays to prod (RT-603) * feat: add moq to Media docs sidebar (RT-603) Mirrors the docs-sidebar change already merged on `staging` in bc6efc0d, ported here to `main`. Prior staging commit: https://gitlab.cfdata.org/cloudflare/sdks/cloudflare-config/-/commit/bc6efc0d56c3e82de73517fdc75e023c477cf1c5 * feat: add MoQ relays to prod (RT-603) --- .stats.yml | 6 +- api.md | 2 + src/cloudflare/_client.py | 38 + src/cloudflare/resources/moq/__init__.py | 33 + src/cloudflare/resources/moq/api.md | 34 + src/cloudflare/resources/moq/moq.py | 102 +++ .../resources/moq/relays/__init__.py | 33 + src/cloudflare/resources/moq/relays/relays.py | 709 ++++++++++++++++++ src/cloudflare/resources/moq/relays/tokens.py | 203 +++++ src/cloudflare/types/moq/__init__.py | 11 + .../types/moq/relay_create_params.py | 15 + .../types/moq/relay_create_response.py | 69 ++ .../types/moq/relay_get_response.py | 66 ++ src/cloudflare/types/moq/relay_list_params.py | 41 + .../types/moq/relay_list_response.py | 19 + .../types/moq/relay_update_params.py | 57 ++ .../types/moq/relay_update_response.py | 66 ++ src/cloudflare/types/moq/relays/__init__.py | 6 + .../types/moq/relays/token_rotate_params.py | 15 + .../types/moq/relays/token_rotate_response.py | 14 + tests/api_resources/moq/__init__.py | 1 + tests/api_resources/moq/relays/__init__.py | 1 + tests/api_resources/moq/relays/test_tokens.py | 130 ++++ tests/api_resources/moq/test_relays.py | 539 +++++++++++++ 24 files changed, 2207 insertions(+), 3 deletions(-) create mode 100644 src/cloudflare/resources/moq/__init__.py create mode 100644 src/cloudflare/resources/moq/api.md create mode 100644 src/cloudflare/resources/moq/moq.py create mode 100644 src/cloudflare/resources/moq/relays/__init__.py create mode 100644 src/cloudflare/resources/moq/relays/relays.py create mode 100644 src/cloudflare/resources/moq/relays/tokens.py create mode 100644 src/cloudflare/types/moq/__init__.py create mode 100644 src/cloudflare/types/moq/relay_create_params.py create mode 100644 src/cloudflare/types/moq/relay_create_response.py create mode 100644 src/cloudflare/types/moq/relay_get_response.py create mode 100644 src/cloudflare/types/moq/relay_list_params.py create mode 100644 src/cloudflare/types/moq/relay_list_response.py create mode 100644 src/cloudflare/types/moq/relay_update_params.py create mode 100644 src/cloudflare/types/moq/relay_update_response.py create mode 100644 src/cloudflare/types/moq/relays/__init__.py create mode 100644 src/cloudflare/types/moq/relays/token_rotate_params.py create mode 100644 src/cloudflare/types/moq/relays/token_rotate_response.py create mode 100644 tests/api_resources/moq/__init__.py create mode 100644 tests/api_resources/moq/relays/__init__.py create mode 100644 tests/api_resources/moq/relays/test_tokens.py create mode 100644 tests/api_resources/moq/test_relays.py diff --git a/.stats.yml b/.stats.yml index 77307acd813..81364e49ccf 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 2403 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-61e07cf647d4d06c1be0372b305a2ac6d59f5dd04665dcccee0ab93e7f4bb21d.yml +configured_endpoints: 2409 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-412697de2e0d5950b5488562c5d94529f201df103c4158c39258cd60e7ae49f9.yml openapi_spec_hash: f780d69e713934cfb42036b9ba1462f8 -config_hash: 2f529580a17438fc62cd0b47db41b6f1 +config_hash: bbf6df558b7c0807286a2085dd306dde diff --git a/api.md b/api.md index b05e7b8d057..7152ccab0d6 100644 --- a/api.md +++ b/api.md @@ -211,6 +211,8 @@ from cloudflare.types import ( # [Calls](src/cloudflare/resources/calls/api.md) +# [Moq](src/cloudflare/resources/moq/api.md) + # [CloudforceOne](src/cloudflare/resources/cloudforce_one/api.md) # [AIGateway](src/cloudflare/resources/ai_gateway/api.md) diff --git a/src/cloudflare/_client.py b/src/cloudflare/_client.py index af5c148ccc9..ef29c9e0f72 100644 --- a/src/cloudflare/_client.py +++ b/src/cloudflare/_client.py @@ -47,6 +47,7 @@ dns, iam, ips, + moq, rum, ssl, argo, @@ -163,6 +164,7 @@ from .resources.dns.dns import DNSResource, AsyncDNSResource from .resources.iam.iam import IAMResource, AsyncIAMResource from .resources.ips.ips import IPsResource, AsyncIPsResource + from .resources.moq.moq import MoqResource, AsyncMoqResource from .resources.rum.rum import RUMResource, AsyncRUMResource from .resources.ssl.ssl import SSLResource, AsyncSSLResource from .resources.argo.argo import ArgoResource, AsyncArgoResource @@ -965,6 +967,12 @@ def calls(self) -> CallsResource: return CallsResource(self) + @cached_property + def moq(self) -> MoqResource: + from .resources.moq import MoqResource + + return MoqResource(self) + @cached_property def cloudforce_one(self) -> CloudforceOneResource: from .resources.cloudforce_one import CloudforceOneResource @@ -1917,6 +1925,12 @@ def calls(self) -> AsyncCallsResource: return AsyncCallsResource(self) + @cached_property + def moq(self) -> AsyncMoqResource: + from .resources.moq import AsyncMoqResource + + return AsyncMoqResource(self) + @cached_property def cloudforce_one(self) -> AsyncCloudforceOneResource: from .resources.cloudforce_one import AsyncCloudforceOneResource @@ -2793,6 +2807,12 @@ def calls(self) -> calls.CallsResourceWithRawResponse: return CallsResourceWithRawResponse(self._client.calls) + @cached_property + def moq(self) -> moq.MoqResourceWithRawResponse: + from .resources.moq import MoqResourceWithRawResponse + + return MoqResourceWithRawResponse(self._client.moq) + @cached_property def cloudforce_one(self) -> cloudforce_one.CloudforceOneResourceWithRawResponse: from .resources.cloudforce_one import CloudforceOneResourceWithRawResponse @@ -3496,6 +3516,12 @@ def calls(self) -> calls.AsyncCallsResourceWithRawResponse: return AsyncCallsResourceWithRawResponse(self._client.calls) + @cached_property + def moq(self) -> moq.AsyncMoqResourceWithRawResponse: + from .resources.moq import AsyncMoqResourceWithRawResponse + + return AsyncMoqResourceWithRawResponse(self._client.moq) + @cached_property def cloudforce_one(self) -> cloudforce_one.AsyncCloudforceOneResourceWithRawResponse: from .resources.cloudforce_one import AsyncCloudforceOneResourceWithRawResponse @@ -4199,6 +4225,12 @@ def calls(self) -> calls.CallsResourceWithStreamingResponse: return CallsResourceWithStreamingResponse(self._client.calls) + @cached_property + def moq(self) -> moq.MoqResourceWithStreamingResponse: + from .resources.moq import MoqResourceWithStreamingResponse + + return MoqResourceWithStreamingResponse(self._client.moq) + @cached_property def cloudforce_one(self) -> cloudforce_one.CloudforceOneResourceWithStreamingResponse: from .resources.cloudforce_one import CloudforceOneResourceWithStreamingResponse @@ -4910,6 +4942,12 @@ def calls(self) -> calls.AsyncCallsResourceWithStreamingResponse: return AsyncCallsResourceWithStreamingResponse(self._client.calls) + @cached_property + def moq(self) -> moq.AsyncMoqResourceWithStreamingResponse: + from .resources.moq import AsyncMoqResourceWithStreamingResponse + + return AsyncMoqResourceWithStreamingResponse(self._client.moq) + @cached_property def cloudforce_one(self) -> cloudforce_one.AsyncCloudforceOneResourceWithStreamingResponse: from .resources.cloudforce_one import AsyncCloudforceOneResourceWithStreamingResponse diff --git a/src/cloudflare/resources/moq/__init__.py b/src/cloudflare/resources/moq/__init__.py new file mode 100644 index 00000000000..02338c3f29f --- /dev/null +++ b/src/cloudflare/resources/moq/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .moq import ( + MoqResource, + AsyncMoqResource, + MoqResourceWithRawResponse, + AsyncMoqResourceWithRawResponse, + MoqResourceWithStreamingResponse, + AsyncMoqResourceWithStreamingResponse, +) +from .relays import ( + RelaysResource, + AsyncRelaysResource, + RelaysResourceWithRawResponse, + AsyncRelaysResourceWithRawResponse, + RelaysResourceWithStreamingResponse, + AsyncRelaysResourceWithStreamingResponse, +) + +__all__ = [ + "RelaysResource", + "AsyncRelaysResource", + "RelaysResourceWithRawResponse", + "AsyncRelaysResourceWithRawResponse", + "RelaysResourceWithStreamingResponse", + "AsyncRelaysResourceWithStreamingResponse", + "MoqResource", + "AsyncMoqResource", + "MoqResourceWithRawResponse", + "AsyncMoqResourceWithRawResponse", + "MoqResourceWithStreamingResponse", + "AsyncMoqResourceWithStreamingResponse", +] diff --git a/src/cloudflare/resources/moq/api.md b/src/cloudflare/resources/moq/api.md new file mode 100644 index 00000000000..1e91155e3fa --- /dev/null +++ b/src/cloudflare/resources/moq/api.md @@ -0,0 +1,34 @@ +# Moq + +## Relays + +Types: + +```python +from cloudflare.types.moq import ( + RelayCreateResponse, + RelayUpdateResponse, + RelayListResponse, + RelayGetResponse, +) +``` + +Methods: + +- client.moq.relays.create(\*, account_id, \*\*params) -> Optional[RelayCreateResponse] +- client.moq.relays.update(relay_id, \*, account_id, \*\*params) -> Optional[RelayUpdateResponse] +- client.moq.relays.list(\*, account_id, \*\*params) -> SyncSinglePage[RelayListResponse] +- client.moq.relays.delete(relay_id, \*, account_id) -> object +- client.moq.relays.get(relay_id, \*, account_id) -> Optional[RelayGetResponse] + +### Tokens + +Types: + +```python +from cloudflare.types.moq.relays import TokenRotateResponse +``` + +Methods: + +- client.moq.relays.tokens.rotate(relay_id, \*, account_id, \*\*params) -> Optional[TokenRotateResponse] diff --git a/src/cloudflare/resources/moq/moq.py b/src/cloudflare/resources/moq/moq.py new file mode 100644 index 00000000000..092ebafe1d1 --- /dev/null +++ b/src/cloudflare/resources/moq/moq.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .relays.relays import ( + RelaysResource, + AsyncRelaysResource, + RelaysResourceWithRawResponse, + AsyncRelaysResourceWithRawResponse, + RelaysResourceWithStreamingResponse, + AsyncRelaysResourceWithStreamingResponse, +) + +__all__ = ["MoqResource", "AsyncMoqResource"] + + +class MoqResource(SyncAPIResource): + @cached_property + def relays(self) -> RelaysResource: + return RelaysResource(self._client) + + @cached_property + def with_raw_response(self) -> MoqResourceWithRawResponse: + """ + 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 MoqResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> MoqResourceWithStreamingResponse: + """ + 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 MoqResourceWithStreamingResponse(self) + + +class AsyncMoqResource(AsyncAPIResource): + @cached_property + def relays(self) -> AsyncRelaysResource: + return AsyncRelaysResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncMoqResourceWithRawResponse: + """ + 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 AsyncMoqResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncMoqResourceWithStreamingResponse: + """ + 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 AsyncMoqResourceWithStreamingResponse(self) + + +class MoqResourceWithRawResponse: + def __init__(self, moq: MoqResource) -> None: + self._moq = moq + + @cached_property + def relays(self) -> RelaysResourceWithRawResponse: + return RelaysResourceWithRawResponse(self._moq.relays) + + +class AsyncMoqResourceWithRawResponse: + def __init__(self, moq: AsyncMoqResource) -> None: + self._moq = moq + + @cached_property + def relays(self) -> AsyncRelaysResourceWithRawResponse: + return AsyncRelaysResourceWithRawResponse(self._moq.relays) + + +class MoqResourceWithStreamingResponse: + def __init__(self, moq: MoqResource) -> None: + self._moq = moq + + @cached_property + def relays(self) -> RelaysResourceWithStreamingResponse: + return RelaysResourceWithStreamingResponse(self._moq.relays) + + +class AsyncMoqResourceWithStreamingResponse: + def __init__(self, moq: AsyncMoqResource) -> None: + self._moq = moq + + @cached_property + def relays(self) -> AsyncRelaysResourceWithStreamingResponse: + return AsyncRelaysResourceWithStreamingResponse(self._moq.relays) diff --git a/src/cloudflare/resources/moq/relays/__init__.py b/src/cloudflare/resources/moq/relays/__init__.py new file mode 100644 index 00000000000..c69bba67272 --- /dev/null +++ b/src/cloudflare/resources/moq/relays/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .relays import ( + RelaysResource, + AsyncRelaysResource, + RelaysResourceWithRawResponse, + AsyncRelaysResourceWithRawResponse, + RelaysResourceWithStreamingResponse, + AsyncRelaysResourceWithStreamingResponse, +) +from .tokens import ( + TokensResource, + AsyncTokensResource, + TokensResourceWithRawResponse, + AsyncTokensResourceWithRawResponse, + TokensResourceWithStreamingResponse, + AsyncTokensResourceWithStreamingResponse, +) + +__all__ = [ + "TokensResource", + "AsyncTokensResource", + "TokensResourceWithRawResponse", + "AsyncTokensResourceWithRawResponse", + "TokensResourceWithStreamingResponse", + "AsyncTokensResourceWithStreamingResponse", + "RelaysResource", + "AsyncRelaysResource", + "RelaysResourceWithRawResponse", + "AsyncRelaysResourceWithRawResponse", + "RelaysResourceWithStreamingResponse", + "AsyncRelaysResourceWithStreamingResponse", +] diff --git a/src/cloudflare/resources/moq/relays/relays.py b/src/cloudflare/resources/moq/relays/relays.py new file mode 100644 index 00000000000..11eed213e54 --- /dev/null +++ b/src/cloudflare/resources/moq/relays/relays.py @@ -0,0 +1,709 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Type, Union, Optional, cast +from datetime import datetime + +import httpx + +from .tokens import ( + TokensResource, + AsyncTokensResource, + TokensResourceWithRawResponse, + AsyncTokensResourceWithRawResponse, + TokensResourceWithStreamingResponse, + AsyncTokensResourceWithStreamingResponse, +) +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._wrappers import ResultWrapper +from ....types.moq import relay_list_params, relay_create_params, relay_update_params +from ....pagination import SyncSinglePage, AsyncSinglePage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.moq.relay_get_response import RelayGetResponse +from ....types.moq.relay_list_response import RelayListResponse +from ....types.moq.relay_create_response import RelayCreateResponse +from ....types.moq.relay_update_response import RelayUpdateResponse + +__all__ = ["RelaysResource", "AsyncRelaysResource"] + + +class RelaysResource(SyncAPIResource): + @cached_property + def tokens(self) -> TokensResource: + return TokensResource(self._client) + + @cached_property + def with_raw_response(self) -> RelaysResourceWithRawResponse: + """ + 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 RelaysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RelaysResourceWithStreamingResponse: + """ + 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 RelaysResourceWithStreamingResponse(self) + + def create( + self, + *, + account_id: str, + name: 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, + ) -> Optional[RelayCreateResponse]: + """Provisions a new MoQ relay instance. + + Auto-creates a publish+subscribe token and + a subscribe-only token. Token values are included in the response (shown once). + Config is set to defaults (lingering subscribe enabled, 30s ceiling, origin + fallback off). Use PUT to modify. + + Args: + account_id: Cloudflare account identifier. + + name: Human-readable name for the relay. + + 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}") + return self._post( + path_template("/accounts/{account_id}/moq/relays", account_id=account_id), + body=maybe_transform({"name": name}, relay_create_params.RelayCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[RelayCreateResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[RelayCreateResponse]], ResultWrapper[RelayCreateResponse]), + ) + + def update( + self, + relay_id: str, + *, + account_id: str, + config: relay_update_params.Config | Omit = omit, + name: str | Omit = omit, + # 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, + ) -> Optional[RelayUpdateResponse]: + """Updates a relay's name and/or configuration. + + Partial updates: omitted fields are + preserved. Config sub-objects replace as whole objects when present. + origin_fallback and lingering_subscribe are mutually exclusive. + + Args: + account_id: Cloudflare account identifier. + + config: origin_fallback and lingering_subscribe are mutually exclusive. + + 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 relay_id: + raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}") + return self._put( + path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id), + body=maybe_transform( + { + "config": config, + "name": name, + }, + relay_update_params.RelayUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[RelayUpdateResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[RelayUpdateResponse]], ResultWrapper[RelayUpdateResponse]), + ) + + def list( + self, + *, + account_id: str, + asc: bool | Omit = omit, + created_after: Union[str, datetime] | Omit = omit, + created_before: Union[str, datetime] | Omit = omit, + per_page: int | Omit = omit, + # 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, + ) -> SyncSinglePage[RelayListResponse]: + """Lists all MoQ relays for the account. + + Returns only metadata. Config, status, and + tokens are omitted. + + Results are cursor-paginated (keyset on the `created` timestamp). Use + `created_before` / `created_after` with the `created` value of the first/last + item in a page to fetch the adjacent page. `result_info` reports the page + `count` and the `total` matching the cursor filters. + + Args: + account_id: Cloudflare account identifier. + + asc: Sort order by `created`. When true, results are returned oldest-first + (ascending); otherwise newest-first (descending, the default). + + created_after: Cursor for pagination. Returns relays created strictly after this RFC 3339 + timestamp (typically the `created` value of the last item on the current page, + to fetch the next page). + + created_before: Cursor for pagination. Returns relays created strictly before this RFC 3339 + timestamp (typically the `created` value of the first item on the current page, + to fetch the previous page). + + per_page: Maximum number of relays to return per page. + + 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}") + return self._get_api_list( + path_template("/accounts/{account_id}/moq/relays", account_id=account_id), + page=SyncSinglePage[RelayListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "asc": asc, + "created_after": created_after, + "created_before": created_before, + "per_page": per_page, + }, + relay_list_params.RelayListParams, + ), + ), + model=RelayListResponse, + ) + + def delete( + self, + relay_id: str, + *, + account_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, + ) -> object: + """ + Soft-deletes a MoQ relay. + + Args: + account_id: Cloudflare account identifier. + + 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 relay_id: + raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}") + return self._delete( + path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[object]]._unwrapper, + ), + cast_to=cast(Type[object], ResultWrapper[object]), + ) + + def get( + self, + relay_id: str, + *, + account_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, + ) -> Optional[RelayGetResponse]: + """Retrieves a single MoQ relay including config and status. + + Tokens are NOT + included. + + Args: + account_id: Cloudflare account identifier. + + 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 relay_id: + raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}") + return self._get( + path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[RelayGetResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[RelayGetResponse]], ResultWrapper[RelayGetResponse]), + ) + + +class AsyncRelaysResource(AsyncAPIResource): + @cached_property + def tokens(self) -> AsyncTokensResource: + return AsyncTokensResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncRelaysResourceWithRawResponse: + """ + 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 AsyncRelaysResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRelaysResourceWithStreamingResponse: + """ + 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 AsyncRelaysResourceWithStreamingResponse(self) + + async def create( + self, + *, + account_id: str, + name: 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, + ) -> Optional[RelayCreateResponse]: + """Provisions a new MoQ relay instance. + + Auto-creates a publish+subscribe token and + a subscribe-only token. Token values are included in the response (shown once). + Config is set to defaults (lingering subscribe enabled, 30s ceiling, origin + fallback off). Use PUT to modify. + + Args: + account_id: Cloudflare account identifier. + + name: Human-readable name for the relay. + + 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}") + return await self._post( + path_template("/accounts/{account_id}/moq/relays", account_id=account_id), + body=await async_maybe_transform({"name": name}, relay_create_params.RelayCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[RelayCreateResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[RelayCreateResponse]], ResultWrapper[RelayCreateResponse]), + ) + + async def update( + self, + relay_id: str, + *, + account_id: str, + config: relay_update_params.Config | Omit = omit, + name: str | Omit = omit, + # 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, + ) -> Optional[RelayUpdateResponse]: + """Updates a relay's name and/or configuration. + + Partial updates: omitted fields are + preserved. Config sub-objects replace as whole objects when present. + origin_fallback and lingering_subscribe are mutually exclusive. + + Args: + account_id: Cloudflare account identifier. + + config: origin_fallback and lingering_subscribe are mutually exclusive. + + 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 relay_id: + raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}") + return await self._put( + path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id), + body=await async_maybe_transform( + { + "config": config, + "name": name, + }, + relay_update_params.RelayUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[RelayUpdateResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[RelayUpdateResponse]], ResultWrapper[RelayUpdateResponse]), + ) + + def list( + self, + *, + account_id: str, + asc: bool | Omit = omit, + created_after: Union[str, datetime] | Omit = omit, + created_before: Union[str, datetime] | Omit = omit, + per_page: int | Omit = omit, + # 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, + ) -> AsyncPaginator[RelayListResponse, AsyncSinglePage[RelayListResponse]]: + """Lists all MoQ relays for the account. + + Returns only metadata. Config, status, and + tokens are omitted. + + Results are cursor-paginated (keyset on the `created` timestamp). Use + `created_before` / `created_after` with the `created` value of the first/last + item in a page to fetch the adjacent page. `result_info` reports the page + `count` and the `total` matching the cursor filters. + + Args: + account_id: Cloudflare account identifier. + + asc: Sort order by `created`. When true, results are returned oldest-first + (ascending); otherwise newest-first (descending, the default). + + created_after: Cursor for pagination. Returns relays created strictly after this RFC 3339 + timestamp (typically the `created` value of the last item on the current page, + to fetch the next page). + + created_before: Cursor for pagination. Returns relays created strictly before this RFC 3339 + timestamp (typically the `created` value of the first item on the current page, + to fetch the previous page). + + per_page: Maximum number of relays to return per page. + + 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}") + return self._get_api_list( + path_template("/accounts/{account_id}/moq/relays", account_id=account_id), + page=AsyncSinglePage[RelayListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "asc": asc, + "created_after": created_after, + "created_before": created_before, + "per_page": per_page, + }, + relay_list_params.RelayListParams, + ), + ), + model=RelayListResponse, + ) + + async def delete( + self, + relay_id: str, + *, + account_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, + ) -> object: + """ + Soft-deletes a MoQ relay. + + Args: + account_id: Cloudflare account identifier. + + 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 relay_id: + raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}") + return await self._delete( + path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[object]]._unwrapper, + ), + cast_to=cast(Type[object], ResultWrapper[object]), + ) + + async def get( + self, + relay_id: str, + *, + account_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, + ) -> Optional[RelayGetResponse]: + """Retrieves a single MoQ relay including config and status. + + Tokens are NOT + included. + + Args: + account_id: Cloudflare account identifier. + + 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 relay_id: + raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}") + return await self._get( + path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[RelayGetResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[RelayGetResponse]], ResultWrapper[RelayGetResponse]), + ) + + +class RelaysResourceWithRawResponse: + def __init__(self, relays: RelaysResource) -> None: + self._relays = relays + + self.create = to_raw_response_wrapper( + relays.create, + ) + self.update = to_raw_response_wrapper( + relays.update, + ) + self.list = to_raw_response_wrapper( + relays.list, + ) + self.delete = to_raw_response_wrapper( + relays.delete, + ) + self.get = to_raw_response_wrapper( + relays.get, + ) + + @cached_property + def tokens(self) -> TokensResourceWithRawResponse: + return TokensResourceWithRawResponse(self._relays.tokens) + + +class AsyncRelaysResourceWithRawResponse: + def __init__(self, relays: AsyncRelaysResource) -> None: + self._relays = relays + + self.create = async_to_raw_response_wrapper( + relays.create, + ) + self.update = async_to_raw_response_wrapper( + relays.update, + ) + self.list = async_to_raw_response_wrapper( + relays.list, + ) + self.delete = async_to_raw_response_wrapper( + relays.delete, + ) + self.get = async_to_raw_response_wrapper( + relays.get, + ) + + @cached_property + def tokens(self) -> AsyncTokensResourceWithRawResponse: + return AsyncTokensResourceWithRawResponse(self._relays.tokens) + + +class RelaysResourceWithStreamingResponse: + def __init__(self, relays: RelaysResource) -> None: + self._relays = relays + + self.create = to_streamed_response_wrapper( + relays.create, + ) + self.update = to_streamed_response_wrapper( + relays.update, + ) + self.list = to_streamed_response_wrapper( + relays.list, + ) + self.delete = to_streamed_response_wrapper( + relays.delete, + ) + self.get = to_streamed_response_wrapper( + relays.get, + ) + + @cached_property + def tokens(self) -> TokensResourceWithStreamingResponse: + return TokensResourceWithStreamingResponse(self._relays.tokens) + + +class AsyncRelaysResourceWithStreamingResponse: + def __init__(self, relays: AsyncRelaysResource) -> None: + self._relays = relays + + self.create = async_to_streamed_response_wrapper( + relays.create, + ) + self.update = async_to_streamed_response_wrapper( + relays.update, + ) + self.list = async_to_streamed_response_wrapper( + relays.list, + ) + self.delete = async_to_streamed_response_wrapper( + relays.delete, + ) + self.get = async_to_streamed_response_wrapper( + relays.get, + ) + + @cached_property + def tokens(self) -> AsyncTokensResourceWithStreamingResponse: + return AsyncTokensResourceWithStreamingResponse(self._relays.tokens) diff --git a/src/cloudflare/resources/moq/relays/tokens.py b/src/cloudflare/resources/moq/relays/tokens.py new file mode 100644 index 00000000000..c09937159b1 --- /dev/null +++ b/src/cloudflare/resources/moq/relays/tokens.py @@ -0,0 +1,203 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Type, Optional, cast +from typing_extensions import Literal + +import httpx + +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._wrappers import ResultWrapper +from ...._base_client import make_request_options +from ....types.moq.relays import token_rotate_params +from ....types.moq.relays.token_rotate_response import TokenRotateResponse + +__all__ = ["TokensResource", "AsyncTokensResource"] + + +class TokensResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> TokensResourceWithRawResponse: + """ + 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 TokensResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TokensResourceWithStreamingResponse: + """ + 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 TokensResourceWithStreamingResponse(self) + + def rotate( + self, + relay_id: str, + *, + account_id: str, + type: Literal["publish_subscribe", "subscribe"], + # 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, + ) -> Optional[TokenRotateResponse]: + """Generates a new token for the specified type. + + The old token is immediately + invalidated. Token value is shown once in the response. + + Args: + account_id: Cloudflare account identifier. + + type: Which token type to rotate. + + 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 relay_id: + raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}") + return self._post( + path_template( + "/accounts/{account_id}/moq/relays/{relay_id}/tokens/rotate", account_id=account_id, relay_id=relay_id + ), + body=maybe_transform({"type": type}, token_rotate_params.TokenRotateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[TokenRotateResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[TokenRotateResponse]], ResultWrapper[TokenRotateResponse]), + ) + + +class AsyncTokensResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncTokensResourceWithRawResponse: + """ + 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 AsyncTokensResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTokensResourceWithStreamingResponse: + """ + 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 AsyncTokensResourceWithStreamingResponse(self) + + async def rotate( + self, + relay_id: str, + *, + account_id: str, + type: Literal["publish_subscribe", "subscribe"], + # 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, + ) -> Optional[TokenRotateResponse]: + """Generates a new token for the specified type. + + The old token is immediately + invalidated. Token value is shown once in the response. + + Args: + account_id: Cloudflare account identifier. + + type: Which token type to rotate. + + 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 relay_id: + raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}") + return await self._post( + path_template( + "/accounts/{account_id}/moq/relays/{relay_id}/tokens/rotate", account_id=account_id, relay_id=relay_id + ), + body=await async_maybe_transform({"type": type}, token_rotate_params.TokenRotateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[TokenRotateResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[TokenRotateResponse]], ResultWrapper[TokenRotateResponse]), + ) + + +class TokensResourceWithRawResponse: + def __init__(self, tokens: TokensResource) -> None: + self._tokens = tokens + + self.rotate = to_raw_response_wrapper( + tokens.rotate, + ) + + +class AsyncTokensResourceWithRawResponse: + def __init__(self, tokens: AsyncTokensResource) -> None: + self._tokens = tokens + + self.rotate = async_to_raw_response_wrapper( + tokens.rotate, + ) + + +class TokensResourceWithStreamingResponse: + def __init__(self, tokens: TokensResource) -> None: + self._tokens = tokens + + self.rotate = to_streamed_response_wrapper( + tokens.rotate, + ) + + +class AsyncTokensResourceWithStreamingResponse: + def __init__(self, tokens: AsyncTokensResource) -> None: + self._tokens = tokens + + self.rotate = async_to_streamed_response_wrapper( + tokens.rotate, + ) diff --git a/src/cloudflare/types/moq/__init__.py b/src/cloudflare/types/moq/__init__.py new file mode 100644 index 00000000000..6c12a15de24 --- /dev/null +++ b/src/cloudflare/types/moq/__init__.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .relay_list_params import RelayListParams as RelayListParams +from .relay_get_response import RelayGetResponse as RelayGetResponse +from .relay_create_params import RelayCreateParams as RelayCreateParams +from .relay_list_response import RelayListResponse as RelayListResponse +from .relay_update_params import RelayUpdateParams as RelayUpdateParams +from .relay_create_response import RelayCreateResponse as RelayCreateResponse +from .relay_update_response import RelayUpdateResponse as RelayUpdateResponse diff --git a/src/cloudflare/types/moq/relay_create_params.py b/src/cloudflare/types/moq/relay_create_params.py new file mode 100644 index 00000000000..104c3d032c1 --- /dev/null +++ b/src/cloudflare/types/moq/relay_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RelayCreateParams"] + + +class RelayCreateParams(TypedDict, total=False): + account_id: Required[str] + """Cloudflare account identifier.""" + + name: Required[str] + """Human-readable name for the relay.""" diff --git a/src/cloudflare/types/moq/relay_create_response.py b/src/cloudflare/types/moq/relay_create_response.py new file mode 100644 index 00000000000..1cf2516282e --- /dev/null +++ b/src/cloudflare/types/moq/relay_create_response.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from ..._models import BaseModel + +__all__ = [ + "RelayCreateResponse", + "Config", + "ConfigLingeringSubscribe", + "ConfigOriginFallback", + "ConfigOriginFallbackOrigin", +] + + +class ConfigLingeringSubscribe(BaseModel): + enabled: Optional[bool] = None + + max_timeout_ms: Optional[int] = None + """Relay-level ceiling on lingering subscribe timeout (ms). Default 30000.""" + + +class ConfigOriginFallbackOrigin(BaseModel): + """A single upstream origin relay.""" + + url: Optional[str] = None + """Upstream origin relay URL.""" + + +class ConfigOriginFallback(BaseModel): + enabled: Optional[bool] = None + + origins: Optional[List[ConfigOriginFallbackOrigin]] = None + """Ordered list of upstream origin relays. + + Each entry is an object (not a bare string) so per-origin configuration can be + added in the future without another breaking change. + """ + + +class Config(BaseModel): + """origin_fallback and lingering_subscribe are mutually exclusive.""" + + lingering_subscribe: Optional[ConfigLingeringSubscribe] = None + + origin_fallback: Optional[ConfigOriginFallback] = None + + +class RelayCreateResponse(BaseModel): + """Relay with auto-generated tokens (shown once).""" + + config: Config + """origin_fallback and lingering_subscribe are mutually exclusive.""" + + created: datetime + + modified: datetime + + name: str + + token_publish_subscribe: str + """Full access token (publish + subscribe). Treat as sensitive.""" + + token_subscribe: str + """Subscribe-only token. Treat as sensitive.""" + + uid: str + """Server-generated unique identifier (32 hex chars).""" diff --git a/src/cloudflare/types/moq/relay_get_response.py b/src/cloudflare/types/moq/relay_get_response.py new file mode 100644 index 00000000000..b8d008ebd7e --- /dev/null +++ b/src/cloudflare/types/moq/relay_get_response.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "RelayGetResponse", + "Config", + "ConfigLingeringSubscribe", + "ConfigOriginFallback", + "ConfigOriginFallbackOrigin", +] + + +class ConfigLingeringSubscribe(BaseModel): + enabled: Optional[bool] = None + + max_timeout_ms: Optional[int] = None + """Relay-level ceiling on lingering subscribe timeout (ms). Default 30000.""" + + +class ConfigOriginFallbackOrigin(BaseModel): + """A single upstream origin relay.""" + + url: Optional[str] = None + """Upstream origin relay URL.""" + + +class ConfigOriginFallback(BaseModel): + enabled: Optional[bool] = None + + origins: Optional[List[ConfigOriginFallbackOrigin]] = None + """Ordered list of upstream origin relays. + + Each entry is an object (not a bare string) so per-origin configuration can be + added in the future without another breaking change. + """ + + +class Config(BaseModel): + """origin_fallback and lingering_subscribe are mutually exclusive.""" + + lingering_subscribe: Optional[ConfigLingeringSubscribe] = None + + origin_fallback: Optional[ConfigOriginFallback] = None + + +class RelayGetResponse(BaseModel): + """Full relay details (no tokens).""" + + config: Config + """origin_fallback and lingering_subscribe are mutually exclusive.""" + + created: datetime + + modified: datetime + + name: str + + uid: str + + status: Optional[Literal["connected"]] = None + """\"connected" when active, omitted otherwise.""" diff --git a/src/cloudflare/types/moq/relay_list_params.py b/src/cloudflare/types/moq/relay_list_params.py new file mode 100644 index 00000000000..6abd6a25248 --- /dev/null +++ b/src/cloudflare/types/moq/relay_list_params.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["RelayListParams"] + + +class RelayListParams(TypedDict, total=False): + account_id: Required[str] + """Cloudflare account identifier.""" + + asc: bool + """Sort order by `created`. + + When true, results are returned oldest-first (ascending); otherwise newest-first + (descending, the default). + """ + + created_after: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Cursor for pagination. + + Returns relays created strictly after this RFC 3339 timestamp (typically the + `created` value of the last item on the current page, to fetch the next page). + """ + + created_before: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Cursor for pagination. + + Returns relays created strictly before this RFC 3339 timestamp (typically the + `created` value of the first item on the current page, to fetch the previous + page). + """ + + per_page: int + """Maximum number of relays to return per page.""" diff --git a/src/cloudflare/types/moq/relay_list_response.py b/src/cloudflare/types/moq/relay_list_response.py new file mode 100644 index 00000000000..00576f6b42a --- /dev/null +++ b/src/cloudflare/types/moq/relay_list_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["RelayListResponse"] + + +class RelayListResponse(BaseModel): + """Abbreviated relay for list responses.""" + + created: datetime + + modified: datetime + + name: str + + uid: str diff --git a/src/cloudflare/types/moq/relay_update_params.py b/src/cloudflare/types/moq/relay_update_params.py new file mode 100644 index 00000000000..1a00e6e8cf2 --- /dev/null +++ b/src/cloudflare/types/moq/relay_update_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Required, TypedDict + +__all__ = [ + "RelayUpdateParams", + "Config", + "ConfigLingeringSubscribe", + "ConfigOriginFallback", + "ConfigOriginFallbackOrigin", +] + + +class RelayUpdateParams(TypedDict, total=False): + account_id: Required[str] + """Cloudflare account identifier.""" + + config: Config + """origin_fallback and lingering_subscribe are mutually exclusive.""" + + name: str + + +class ConfigLingeringSubscribe(TypedDict, total=False): + enabled: bool + + max_timeout_ms: int + """Relay-level ceiling on lingering subscribe timeout (ms). Default 30000.""" + + +class ConfigOriginFallbackOrigin(TypedDict, total=False): + """A single upstream origin relay.""" + + url: str + """Upstream origin relay URL.""" + + +class ConfigOriginFallback(TypedDict, total=False): + enabled: bool + + origins: Iterable[ConfigOriginFallbackOrigin] + """Ordered list of upstream origin relays. + + Each entry is an object (not a bare string) so per-origin configuration can be + added in the future without another breaking change. + """ + + +class Config(TypedDict, total=False): + """origin_fallback and lingering_subscribe are mutually exclusive.""" + + lingering_subscribe: ConfigLingeringSubscribe + + origin_fallback: ConfigOriginFallback diff --git a/src/cloudflare/types/moq/relay_update_response.py b/src/cloudflare/types/moq/relay_update_response.py new file mode 100644 index 00000000000..10e35818945 --- /dev/null +++ b/src/cloudflare/types/moq/relay_update_response.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "RelayUpdateResponse", + "Config", + "ConfigLingeringSubscribe", + "ConfigOriginFallback", + "ConfigOriginFallbackOrigin", +] + + +class ConfigLingeringSubscribe(BaseModel): + enabled: Optional[bool] = None + + max_timeout_ms: Optional[int] = None + """Relay-level ceiling on lingering subscribe timeout (ms). Default 30000.""" + + +class ConfigOriginFallbackOrigin(BaseModel): + """A single upstream origin relay.""" + + url: Optional[str] = None + """Upstream origin relay URL.""" + + +class ConfigOriginFallback(BaseModel): + enabled: Optional[bool] = None + + origins: Optional[List[ConfigOriginFallbackOrigin]] = None + """Ordered list of upstream origin relays. + + Each entry is an object (not a bare string) so per-origin configuration can be + added in the future without another breaking change. + """ + + +class Config(BaseModel): + """origin_fallback and lingering_subscribe are mutually exclusive.""" + + lingering_subscribe: Optional[ConfigLingeringSubscribe] = None + + origin_fallback: Optional[ConfigOriginFallback] = None + + +class RelayUpdateResponse(BaseModel): + """Full relay details (no tokens).""" + + config: Config + """origin_fallback and lingering_subscribe are mutually exclusive.""" + + created: datetime + + modified: datetime + + name: str + + uid: str + + status: Optional[Literal["connected"]] = None + """\"connected" when active, omitted otherwise.""" diff --git a/src/cloudflare/types/moq/relays/__init__.py b/src/cloudflare/types/moq/relays/__init__.py new file mode 100644 index 00000000000..e1548b499d4 --- /dev/null +++ b/src/cloudflare/types/moq/relays/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .token_rotate_params import TokenRotateParams as TokenRotateParams +from .token_rotate_response import TokenRotateResponse as TokenRotateResponse diff --git a/src/cloudflare/types/moq/relays/token_rotate_params.py b/src/cloudflare/types/moq/relays/token_rotate_params.py new file mode 100644 index 00000000000..07611c3cffc --- /dev/null +++ b/src/cloudflare/types/moq/relays/token_rotate_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["TokenRotateParams"] + + +class TokenRotateParams(TypedDict, total=False): + account_id: Required[str] + """Cloudflare account identifier.""" + + type: Required[Literal["publish_subscribe", "subscribe"]] + """Which token type to rotate.""" diff --git a/src/cloudflare/types/moq/relays/token_rotate_response.py b/src/cloudflare/types/moq/relays/token_rotate_response.py new file mode 100644 index 00000000000..5f71a3081dc --- /dev/null +++ b/src/cloudflare/types/moq/relays/token_rotate_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["TokenRotateResponse"] + + +class TokenRotateResponse(BaseModel): + token: str + """New token value (shown once). Treat as sensitive.""" + + type: Literal["publish_subscribe", "subscribe"] diff --git a/tests/api_resources/moq/__init__.py b/tests/api_resources/moq/__init__.py new file mode 100644 index 00000000000..fd8019a9a1a --- /dev/null +++ b/tests/api_resources/moq/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/moq/relays/__init__.py b/tests/api_resources/moq/relays/__init__.py new file mode 100644 index 00000000000..fd8019a9a1a --- /dev/null +++ b/tests/api_resources/moq/relays/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/moq/relays/test_tokens.py b/tests/api_resources/moq/relays/test_tokens.py new file mode 100644 index 00000000000..f39caa3990e --- /dev/null +++ b/tests/api_resources/moq/relays/test_tokens.py @@ -0,0 +1,130 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, Optional, cast + +import pytest + +from cloudflare import Cloudflare, AsyncCloudflare +from tests.utils import assert_matches_type +from cloudflare.types.moq.relays import TokenRotateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestTokens: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_rotate(self, client: Cloudflare) -> None: + token = client.moq.relays.tokens.rotate( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + type="publish_subscribe", + ) + assert_matches_type(Optional[TokenRotateResponse], token, path=["response"]) + + @parametrize + def test_raw_response_rotate(self, client: Cloudflare) -> None: + response = client.moq.relays.tokens.with_raw_response.rotate( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + type="publish_subscribe", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + token = response.parse() + assert_matches_type(Optional[TokenRotateResponse], token, path=["response"]) + + @parametrize + def test_streaming_response_rotate(self, client: Cloudflare) -> None: + with client.moq.relays.tokens.with_streaming_response.rotate( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + type="publish_subscribe", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + token = response.parse() + assert_matches_type(Optional[TokenRotateResponse], token, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_rotate(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.moq.relays.tokens.with_raw_response.rotate( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="", + type="publish_subscribe", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"): + client.moq.relays.tokens.with_raw_response.rotate( + relay_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + type="publish_subscribe", + ) + + +class TestAsyncTokens: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_rotate(self, async_client: AsyncCloudflare) -> None: + token = await async_client.moq.relays.tokens.rotate( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + type="publish_subscribe", + ) + assert_matches_type(Optional[TokenRotateResponse], token, path=["response"]) + + @parametrize + async def test_raw_response_rotate(self, async_client: AsyncCloudflare) -> None: + response = await async_client.moq.relays.tokens.with_raw_response.rotate( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + type="publish_subscribe", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + token = await response.parse() + assert_matches_type(Optional[TokenRotateResponse], token, path=["response"]) + + @parametrize + async def test_streaming_response_rotate(self, async_client: AsyncCloudflare) -> None: + async with async_client.moq.relays.tokens.with_streaming_response.rotate( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + type="publish_subscribe", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + token = await response.parse() + assert_matches_type(Optional[TokenRotateResponse], token, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_rotate(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.moq.relays.tokens.with_raw_response.rotate( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="", + type="publish_subscribe", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"): + await async_client.moq.relays.tokens.with_raw_response.rotate( + relay_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + type="publish_subscribe", + ) diff --git a/tests/api_resources/moq/test_relays.py b/tests/api_resources/moq/test_relays.py new file mode 100644 index 00000000000..ca73a9d4df8 --- /dev/null +++ b/tests/api_resources/moq/test_relays.py @@ -0,0 +1,539 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, Optional, cast + +import pytest + +from cloudflare import Cloudflare, AsyncCloudflare +from tests.utils import assert_matches_type +from cloudflare._utils import parse_datetime +from cloudflare.types.moq import ( + RelayGetResponse, + RelayListResponse, + RelayCreateResponse, + RelayUpdateResponse, +) +from cloudflare.pagination import SyncSinglePage, AsyncSinglePage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRelays: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Cloudflare) -> None: + relay = client.moq.relays.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + name="Production Live Stream", + ) + assert_matches_type(Optional[RelayCreateResponse], relay, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Cloudflare) -> None: + response = client.moq.relays.with_raw_response.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + name="Production Live Stream", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + relay = response.parse() + assert_matches_type(Optional[RelayCreateResponse], relay, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Cloudflare) -> None: + with client.moq.relays.with_streaming_response.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + name="Production Live Stream", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + relay = response.parse() + assert_matches_type(Optional[RelayCreateResponse], relay, 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 `account_id` but received ''"): + client.moq.relays.with_raw_response.create( + account_id="", + name="Production Live Stream", + ) + + @parametrize + def test_method_update(self, client: Cloudflare) -> None: + relay = client.moq.relays.update( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: Cloudflare) -> None: + relay = client.moq.relays.update( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + config={ + "lingering_subscribe": { + "enabled": True, + "max_timeout_ms": 0, + }, + "origin_fallback": { + "enabled": True, + "origins": [{"url": "url"}], + }, + }, + name="name", + ) + assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: Cloudflare) -> None: + response = client.moq.relays.with_raw_response.update( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + relay = response.parse() + assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: Cloudflare) -> None: + with client.moq.relays.with_streaming_response.update( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + relay = response.parse() + assert_matches_type(Optional[RelayUpdateResponse], relay, 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.moq.relays.with_raw_response.update( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"): + client.moq.relays.with_raw_response.update( + relay_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + @parametrize + def test_method_list(self, client: Cloudflare) -> None: + relay = client.moq.relays.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(SyncSinglePage[RelayListResponse], relay, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Cloudflare) -> None: + relay = client.moq.relays.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + asc=True, + created_after=parse_datetime("2026-03-27T15:00:00Z"), + created_before=parse_datetime("2026-03-27T15:00:00Z"), + per_page=50, + ) + assert_matches_type(SyncSinglePage[RelayListResponse], relay, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Cloudflare) -> None: + response = client.moq.relays.with_raw_response.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + relay = response.parse() + assert_matches_type(SyncSinglePage[RelayListResponse], relay, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Cloudflare) -> None: + with client.moq.relays.with_streaming_response.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + relay = response.parse() + assert_matches_type(SyncSinglePage[RelayListResponse], relay, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.moq.relays.with_raw_response.list( + account_id="", + ) + + @parametrize + def test_method_delete(self, client: Cloudflare) -> None: + relay = client.moq.relays.delete( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(object, relay, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: Cloudflare) -> None: + response = client.moq.relays.with_raw_response.delete( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + relay = response.parse() + assert_matches_type(object, relay, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: Cloudflare) -> None: + with client.moq.relays.with_streaming_response.delete( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + relay = response.parse() + assert_matches_type(object, relay, 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.moq.relays.with_raw_response.delete( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"): + client.moq.relays.with_raw_response.delete( + relay_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + @parametrize + def test_method_get(self, client: Cloudflare) -> None: + relay = client.moq.relays.get( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(Optional[RelayGetResponse], relay, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Cloudflare) -> None: + response = client.moq.relays.with_raw_response.get( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + relay = response.parse() + assert_matches_type(Optional[RelayGetResponse], relay, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Cloudflare) -> None: + with client.moq.relays.with_streaming_response.get( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + relay = response.parse() + assert_matches_type(Optional[RelayGetResponse], relay, 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.moq.relays.with_raw_response.get( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"): + client.moq.relays.with_raw_response.get( + relay_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + +class TestAsyncRelays: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncCloudflare) -> None: + relay = await async_client.moq.relays.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + name="Production Live Stream", + ) + assert_matches_type(Optional[RelayCreateResponse], relay, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None: + response = await async_client.moq.relays.with_raw_response.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + name="Production Live Stream", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + relay = await response.parse() + assert_matches_type(Optional[RelayCreateResponse], relay, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncCloudflare) -> None: + async with async_client.moq.relays.with_streaming_response.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + name="Production Live Stream", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + relay = await response.parse() + assert_matches_type(Optional[RelayCreateResponse], relay, 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 `account_id` but received ''"): + await async_client.moq.relays.with_raw_response.create( + account_id="", + name="Production Live Stream", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncCloudflare) -> None: + relay = await async_client.moq.relays.update( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncCloudflare) -> None: + relay = await async_client.moq.relays.update( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + config={ + "lingering_subscribe": { + "enabled": True, + "max_timeout_ms": 0, + }, + "origin_fallback": { + "enabled": True, + "origins": [{"url": "url"}], + }, + }, + name="name", + ) + assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncCloudflare) -> None: + response = await async_client.moq.relays.with_raw_response.update( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + relay = await response.parse() + assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncCloudflare) -> None: + async with async_client.moq.relays.with_streaming_response.update( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + relay = await response.parse() + assert_matches_type(Optional[RelayUpdateResponse], relay, 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.moq.relays.with_raw_response.update( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"): + await async_client.moq.relays.with_raw_response.update( + relay_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncCloudflare) -> None: + relay = await async_client.moq.relays.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(AsyncSinglePage[RelayListResponse], relay, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None: + relay = await async_client.moq.relays.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + asc=True, + created_after=parse_datetime("2026-03-27T15:00:00Z"), + created_before=parse_datetime("2026-03-27T15:00:00Z"), + per_page=50, + ) + assert_matches_type(AsyncSinglePage[RelayListResponse], relay, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None: + response = await async_client.moq.relays.with_raw_response.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + relay = await response.parse() + assert_matches_type(AsyncSinglePage[RelayListResponse], relay, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncCloudflare) -> None: + async with async_client.moq.relays.with_streaming_response.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + relay = await response.parse() + assert_matches_type(AsyncSinglePage[RelayListResponse], relay, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.moq.relays.with_raw_response.list( + account_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncCloudflare) -> None: + relay = await async_client.moq.relays.delete( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(object, relay, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncCloudflare) -> None: + response = await async_client.moq.relays.with_raw_response.delete( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + relay = await response.parse() + assert_matches_type(object, relay, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncCloudflare) -> None: + async with async_client.moq.relays.with_streaming_response.delete( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + relay = await response.parse() + assert_matches_type(object, relay, 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.moq.relays.with_raw_response.delete( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"): + await async_client.moq.relays.with_raw_response.delete( + relay_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + @parametrize + async def test_method_get(self, async_client: AsyncCloudflare) -> None: + relay = await async_client.moq.relays.get( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(Optional[RelayGetResponse], relay, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None: + response = await async_client.moq.relays.with_raw_response.get( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + relay = await response.parse() + assert_matches_type(Optional[RelayGetResponse], relay, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None: + async with async_client.moq.relays.with_streaming_response.get( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + relay = await response.parse() + assert_matches_type(Optional[RelayGetResponse], relay, 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.moq.relays.with_raw_response.get( + relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"): + await async_client.moq.relays.with_raw_response.get( + relay_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) From 72346d33a7a9ca0134d2237df71bcc411f1b07bf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 15:43:40 +0000 Subject: [PATCH 07/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 81364e49ccf..f3a8c38eee8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-412697de2e0d5950b5488562c5d94529f201df103c4158c39258cd60e7ae49f9.yml -openapi_spec_hash: f780d69e713934cfb42036b9ba1462f8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-041f9983e7767d34364afd30ab678d4b78dee37f689403f7fc99c5c5403a13d1.yml +openapi_spec_hash: f249c6c4570c884e29c3383010462342 config_hash: bbf6df558b7c0807286a2085dd306dde From 99d3b3787e108fab78c096b8e1ea43963924582a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 15:49:46 +0000 Subject: [PATCH 08/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f3a8c38eee8..f2ae3e506be 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-041f9983e7767d34364afd30ab678d4b78dee37f689403f7fc99c5c5403a13d1.yml -openapi_spec_hash: f249c6c4570c884e29c3383010462342 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-2638067e2ee443dc7c7a5fa38c2a7ed38f7bbd6de7db58aa4b364539ea96000c.yml +openapi_spec_hash: b955d89313a0c7f4e27f2aaa04cd13a8 config_hash: bbf6df558b7c0807286a2085dd306dde From 613f3c0890a9191dfa35899e4301824ed2bc5d47 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 17:02:05 +0000 Subject: [PATCH 09/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f2ae3e506be..17840a4c4cc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-2638067e2ee443dc7c7a5fa38c2a7ed38f7bbd6de7db58aa4b364539ea96000c.yml -openapi_spec_hash: b955d89313a0c7f4e27f2aaa04cd13a8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-55d48ca4bab251e3897d7ebdf9956ac543ac6e75c6b033975ac677f46478c57b.yml +openapi_spec_hash: 9d3814ffe9696e264383b91cfa1a391a config_hash: bbf6df558b7c0807286a2085dd306dde From 168da5a7c47f96947e64c0be9a84cc8eb39589b2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 17:34:05 +0000 Subject: [PATCH 10/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 17840a4c4cc..e1a2f0adb82 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-55d48ca4bab251e3897d7ebdf9956ac543ac6e75c6b033975ac677f46478c57b.yml -openapi_spec_hash: 9d3814ffe9696e264383b91cfa1a391a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-2bc11b07e225d2fe0ea5a39f2c2bc2106dbaf49d902b52b582713938b468b9a1.yml +openapi_spec_hash: d58db17fcb028178d579bce0112e1c71 config_hash: bbf6df558b7c0807286a2085dd306dde From ec9a675368ed8486cc42a9922a17f5e851ed8432 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:15:10 +0000 Subject: [PATCH 11/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index e1a2f0adb82..b7d0d85cfb5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-2bc11b07e225d2fe0ea5a39f2c2bc2106dbaf49d902b52b582713938b468b9a1.yml -openapi_spec_hash: d58db17fcb028178d579bce0112e1c71 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-64c75bcb4da367adfd4f8ef4a3f56ba72319441f8192961275c5908530d96b6a.yml +openapi_spec_hash: 5f2a1a80ef585c73c6b546c4075d18f8 config_hash: bbf6df558b7c0807286a2085dd306dde From 11ac69771f23fd1340c512cac0874ab7fcb65287 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:48:16 +0000 Subject: [PATCH 12/29] feat: fix(moq): render MoQ via explicit custom casing (RT-603) * fix(moq): render MoQ via explicit custom casing (RT-603) The moq token was 'initialism: true', which renders MOQ. MoQ (Media over QUIC) is a mixed-case acronym, so use an explicit casing block (like DDoS): pascal/capital = MoQ, camel/snake = moq. Fixes the API docs/SDK rendering 'Moq'. (cherry picked from commit bbc15361ceba0c208977f40ba6e689e1611fc39d) --- .stats.yml | 4 +-- api.md | 2 +- src/cloudflare/_client.py | 38 ++++++++++++------------ src/cloudflare/resources/moq/__init__.py | 24 +++++++-------- src/cloudflare/resources/moq/api.md | 2 +- src/cloudflare/resources/moq/moq.py | 38 ++++++++++++------------ 6 files changed, 54 insertions(+), 54 deletions(-) diff --git a/.stats.yml b/.stats.yml index b7d0d85cfb5..573ea3ddcbc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-64c75bcb4da367adfd4f8ef4a3f56ba72319441f8192961275c5908530d96b6a.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-dfb646d2d43385c312e319a5f5c0d09c4f8b41d89e4486cd58acc970bfe74223.yml openapi_spec_hash: 5f2a1a80ef585c73c6b546c4075d18f8 -config_hash: bbf6df558b7c0807286a2085dd306dde +config_hash: 517f3639f0355eae78476ce617456698 diff --git a/api.md b/api.md index 7152ccab0d6..0269445d227 100644 --- a/api.md +++ b/api.md @@ -211,7 +211,7 @@ from cloudflare.types import ( # [Calls](src/cloudflare/resources/calls/api.md) -# [Moq](src/cloudflare/resources/moq/api.md) +# [MoQ](src/cloudflare/resources/moq/api.md) # [CloudforceOne](src/cloudflare/resources/cloudforce_one/api.md) diff --git a/src/cloudflare/_client.py b/src/cloudflare/_client.py index ef29c9e0f72..b25df3317a5 100644 --- a/src/cloudflare/_client.py +++ b/src/cloudflare/_client.py @@ -164,7 +164,7 @@ from .resources.dns.dns import DNSResource, AsyncDNSResource from .resources.iam.iam import IAMResource, AsyncIAMResource from .resources.ips.ips import IPsResource, AsyncIPsResource - from .resources.moq.moq import MoqResource, AsyncMoqResource + from .resources.moq.moq import MoQResource, AsyncMoQResource from .resources.rum.rum import RUMResource, AsyncRUMResource from .resources.ssl.ssl import SSLResource, AsyncSSLResource from .resources.argo.argo import ArgoResource, AsyncArgoResource @@ -968,10 +968,10 @@ def calls(self) -> CallsResource: return CallsResource(self) @cached_property - def moq(self) -> MoqResource: - from .resources.moq import MoqResource + def moq(self) -> MoQResource: + from .resources.moq import MoQResource - return MoqResource(self) + return MoQResource(self) @cached_property def cloudforce_one(self) -> CloudforceOneResource: @@ -1926,10 +1926,10 @@ def calls(self) -> AsyncCallsResource: return AsyncCallsResource(self) @cached_property - def moq(self) -> AsyncMoqResource: - from .resources.moq import AsyncMoqResource + def moq(self) -> AsyncMoQResource: + from .resources.moq import AsyncMoQResource - return AsyncMoqResource(self) + return AsyncMoQResource(self) @cached_property def cloudforce_one(self) -> AsyncCloudforceOneResource: @@ -2808,10 +2808,10 @@ def calls(self) -> calls.CallsResourceWithRawResponse: return CallsResourceWithRawResponse(self._client.calls) @cached_property - def moq(self) -> moq.MoqResourceWithRawResponse: - from .resources.moq import MoqResourceWithRawResponse + def moq(self) -> moq.MoQResourceWithRawResponse: + from .resources.moq import MoQResourceWithRawResponse - return MoqResourceWithRawResponse(self._client.moq) + return MoQResourceWithRawResponse(self._client.moq) @cached_property def cloudforce_one(self) -> cloudforce_one.CloudforceOneResourceWithRawResponse: @@ -3517,10 +3517,10 @@ def calls(self) -> calls.AsyncCallsResourceWithRawResponse: return AsyncCallsResourceWithRawResponse(self._client.calls) @cached_property - def moq(self) -> moq.AsyncMoqResourceWithRawResponse: - from .resources.moq import AsyncMoqResourceWithRawResponse + def moq(self) -> moq.AsyncMoQResourceWithRawResponse: + from .resources.moq import AsyncMoQResourceWithRawResponse - return AsyncMoqResourceWithRawResponse(self._client.moq) + return AsyncMoQResourceWithRawResponse(self._client.moq) @cached_property def cloudforce_one(self) -> cloudforce_one.AsyncCloudforceOneResourceWithRawResponse: @@ -4226,10 +4226,10 @@ def calls(self) -> calls.CallsResourceWithStreamingResponse: return CallsResourceWithStreamingResponse(self._client.calls) @cached_property - def moq(self) -> moq.MoqResourceWithStreamingResponse: - from .resources.moq import MoqResourceWithStreamingResponse + def moq(self) -> moq.MoQResourceWithStreamingResponse: + from .resources.moq import MoQResourceWithStreamingResponse - return MoqResourceWithStreamingResponse(self._client.moq) + return MoQResourceWithStreamingResponse(self._client.moq) @cached_property def cloudforce_one(self) -> cloudforce_one.CloudforceOneResourceWithStreamingResponse: @@ -4943,10 +4943,10 @@ def calls(self) -> calls.AsyncCallsResourceWithStreamingResponse: return AsyncCallsResourceWithStreamingResponse(self._client.calls) @cached_property - def moq(self) -> moq.AsyncMoqResourceWithStreamingResponse: - from .resources.moq import AsyncMoqResourceWithStreamingResponse + def moq(self) -> moq.AsyncMoQResourceWithStreamingResponse: + from .resources.moq import AsyncMoQResourceWithStreamingResponse - return AsyncMoqResourceWithStreamingResponse(self._client.moq) + return AsyncMoQResourceWithStreamingResponse(self._client.moq) @cached_property def cloudforce_one(self) -> cloudforce_one.AsyncCloudforceOneResourceWithStreamingResponse: diff --git a/src/cloudflare/resources/moq/__init__.py b/src/cloudflare/resources/moq/__init__.py index 02338c3f29f..8b3e963eae5 100644 --- a/src/cloudflare/resources/moq/__init__.py +++ b/src/cloudflare/resources/moq/__init__.py @@ -1,12 +1,12 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .moq import ( - MoqResource, - AsyncMoqResource, - MoqResourceWithRawResponse, - AsyncMoqResourceWithRawResponse, - MoqResourceWithStreamingResponse, - AsyncMoqResourceWithStreamingResponse, + MoQResource, + AsyncMoQResource, + MoQResourceWithRawResponse, + AsyncMoQResourceWithRawResponse, + MoQResourceWithStreamingResponse, + AsyncMoQResourceWithStreamingResponse, ) from .relays import ( RelaysResource, @@ -24,10 +24,10 @@ "AsyncRelaysResourceWithRawResponse", "RelaysResourceWithStreamingResponse", "AsyncRelaysResourceWithStreamingResponse", - "MoqResource", - "AsyncMoqResource", - "MoqResourceWithRawResponse", - "AsyncMoqResourceWithRawResponse", - "MoqResourceWithStreamingResponse", - "AsyncMoqResourceWithStreamingResponse", + "MoQResource", + "AsyncMoQResource", + "MoQResourceWithRawResponse", + "AsyncMoQResourceWithRawResponse", + "MoQResourceWithStreamingResponse", + "AsyncMoQResourceWithStreamingResponse", ] diff --git a/src/cloudflare/resources/moq/api.md b/src/cloudflare/resources/moq/api.md index 1e91155e3fa..434c6a9d326 100644 --- a/src/cloudflare/resources/moq/api.md +++ b/src/cloudflare/resources/moq/api.md @@ -1,4 +1,4 @@ -# Moq +# MoQ ## Relays diff --git a/src/cloudflare/resources/moq/moq.py b/src/cloudflare/resources/moq/moq.py index 092ebafe1d1..75102e3b192 100644 --- a/src/cloudflare/resources/moq/moq.py +++ b/src/cloudflare/resources/moq/moq.py @@ -13,61 +13,61 @@ AsyncRelaysResourceWithStreamingResponse, ) -__all__ = ["MoqResource", "AsyncMoqResource"] +__all__ = ["MoQResource", "AsyncMoQResource"] -class MoqResource(SyncAPIResource): +class MoQResource(SyncAPIResource): @cached_property def relays(self) -> RelaysResource: return RelaysResource(self._client) @cached_property - def with_raw_response(self) -> MoqResourceWithRawResponse: + def with_raw_response(self) -> MoQResourceWithRawResponse: """ 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 MoqResourceWithRawResponse(self) + return MoQResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> MoqResourceWithStreamingResponse: + def with_streaming_response(self) -> MoQResourceWithStreamingResponse: """ 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 MoqResourceWithStreamingResponse(self) + return MoQResourceWithStreamingResponse(self) -class AsyncMoqResource(AsyncAPIResource): +class AsyncMoQResource(AsyncAPIResource): @cached_property def relays(self) -> AsyncRelaysResource: return AsyncRelaysResource(self._client) @cached_property - def with_raw_response(self) -> AsyncMoqResourceWithRawResponse: + def with_raw_response(self) -> AsyncMoQResourceWithRawResponse: """ 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 AsyncMoqResourceWithRawResponse(self) + return AsyncMoQResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> AsyncMoqResourceWithStreamingResponse: + def with_streaming_response(self) -> AsyncMoQResourceWithStreamingResponse: """ 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 AsyncMoqResourceWithStreamingResponse(self) + return AsyncMoQResourceWithStreamingResponse(self) -class MoqResourceWithRawResponse: - def __init__(self, moq: MoqResource) -> None: +class MoQResourceWithRawResponse: + def __init__(self, moq: MoQResource) -> None: self._moq = moq @cached_property @@ -75,8 +75,8 @@ def relays(self) -> RelaysResourceWithRawResponse: return RelaysResourceWithRawResponse(self._moq.relays) -class AsyncMoqResourceWithRawResponse: - def __init__(self, moq: AsyncMoqResource) -> None: +class AsyncMoQResourceWithRawResponse: + def __init__(self, moq: AsyncMoQResource) -> None: self._moq = moq @cached_property @@ -84,8 +84,8 @@ def relays(self) -> AsyncRelaysResourceWithRawResponse: return AsyncRelaysResourceWithRawResponse(self._moq.relays) -class MoqResourceWithStreamingResponse: - def __init__(self, moq: MoqResource) -> None: +class MoQResourceWithStreamingResponse: + def __init__(self, moq: MoQResource) -> None: self._moq = moq @cached_property @@ -93,8 +93,8 @@ def relays(self) -> RelaysResourceWithStreamingResponse: return RelaysResourceWithStreamingResponse(self._moq.relays) -class AsyncMoqResourceWithStreamingResponse: - def __init__(self, moq: AsyncMoqResource) -> None: +class AsyncMoQResourceWithStreamingResponse: + def __init__(self, moq: AsyncMoQResource) -> None: self._moq = moq @cached_property From 9aec347626a34439aae2f871e8a2a20a86f9ce44 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:24:06 +0000 Subject: [PATCH 13/29] chore(api): update composite API spec --- .stats.yml | 4 ++-- .../magic_transit/gre_tunnel_bulk_update_response.py | 6 ++++++ .../types/magic_transit/gre_tunnel_create_params.py | 6 ++++++ .../types/magic_transit/gre_tunnel_create_response.py | 6 ++++++ .../types/magic_transit/gre_tunnel_delete_response.py | 6 ++++++ .../types/magic_transit/gre_tunnel_get_response.py | 6 ++++++ .../types/magic_transit/gre_tunnel_list_response.py | 6 ++++++ .../types/magic_transit/gre_tunnel_update_response.py | 6 ++++++ .../magic_transit/ipsec_tunnel_bulk_update_response.py | 6 ++++++ .../types/magic_transit/ipsec_tunnel_create_params.py | 6 ++++++ .../types/magic_transit/ipsec_tunnel_create_response.py | 6 ++++++ .../types/magic_transit/ipsec_tunnel_delete_response.py | 6 ++++++ .../types/magic_transit/ipsec_tunnel_get_response.py | 6 ++++++ .../types/magic_transit/ipsec_tunnel_list_response.py | 6 ++++++ .../types/magic_transit/ipsec_tunnel_update_params.py | 6 ++++++ .../types/magic_transit/ipsec_tunnel_update_response.py | 6 ++++++ tests/api_resources/magic_transit/test_gre_tunnels.py | 4 ++++ tests/api_resources/magic_transit/test_ipsec_tunnels.py | 8 ++++++++ 18 files changed, 104 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 573ea3ddcbc..956c818f721 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-dfb646d2d43385c312e319a5f5c0d09c4f8b41d89e4486cd58acc970bfe74223.yml -openapi_spec_hash: 5f2a1a80ef585c73c6b546c4075d18f8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-35dd01c27b85d96e67988097dd1b36ee52da0da30f300d6d992b807b8fef6d09.yml +openapi_spec_hash: fe1a3e3ff1462519c983de7ce2b67ef7 config_hash: 517f3639f0355eae78476ce617456698 diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_bulk_update_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_bulk_update_response.py index ffacd0ffc74..924cb5b6907 100644 --- a/src/cloudflare/types/magic_transit/gre_tunnel_bulk_update_response.py +++ b/src/cloudflare/types/magic_transit/gre_tunnel_bulk_update_response.py @@ -23,12 +23,18 @@ class ModifiedGRETunnelBGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_create_params.py b/src/cloudflare/types/magic_transit/gre_tunnel_create_params.py index 19e85148542..2c45cd93e3b 100644 --- a/src/cloudflare/types/magic_transit/gre_tunnel_create_params.py +++ b/src/cloudflare/types/magic_transit/gre_tunnel_create_params.py @@ -81,12 +81,18 @@ class BGP(TypedDict, total=False): customer_asn: Required[int] """ASN used on the customer end of the BGP session""" + export_filter_id: str + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: SequenceNotStr[str] """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: str + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: str """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_create_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_create_response.py index ee95b6fa8b2..3ae3d7d2282 100644 --- a/src/cloudflare/types/magic_transit/gre_tunnel_create_response.py +++ b/src/cloudflare/types/magic_transit/gre_tunnel_create_response.py @@ -22,12 +22,18 @@ class BGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_delete_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_delete_response.py index 2607e9e41bf..dc494813de7 100644 --- a/src/cloudflare/types/magic_transit/gre_tunnel_delete_response.py +++ b/src/cloudflare/types/magic_transit/gre_tunnel_delete_response.py @@ -23,12 +23,18 @@ class DeletedGRETunnelBGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_get_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_get_response.py index ef8eb5cacd8..32fd9527373 100644 --- a/src/cloudflare/types/magic_transit/gre_tunnel_get_response.py +++ b/src/cloudflare/types/magic_transit/gre_tunnel_get_response.py @@ -23,12 +23,18 @@ class GRETunnelBGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_list_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_list_response.py index e4d0cc9ec77..88c54249db2 100644 --- a/src/cloudflare/types/magic_transit/gre_tunnel_list_response.py +++ b/src/cloudflare/types/magic_transit/gre_tunnel_list_response.py @@ -23,12 +23,18 @@ class GRETunnelBGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_update_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_update_response.py index a1bba508388..28ca8a99198 100644 --- a/src/cloudflare/types/magic_transit/gre_tunnel_update_response.py +++ b/src/cloudflare/types/magic_transit/gre_tunnel_update_response.py @@ -23,12 +23,18 @@ class ModifiedGRETunnelBGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_bulk_update_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_bulk_update_response.py index 09d3a3e6bc4..2bd26657b61 100644 --- a/src/cloudflare/types/magic_transit/ipsec_tunnel_bulk_update_response.py +++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_bulk_update_response.py @@ -25,12 +25,18 @@ class ModifiedIPSECTunnelBGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_create_params.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_create_params.py index cbfb544cfcf..42ad6c4cd85 100644 --- a/src/cloudflare/types/magic_transit/ipsec_tunnel_create_params.py +++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_create_params.py @@ -83,12 +83,18 @@ class BGP(TypedDict, total=False): customer_asn: Required[int] """ASN used on the customer end of the BGP session""" + export_filter_id: str + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: SequenceNotStr[str] """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: str + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: str """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_create_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_create_response.py index 3dfa6eef0d3..3e415ceebb6 100644 --- a/src/cloudflare/types/magic_transit/ipsec_tunnel_create_response.py +++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_create_response.py @@ -24,12 +24,18 @@ class BGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_delete_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_delete_response.py index 196c7dd3204..2b266a603ef 100644 --- a/src/cloudflare/types/magic_transit/ipsec_tunnel_delete_response.py +++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_delete_response.py @@ -25,12 +25,18 @@ class DeletedIPSECTunnelBGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_get_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_get_response.py index f8bc64d3ba0..62e830f64ce 100644 --- a/src/cloudflare/types/magic_transit/ipsec_tunnel_get_response.py +++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_get_response.py @@ -25,12 +25,18 @@ class IPSECTunnelBGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_list_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_list_response.py index 2b400df722d..5245477a780 100644 --- a/src/cloudflare/types/magic_transit/ipsec_tunnel_list_response.py +++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_list_response.py @@ -25,12 +25,18 @@ class IPSECTunnelBGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_update_params.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_update_params.py index b8d6325f32e..c4d9e5b9bf4 100644 --- a/src/cloudflare/types/magic_transit/ipsec_tunnel_update_params.py +++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_update_params.py @@ -83,12 +83,18 @@ class BGP(TypedDict, total=False): customer_asn: Required[int] """ASN used on the customer end of the BGP session""" + export_filter_id: str + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: SequenceNotStr[str] """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: str + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: str """MD5 key to use for session authentication. diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_update_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_update_response.py index 9e82141a51d..217447332b4 100644 --- a/src/cloudflare/types/magic_transit/ipsec_tunnel_update_response.py +++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_update_response.py @@ -25,12 +25,18 @@ class ModifiedIPSECTunnelBGP(BaseModel): customer_asn: int """ASN used on the customer end of the BGP session""" + export_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes advertised to the customer.""" + extra_prefixes: Optional[List[str]] = None """ Prefixes in this list will be advertised to the customer device, in addition to the routes in the Magic routing table. """ + import_filter_id: Optional[str] = None + """ID of the BGP filter profile applied to routes received from the customer.""" + md5_key: Optional[str] = None """MD5 key to use for session authentication. diff --git a/tests/api_resources/magic_transit/test_gre_tunnels.py b/tests/api_resources/magic_transit/test_gre_tunnels.py index 95128794a5d..dc8a2757d5f 100644 --- a/tests/api_resources/magic_transit/test_gre_tunnels.py +++ b/tests/api_resources/magic_transit/test_gre_tunnels.py @@ -48,7 +48,9 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None: automatic_return_routing=True, bgp={ "customer_asn": 0, + "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "extra_prefixes": ["string"], + "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "md5_key": "md5_key", }, description="Tunnel for ISP X", @@ -456,7 +458,9 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare automatic_return_routing=True, bgp={ "customer_asn": 0, + "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "extra_prefixes": ["string"], + "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "md5_key": "md5_key", }, description="Tunnel for ISP X", diff --git a/tests/api_resources/magic_transit/test_ipsec_tunnels.py b/tests/api_resources/magic_transit/test_ipsec_tunnels.py index a9c532cf3eb..d7d4aa22b0e 100644 --- a/tests/api_resources/magic_transit/test_ipsec_tunnels.py +++ b/tests/api_resources/magic_transit/test_ipsec_tunnels.py @@ -46,7 +46,9 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None: automatic_return_routing=True, bgp={ "customer_asn": 0, + "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "extra_prefixes": ["string"], + "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "md5_key": "md5_key", }, custom_remote_identities={"fqdn_id": "fqdn_id"}, @@ -130,7 +132,9 @@ def test_method_update_with_all_params(self, client: Cloudflare) -> None: automatic_return_routing=True, bgp={ "customer_asn": 0, + "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "extra_prefixes": ["string"], + "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "md5_key": "md5_key", }, custom_remote_identities={"fqdn_id": "fqdn_id"}, @@ -576,7 +580,9 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare automatic_return_routing=True, bgp={ "customer_asn": 0, + "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "extra_prefixes": ["string"], + "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "md5_key": "md5_key", }, custom_remote_identities={"fqdn_id": "fqdn_id"}, @@ -660,7 +666,9 @@ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare automatic_return_routing=True, bgp={ "customer_asn": 0, + "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "extra_prefixes": ["string"], + "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789", "md5_key": "md5_key", }, custom_remote_identities={"fqdn_id": "fqdn_id"}, From 13b7798691bd99ab50d802dc25fe9506dfa01c0a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:29:39 +0000 Subject: [PATCH 14/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 956c818f721..52810b2a391 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-35dd01c27b85d96e67988097dd1b36ee52da0da30f300d6d992b807b8fef6d09.yml -openapi_spec_hash: fe1a3e3ff1462519c983de7ce2b67ef7 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-c4a28c454f80a442d6fe13dc6727fb51eef746adf2266ea644e78273a086c71a.yml +openapi_spec_hash: daebb36576d7be6faedb17ac5aa55024 config_hash: 517f3639f0355eae78476ce617456698 From 7606580667b0e1073e9f5f284125b7bf9a1d3899 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:55:01 +0000 Subject: [PATCH 15/29] chore(api): update composite API spec --- .stats.yml | 4 +- .../zero_trust/organizations/organizations.py | 48 +++++++++---------- .../access/application_create_params.py | 4 +- .../access/application_create_response.py | 4 +- .../access/application_get_response.py | 4 +- .../access/application_list_response.py | 4 +- .../access/application_update_params.py | 4 +- .../access/application_update_response.py | 4 +- .../types/zero_trust/organization.py | 22 ++++----- .../zero_trust/organization_create_params.py | 22 ++++----- .../zero_trust/organization_update_params.py | 22 ++++----- .../zero_trust/access/test_applications.py | 8 ++-- .../zero_trust/test_organizations.py | 16 +++---- 13 files changed, 83 insertions(+), 83 deletions(-) diff --git a/.stats.yml b/.stats.yml index 52810b2a391..751397d37b7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-c4a28c454f80a442d6fe13dc6727fb51eef746adf2266ea644e78273a086c71a.yml -openapi_spec_hash: daebb36576d7be6faedb17ac5aa55024 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-26b407db284a9e5c2ae968d8134f1682fd10ffd1dc4a79aa3b2c6fe10f2251db.yml +openapi_spec_hash: c0570ee25e7fd6eea844983b22b042fd config_hash: 517f3639f0355eae78476ce617456698 diff --git a/src/cloudflare/resources/zero_trust/organizations/organizations.py b/src/cloudflare/resources/zero_trust/organizations/organizations.py index d66cc53d08b..a6bbf066792 100644 --- a/src/cloudflare/resources/zero_trust/organizations/organizations.py +++ b/src/cloudflare/resources/zero_trust/organizations/organizations.py @@ -76,8 +76,8 @@ def create( is_ui_read_only: bool | Omit = omit, login_design: LoginDesignParam | Omit = omit, mfa_config: organization_create_params.MfaConfig | Omit = omit, + mfa_piv_key_requirements: organization_create_params.MfaPivKeyRequirements | Omit = omit, mfa_required_for_all_apps: bool | Omit = omit, - mfa_ssh_piv_key_requirements: organization_create_params.MfaSSHPivKeyRequirements | Omit = omit, session_duration: str | Omit = omit, ui_read_only_toggle_reason: str | Omit = omit, user_seat_expiration_inactive_time: str | Omit = omit, @@ -123,13 +123,13 @@ def create( mfa_config: Configures multi-factor authentication (MFA) settings for an organization. + mfa_piv_key_requirements: Configures PIV key requirements for MFA using hardware security keys. + mfa_required_for_all_apps: Determines whether global MFA settings apply to applications by default. The organization must have MFA enabled with at least one authentication method and a session duration configured. Note: 'allowed_authenticators' cannot only contain - 'ssh_piv_key' if the organization has any non-infrastructure applications - because PIV keys are only compatible with infrastructure apps. - - mfa_ssh_piv_key_requirements: Configures SSH PIV key requirements for MFA using hardware security keys. + 'piv_key' if the organization has any non-infrastructure applications because + PIV keys are only compatible with infrastructure apps. session_duration: The amount of time that tokens issued for applications will be valid. Must be in the format `300ms` or `2h45m`. Valid time units are: ns, us (or µs), ms, s, m, @@ -183,8 +183,8 @@ def create( "is_ui_read_only": is_ui_read_only, "login_design": login_design, "mfa_config": mfa_config, + "mfa_piv_key_requirements": mfa_piv_key_requirements, "mfa_required_for_all_apps": mfa_required_for_all_apps, - "mfa_ssh_piv_key_requirements": mfa_ssh_piv_key_requirements, "session_duration": session_duration, "ui_read_only_toggle_reason": ui_read_only_toggle_reason, "user_seat_expiration_inactive_time": user_seat_expiration_inactive_time, @@ -216,8 +216,8 @@ def update( is_ui_read_only: bool | Omit = omit, login_design: LoginDesignParam | Omit = omit, mfa_config: organization_update_params.MfaConfig | Omit = omit, + mfa_piv_key_requirements: organization_update_params.MfaPivKeyRequirements | Omit = omit, mfa_required_for_all_apps: bool | Omit = omit, - mfa_ssh_piv_key_requirements: organization_update_params.MfaSSHPivKeyRequirements | Omit = omit, name: str | Omit = omit, session_duration: str | Omit = omit, ui_read_only_toggle_reason: str | Omit = omit, @@ -262,13 +262,13 @@ def update( mfa_config: Configures multi-factor authentication (MFA) settings for an organization. + mfa_piv_key_requirements: Configures PIV key requirements for MFA using hardware security keys. + mfa_required_for_all_apps: Determines whether global MFA settings apply to applications by default. The organization must have MFA enabled with at least one authentication method and a session duration configured. Note: 'allowed_authenticators' cannot only contain - 'ssh_piv_key' if the organization has any non-infrastructure applications - because PIV keys are only compatible with infrastructure apps. - - mfa_ssh_piv_key_requirements: Configures SSH PIV key requirements for MFA using hardware security keys. + 'piv_key' if the organization has any non-infrastructure applications because + PIV keys are only compatible with infrastructure apps. name: The name of your Zero Trust organization. @@ -324,8 +324,8 @@ def update( "is_ui_read_only": is_ui_read_only, "login_design": login_design, "mfa_config": mfa_config, + "mfa_piv_key_requirements": mfa_piv_key_requirements, "mfa_required_for_all_apps": mfa_required_for_all_apps, - "mfa_ssh_piv_key_requirements": mfa_ssh_piv_key_requirements, "name": name, "session_duration": session_duration, "ui_read_only_toggle_reason": ui_read_only_toggle_reason, @@ -526,8 +526,8 @@ async def create( is_ui_read_only: bool | Omit = omit, login_design: LoginDesignParam | Omit = omit, mfa_config: organization_create_params.MfaConfig | Omit = omit, + mfa_piv_key_requirements: organization_create_params.MfaPivKeyRequirements | Omit = omit, mfa_required_for_all_apps: bool | Omit = omit, - mfa_ssh_piv_key_requirements: organization_create_params.MfaSSHPivKeyRequirements | Omit = omit, session_duration: str | Omit = omit, ui_read_only_toggle_reason: str | Omit = omit, user_seat_expiration_inactive_time: str | Omit = omit, @@ -573,13 +573,13 @@ async def create( mfa_config: Configures multi-factor authentication (MFA) settings for an organization. + mfa_piv_key_requirements: Configures PIV key requirements for MFA using hardware security keys. + mfa_required_for_all_apps: Determines whether global MFA settings apply to applications by default. The organization must have MFA enabled with at least one authentication method and a session duration configured. Note: 'allowed_authenticators' cannot only contain - 'ssh_piv_key' if the organization has any non-infrastructure applications - because PIV keys are only compatible with infrastructure apps. - - mfa_ssh_piv_key_requirements: Configures SSH PIV key requirements for MFA using hardware security keys. + 'piv_key' if the organization has any non-infrastructure applications because + PIV keys are only compatible with infrastructure apps. session_duration: The amount of time that tokens issued for applications will be valid. Must be in the format `300ms` or `2h45m`. Valid time units are: ns, us (or µs), ms, s, m, @@ -633,8 +633,8 @@ async def create( "is_ui_read_only": is_ui_read_only, "login_design": login_design, "mfa_config": mfa_config, + "mfa_piv_key_requirements": mfa_piv_key_requirements, "mfa_required_for_all_apps": mfa_required_for_all_apps, - "mfa_ssh_piv_key_requirements": mfa_ssh_piv_key_requirements, "session_duration": session_duration, "ui_read_only_toggle_reason": ui_read_only_toggle_reason, "user_seat_expiration_inactive_time": user_seat_expiration_inactive_time, @@ -666,8 +666,8 @@ async def update( is_ui_read_only: bool | Omit = omit, login_design: LoginDesignParam | Omit = omit, mfa_config: organization_update_params.MfaConfig | Omit = omit, + mfa_piv_key_requirements: organization_update_params.MfaPivKeyRequirements | Omit = omit, mfa_required_for_all_apps: bool | Omit = omit, - mfa_ssh_piv_key_requirements: organization_update_params.MfaSSHPivKeyRequirements | Omit = omit, name: str | Omit = omit, session_duration: str | Omit = omit, ui_read_only_toggle_reason: str | Omit = omit, @@ -712,13 +712,13 @@ async def update( mfa_config: Configures multi-factor authentication (MFA) settings for an organization. + mfa_piv_key_requirements: Configures PIV key requirements for MFA using hardware security keys. + mfa_required_for_all_apps: Determines whether global MFA settings apply to applications by default. The organization must have MFA enabled with at least one authentication method and a session duration configured. Note: 'allowed_authenticators' cannot only contain - 'ssh_piv_key' if the organization has any non-infrastructure applications - because PIV keys are only compatible with infrastructure apps. - - mfa_ssh_piv_key_requirements: Configures SSH PIV key requirements for MFA using hardware security keys. + 'piv_key' if the organization has any non-infrastructure applications because + PIV keys are only compatible with infrastructure apps. name: The name of your Zero Trust organization. @@ -774,8 +774,8 @@ async def update( "is_ui_read_only": is_ui_read_only, "login_design": login_design, "mfa_config": mfa_config, + "mfa_piv_key_requirements": mfa_piv_key_requirements, "mfa_required_for_all_apps": mfa_required_for_all_apps, - "mfa_ssh_piv_key_requirements": mfa_ssh_piv_key_requirements, "name": name, "session_duration": session_duration, "ui_read_only_toggle_reason": ui_read_only_toggle_reason, diff --git a/src/cloudflare/types/zero_trust/access/application_create_params.py b/src/cloudflare/types/zero_trust/access/application_create_params.py index bc66ae1b3ee..a26b435aeff 100644 --- a/src/cloudflare/types/zero_trust/access/application_create_params.py +++ b/src/cloudflare/types/zero_trust/access/application_create_params.py @@ -3039,10 +3039,10 @@ class InfrastructureApplicationPolicyMfaConfig(TypedDict, total=False): Configures multi-factor authentication (MFA) settings for infrastructure applications. """ - allowed_authenticators: List[Literal["ssh_piv_key"]] + allowed_authenticators: List[Literal["piv_key"]] """Lists the MFA methods that users can authenticate with. - For infrastructure applications, only `ssh_piv_key` is supported. + For infrastructure applications, only `piv_key` is supported. """ mfa_disabled: bool diff --git a/src/cloudflare/types/zero_trust/access/application_create_response.py b/src/cloudflare/types/zero_trust/access/application_create_response.py index cc2f8dd4e3d..dd44a3f27ae 100644 --- a/src/cloudflare/types/zero_trust/access/application_create_response.py +++ b/src/cloudflare/types/zero_trust/access/application_create_response.py @@ -3068,10 +3068,10 @@ class InfrastructureApplicationPolicyMfaConfig(BaseModel): Configures multi-factor authentication (MFA) settings for infrastructure applications. """ - allowed_authenticators: Optional[List[Literal["ssh_piv_key"]]] = None + allowed_authenticators: Optional[List[Literal["piv_key"]]] = None """Lists the MFA methods that users can authenticate with. - For infrastructure applications, only `ssh_piv_key` is supported. + For infrastructure applications, only `piv_key` is supported. """ mfa_disabled: Optional[bool] = None diff --git a/src/cloudflare/types/zero_trust/access/application_get_response.py b/src/cloudflare/types/zero_trust/access/application_get_response.py index 6e024bb393d..07f245a1390 100644 --- a/src/cloudflare/types/zero_trust/access/application_get_response.py +++ b/src/cloudflare/types/zero_trust/access/application_get_response.py @@ -3068,10 +3068,10 @@ class InfrastructureApplicationPolicyMfaConfig(BaseModel): Configures multi-factor authentication (MFA) settings for infrastructure applications. """ - allowed_authenticators: Optional[List[Literal["ssh_piv_key"]]] = None + allowed_authenticators: Optional[List[Literal["piv_key"]]] = None """Lists the MFA methods that users can authenticate with. - For infrastructure applications, only `ssh_piv_key` is supported. + For infrastructure applications, only `piv_key` is supported. """ mfa_disabled: Optional[bool] = None diff --git a/src/cloudflare/types/zero_trust/access/application_list_response.py b/src/cloudflare/types/zero_trust/access/application_list_response.py index 9bf9d11f75a..d08bb9bc31b 100644 --- a/src/cloudflare/types/zero_trust/access/application_list_response.py +++ b/src/cloudflare/types/zero_trust/access/application_list_response.py @@ -3068,10 +3068,10 @@ class InfrastructureApplicationPolicyMfaConfig(BaseModel): Configures multi-factor authentication (MFA) settings for infrastructure applications. """ - allowed_authenticators: Optional[List[Literal["ssh_piv_key"]]] = None + allowed_authenticators: Optional[List[Literal["piv_key"]]] = None """Lists the MFA methods that users can authenticate with. - For infrastructure applications, only `ssh_piv_key` is supported. + For infrastructure applications, only `piv_key` is supported. """ mfa_disabled: Optional[bool] = None diff --git a/src/cloudflare/types/zero_trust/access/application_update_params.py b/src/cloudflare/types/zero_trust/access/application_update_params.py index 04d3ad5a066..d1917d8e54c 100644 --- a/src/cloudflare/types/zero_trust/access/application_update_params.py +++ b/src/cloudflare/types/zero_trust/access/application_update_params.py @@ -3039,10 +3039,10 @@ class InfrastructureApplicationPolicyMfaConfig(TypedDict, total=False): Configures multi-factor authentication (MFA) settings for infrastructure applications. """ - allowed_authenticators: List[Literal["ssh_piv_key"]] + allowed_authenticators: List[Literal["piv_key"]] """Lists the MFA methods that users can authenticate with. - For infrastructure applications, only `ssh_piv_key` is supported. + For infrastructure applications, only `piv_key` is supported. """ mfa_disabled: bool diff --git a/src/cloudflare/types/zero_trust/access/application_update_response.py b/src/cloudflare/types/zero_trust/access/application_update_response.py index e7020006b87..935cb9a3967 100644 --- a/src/cloudflare/types/zero_trust/access/application_update_response.py +++ b/src/cloudflare/types/zero_trust/access/application_update_response.py @@ -3068,10 +3068,10 @@ class InfrastructureApplicationPolicyMfaConfig(BaseModel): Configures multi-factor authentication (MFA) settings for infrastructure applications. """ - allowed_authenticators: Optional[List[Literal["ssh_piv_key"]]] = None + allowed_authenticators: Optional[List[Literal["piv_key"]]] = None """Lists the MFA methods that users can authenticate with. - For infrastructure applications, only `ssh_piv_key` is supported. + For infrastructure applications, only `piv_key` is supported. """ mfa_disabled: Optional[bool] = None diff --git a/src/cloudflare/types/zero_trust/organization.py b/src/cloudflare/types/zero_trust/organization.py index b4f96e14ab1..b50bb9f26f3 100644 --- a/src/cloudflare/types/zero_trust/organization.py +++ b/src/cloudflare/types/zero_trust/organization.py @@ -6,7 +6,7 @@ from ..._models import BaseModel from .login_design import LoginDesign -__all__ = ["Organization", "CustomPages", "MfaConfig", "MfaSSHPivKeyRequirements"] +__all__ = ["Organization", "CustomPages", "MfaConfig", "MfaPivKeyRequirements"] class CustomPages(BaseModel): @@ -23,7 +23,7 @@ class CustomPages(BaseModel): class MfaConfig(BaseModel): """Configures multi-factor authentication (MFA) settings for an organization.""" - allowed_authenticators: Optional[List[Literal["totp", "biometrics", "security_key", "ssh_piv_key"]]] = None + allowed_authenticators: Optional[List[Literal["totp", "biometrics", "security_key", "piv_key"]]] = None """Lists the MFA methods that users can authenticate with.""" amr_matching_session_duration: Optional[str] = None @@ -45,8 +45,8 @@ class MfaConfig(BaseModel): """ -class MfaSSHPivKeyRequirements(BaseModel): - """Configures SSH PIV key requirements for MFA using hardware security keys.""" +class MfaPivKeyRequirements(BaseModel): + """Configures PIV key requirements for MFA using hardware security keys.""" pin_policy: Optional[Literal["never", "once", "always"]] = None """Defines when a PIN is required to use the SSH key. @@ -57,8 +57,8 @@ class MfaSSHPivKeyRequirements(BaseModel): require_fips_device: Optional[bool] = None """ - Requires the SSH PIV key to be stored on a FIPS 140-2 Level 1 or higher - validated device. + Requires the PIV key to be stored on a FIPS 140-2 Level 1 or higher validated + device. """ ssh_key_size: Optional[List[Literal[256, 384, 521, 2048, 3072, 4096]]] = None @@ -128,18 +128,18 @@ class Organization(BaseModel): mfa_config: Optional[MfaConfig] = None """Configures multi-factor authentication (MFA) settings for an organization.""" + mfa_piv_key_requirements: Optional[MfaPivKeyRequirements] = None + """Configures PIV key requirements for MFA using hardware security keys.""" + mfa_required_for_all_apps: Optional[bool] = None """Determines whether global MFA settings apply to applications by default. The organization must have MFA enabled with at least one authentication method and a session duration configured. Note: 'allowed_authenticators' cannot only - contain 'ssh_piv_key' if the organization has any non-infrastructure - applications because PIV keys are only compatible with infrastructure apps. + contain 'piv_key' if the organization has any non-infrastructure applications + because PIV keys are only compatible with infrastructure apps. """ - mfa_ssh_piv_key_requirements: Optional[MfaSSHPivKeyRequirements] = None - """Configures SSH PIV key requirements for MFA using hardware security keys.""" - name: Optional[str] = None """The name of your Zero Trust organization.""" diff --git a/src/cloudflare/types/zero_trust/organization_create_params.py b/src/cloudflare/types/zero_trust/organization_create_params.py index 7ace8bedc86..28ae4b5321f 100644 --- a/src/cloudflare/types/zero_trust/organization_create_params.py +++ b/src/cloudflare/types/zero_trust/organization_create_params.py @@ -8,7 +8,7 @@ from ..._types import SequenceNotStr from .login_design_param import LoginDesignParam -__all__ = ["OrganizationCreateParams", "MfaConfig", "MfaSSHPivKeyRequirements"] +__all__ = ["OrganizationCreateParams", "MfaConfig", "MfaPivKeyRequirements"] class OrganizationCreateParams(TypedDict, total=False): @@ -64,18 +64,18 @@ class OrganizationCreateParams(TypedDict, total=False): mfa_config: MfaConfig """Configures multi-factor authentication (MFA) settings for an organization.""" + mfa_piv_key_requirements: MfaPivKeyRequirements + """Configures PIV key requirements for MFA using hardware security keys.""" + mfa_required_for_all_apps: bool """Determines whether global MFA settings apply to applications by default. The organization must have MFA enabled with at least one authentication method and a session duration configured. Note: 'allowed_authenticators' cannot only - contain 'ssh_piv_key' if the organization has any non-infrastructure - applications because PIV keys are only compatible with infrastructure apps. + contain 'piv_key' if the organization has any non-infrastructure applications + because PIV keys are only compatible with infrastructure apps. """ - mfa_ssh_piv_key_requirements: MfaSSHPivKeyRequirements - """Configures SSH PIV key requirements for MFA using hardware security keys.""" - session_duration: str """The amount of time that tokens issued for applications will be valid. @@ -105,7 +105,7 @@ class OrganizationCreateParams(TypedDict, total=False): class MfaConfig(TypedDict, total=False): """Configures multi-factor authentication (MFA) settings for an organization.""" - allowed_authenticators: List[Literal["totp", "biometrics", "security_key", "ssh_piv_key"]] + allowed_authenticators: List[Literal["totp", "biometrics", "security_key", "piv_key"]] """Lists the MFA methods that users can authenticate with.""" amr_matching_session_duration: str @@ -127,8 +127,8 @@ class MfaConfig(TypedDict, total=False): """ -class MfaSSHPivKeyRequirements(TypedDict, total=False): - """Configures SSH PIV key requirements for MFA using hardware security keys.""" +class MfaPivKeyRequirements(TypedDict, total=False): + """Configures PIV key requirements for MFA using hardware security keys.""" pin_policy: Literal["never", "once", "always"] """Defines when a PIN is required to use the SSH key. @@ -139,8 +139,8 @@ class MfaSSHPivKeyRequirements(TypedDict, total=False): require_fips_device: bool """ - Requires the SSH PIV key to be stored on a FIPS 140-2 Level 1 or higher - validated device. + Requires the PIV key to be stored on a FIPS 140-2 Level 1 or higher validated + device. """ ssh_key_size: Iterable[Literal[256, 384, 521, 2048, 3072, 4096]] diff --git a/src/cloudflare/types/zero_trust/organization_update_params.py b/src/cloudflare/types/zero_trust/organization_update_params.py index 1405dd3ffb4..ab77fea8cd1 100644 --- a/src/cloudflare/types/zero_trust/organization_update_params.py +++ b/src/cloudflare/types/zero_trust/organization_update_params.py @@ -8,7 +8,7 @@ from ..._types import SequenceNotStr from .login_design_param import LoginDesignParam -__all__ = ["OrganizationUpdateParams", "CustomPages", "MfaConfig", "MfaSSHPivKeyRequirements"] +__all__ = ["OrganizationUpdateParams", "CustomPages", "MfaConfig", "MfaPivKeyRequirements"] class OrganizationUpdateParams(TypedDict, total=False): @@ -63,18 +63,18 @@ class OrganizationUpdateParams(TypedDict, total=False): mfa_config: MfaConfig """Configures multi-factor authentication (MFA) settings for an organization.""" + mfa_piv_key_requirements: MfaPivKeyRequirements + """Configures PIV key requirements for MFA using hardware security keys.""" + mfa_required_for_all_apps: bool """Determines whether global MFA settings apply to applications by default. The organization must have MFA enabled with at least one authentication method and a session duration configured. Note: 'allowed_authenticators' cannot only - contain 'ssh_piv_key' if the organization has any non-infrastructure - applications because PIV keys are only compatible with infrastructure apps. + contain 'piv_key' if the organization has any non-infrastructure applications + because PIV keys are only compatible with infrastructure apps. """ - mfa_ssh_piv_key_requirements: MfaSSHPivKeyRequirements - """Configures SSH PIV key requirements for MFA using hardware security keys.""" - name: str """The name of your Zero Trust organization.""" @@ -118,7 +118,7 @@ class CustomPages(TypedDict, total=False): class MfaConfig(TypedDict, total=False): """Configures multi-factor authentication (MFA) settings for an organization.""" - allowed_authenticators: List[Literal["totp", "biometrics", "security_key", "ssh_piv_key"]] + allowed_authenticators: List[Literal["totp", "biometrics", "security_key", "piv_key"]] """Lists the MFA methods that users can authenticate with.""" amr_matching_session_duration: str @@ -140,8 +140,8 @@ class MfaConfig(TypedDict, total=False): """ -class MfaSSHPivKeyRequirements(TypedDict, total=False): - """Configures SSH PIV key requirements for MFA using hardware security keys.""" +class MfaPivKeyRequirements(TypedDict, total=False): + """Configures PIV key requirements for MFA using hardware security keys.""" pin_policy: Literal["never", "once", "always"] """Defines when a PIN is required to use the SSH key. @@ -152,8 +152,8 @@ class MfaSSHPivKeyRequirements(TypedDict, total=False): require_fips_device: bool """ - Requires the SSH PIV key to be stored on a FIPS 140-2 Level 1 or higher - validated device. + Requires the PIV key to be stored on a FIPS 140-2 Level 1 or higher validated + device. """ ssh_key_size: Iterable[Literal[256, 384, 521, 2048, 3072, 4096]] diff --git a/tests/api_resources/zero_trust/access/test_applications.py b/tests/api_resources/zero_trust/access/test_applications.py index 8de6e58e2a8..027d2b429d3 100644 --- a/tests/api_resources/zero_trust/access/test_applications.py +++ b/tests/api_resources/zero_trust/access/test_applications.py @@ -1084,7 +1084,7 @@ def test_method_create_with_all_params_overload_10(self, client: Cloudflare) -> }, "exclude": [{"certificate": {}}], "mfa_config": { - "allowed_authenticators": ["ssh_piv_key"], + "allowed_authenticators": ["piv_key"], "mfa_disabled": False, "session_duration": "24h", }, @@ -2874,7 +2874,7 @@ def test_method_update_with_all_params_overload_10(self, client: Cloudflare) -> }, "exclude": [{"certificate": {}}], "mfa_config": { - "allowed_authenticators": ["ssh_piv_key"], + "allowed_authenticators": ["piv_key"], "mfa_disabled": False, "session_duration": "24h", }, @@ -4879,7 +4879,7 @@ async def test_method_create_with_all_params_overload_10(self, async_client: Asy }, "exclude": [{"certificate": {}}], "mfa_config": { - "allowed_authenticators": ["ssh_piv_key"], + "allowed_authenticators": ["piv_key"], "mfa_disabled": False, "session_duration": "24h", }, @@ -6669,7 +6669,7 @@ async def test_method_update_with_all_params_overload_10(self, async_client: Asy }, "exclude": [{"certificate": {}}], "mfa_config": { - "allowed_authenticators": ["ssh_piv_key"], + "allowed_authenticators": ["piv_key"], "mfa_disabled": False, "session_duration": "24h", }, diff --git a/tests/api_resources/zero_trust/test_organizations.py b/tests/api_resources/zero_trust/test_organizations.py index acc5cd43afc..a45ee5e8ddc 100644 --- a/tests/api_resources/zero_trust/test_organizations.py +++ b/tests/api_resources/zero_trust/test_organizations.py @@ -55,14 +55,14 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None: "required_aaguids": "2fc0579f-8113-47ea-b116-bb5a8db9202a", "session_duration": "24h", }, - mfa_required_for_all_apps=False, - mfa_ssh_piv_key_requirements={ + mfa_piv_key_requirements={ "pin_policy": "always", "require_fips_device": True, "ssh_key_size": [256, 2048], "ssh_key_type": ["ecdsa", "rsa"], "touch_policy": "always", }, + mfa_required_for_all_apps=False, session_duration="24h", ui_read_only_toggle_reason="Temporarily turn off the UI read only lock to make a change via the UI", user_seat_expiration_inactive_time="730h", @@ -153,14 +153,14 @@ def test_method_update_with_all_params(self, client: Cloudflare) -> None: "required_aaguids": "2fc0579f-8113-47ea-b116-bb5a8db9202a", "session_duration": "24h", }, - mfa_required_for_all_apps=False, - mfa_ssh_piv_key_requirements={ + mfa_piv_key_requirements={ "pin_policy": "always", "require_fips_device": True, "ssh_key_size": [256, 2048], "ssh_key_type": ["ecdsa", "rsa"], "touch_policy": "always", }, + mfa_required_for_all_apps=False, name="Widget Corps Internal Applications", session_duration="24h", ui_read_only_toggle_reason="Temporarily turn off the UI read only lock to make a change via the UI", @@ -369,14 +369,14 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare "required_aaguids": "2fc0579f-8113-47ea-b116-bb5a8db9202a", "session_duration": "24h", }, - mfa_required_for_all_apps=False, - mfa_ssh_piv_key_requirements={ + mfa_piv_key_requirements={ "pin_policy": "always", "require_fips_device": True, "ssh_key_size": [256, 2048], "ssh_key_type": ["ecdsa", "rsa"], "touch_policy": "always", }, + mfa_required_for_all_apps=False, session_duration="24h", ui_read_only_toggle_reason="Temporarily turn off the UI read only lock to make a change via the UI", user_seat_expiration_inactive_time="730h", @@ -467,14 +467,14 @@ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare "required_aaguids": "2fc0579f-8113-47ea-b116-bb5a8db9202a", "session_duration": "24h", }, - mfa_required_for_all_apps=False, - mfa_ssh_piv_key_requirements={ + mfa_piv_key_requirements={ "pin_policy": "always", "require_fips_device": True, "ssh_key_size": [256, 2048], "ssh_key_type": ["ecdsa", "rsa"], "touch_policy": "always", }, + mfa_required_for_all_apps=False, name="Widget Corps Internal Applications", session_duration="24h", ui_read_only_toggle_reason="Temporarily turn off the UI read only lock to make a change via the UI", From e3c84dd7883ce9eac3f387d23f0b7be7dd15bfef Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:44:53 +0000 Subject: [PATCH 16/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 751397d37b7..8c3426431ea 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-26b407db284a9e5c2ae968d8134f1682fd10ffd1dc4a79aa3b2c6fe10f2251db.yml -openapi_spec_hash: c0570ee25e7fd6eea844983b22b042fd +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-3bc4ce7eca3632c44c1b71ccda42d36f53d701e3e121ea05537d09043a83c8bf.yml +openapi_spec_hash: e88ddeaf2c1497d2086b397c081ac4c2 config_hash: 517f3639f0355eae78476ce617456698 From c85c761bec446378c4377d6c70fb054e8cf69840 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 10:02:53 +0000 Subject: [PATCH 17/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 8c3426431ea..d09b233defe 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-3bc4ce7eca3632c44c1b71ccda42d36f53d701e3e121ea05537d09043a83c8bf.yml -openapi_spec_hash: e88ddeaf2c1497d2086b397c081ac4c2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-ba370928dece9a05ad2fe24062332b8d1889430fd8319d6983ced5525de0f7a6.yml +openapi_spec_hash: ffcc17e9bcee0136d58e837b3581f399 config_hash: 517f3639f0355eae78476ce617456698 From 65f2fbcb9ca23c9de23fd544eab8fe53d25a5ae5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 14:26:29 +0000 Subject: [PATCH 18/29] chore(api): update composite API spec --- .stats.yml | 4 +- .../ai_gateway/billing/spending_limit.py | 37 +++-- .../ai_gateway/billing/test_spending_limit.py | 128 ++++++++++-------- 3 files changed, 99 insertions(+), 70 deletions(-) diff --git a/.stats.yml b/.stats.yml index d09b233defe..334f6ded852 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-ba370928dece9a05ad2fe24062332b8d1889430fd8319d6983ced5525de0f7a6.yml -openapi_spec_hash: ffcc17e9bcee0136d58e837b3581f399 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-5e7a38305b9b369ac76d03acb28e80d6e0becfa5d830183c5d05ca3acbe57d97.yml +openapi_spec_hash: ce574d5447a6a01dde1d36709a279533 config_hash: 517f3639f0355eae78476ce617456698 diff --git a/src/cloudflare/resources/ai_gateway/billing/spending_limit.py b/src/cloudflare/resources/ai_gateway/billing/spending_limit.py index 8235d6ccb1c..1d7a18a89a4 100644 --- a/src/cloudflare/resources/ai_gateway/billing/spending_limit.py +++ b/src/cloudflare/resources/ai_gateway/billing/spending_limit.py @@ -2,6 +2,7 @@ from __future__ import annotations +import typing_extensions from typing import Type, cast from typing_extensions import Literal @@ -45,6 +46,7 @@ def with_streaming_response(self) -> SpendingLimitResourceWithStreamingResponse: """ return SpendingLimitResourceWithStreamingResponse(self) + @typing_extensions.deprecated("deprecated") def create( self, *, @@ -60,7 +62,10 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Configure a spending limit with amount, strategy, and duration. + Deprecated: spending limits can no longer be created, enabled, or modified and + this endpoint always responds 403. Use the new AI Gateway spend limits instead: + https://developers.cloudflare.com/ai-gateway/features/spend-limits/. Existing + limits can be removed via DELETE /spending-limit. Args: amount: Spending limit amount in cents (min 100). @@ -194,6 +199,7 @@ def with_streaming_response(self) -> AsyncSpendingLimitResourceWithStreamingResp """ return AsyncSpendingLimitResourceWithStreamingResponse(self) + @typing_extensions.deprecated("deprecated") async def create( self, *, @@ -209,7 +215,10 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> object: """ - Configure a spending limit with amount, strategy, and duration. + Deprecated: spending limits can no longer be created, enabled, or modified and + this endpoint always responds 403. Use the new AI Gateway spend limits instead: + https://developers.cloudflare.com/ai-gateway/features/spend-limits/. Existing + limits can be removed via DELETE /spending-limit. Args: amount: Spending limit amount in cents (min 100). @@ -327,8 +336,10 @@ class SpendingLimitResourceWithRawResponse: def __init__(self, spending_limit: SpendingLimitResource) -> None: self._spending_limit = spending_limit - self.create = to_raw_response_wrapper( - spending_limit.create, + self.create = ( # pyright: ignore[reportDeprecated] + to_raw_response_wrapper( + spending_limit.create, # pyright: ignore[reportDeprecated], + ) ) self.delete = to_raw_response_wrapper( spending_limit.delete, @@ -342,8 +353,10 @@ class AsyncSpendingLimitResourceWithRawResponse: def __init__(self, spending_limit: AsyncSpendingLimitResource) -> None: self._spending_limit = spending_limit - self.create = async_to_raw_response_wrapper( - spending_limit.create, + self.create = ( # pyright: ignore[reportDeprecated] + async_to_raw_response_wrapper( + spending_limit.create, # pyright: ignore[reportDeprecated], + ) ) self.delete = async_to_raw_response_wrapper( spending_limit.delete, @@ -357,8 +370,10 @@ class SpendingLimitResourceWithStreamingResponse: def __init__(self, spending_limit: SpendingLimitResource) -> None: self._spending_limit = spending_limit - self.create = to_streamed_response_wrapper( - spending_limit.create, + self.create = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + spending_limit.create, # pyright: ignore[reportDeprecated], + ) ) self.delete = to_streamed_response_wrapper( spending_limit.delete, @@ -372,8 +387,10 @@ class AsyncSpendingLimitResourceWithStreamingResponse: def __init__(self, spending_limit: AsyncSpendingLimitResource) -> None: self._spending_limit = spending_limit - self.create = async_to_streamed_response_wrapper( - spending_limit.create, + self.create = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + spending_limit.create, # pyright: ignore[reportDeprecated], + ) ) self.delete = async_to_streamed_response_wrapper( spending_limit.delete, diff --git a/tests/api_resources/ai_gateway/billing/test_spending_limit.py b/tests/api_resources/ai_gateway/billing/test_spending_limit.py index 78b1b9aad87..55637811211 100644 --- a/tests/api_resources/ai_gateway/billing/test_spending_limit.py +++ b/tests/api_resources/ai_gateway/billing/test_spending_limit.py @@ -11,6 +11,8 @@ from tests.utils import assert_matches_type from cloudflare.types.ai_gateway.billing import SpendingLimitGetResponse +# pyright: reportDeprecated=false + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -20,23 +22,26 @@ class TestSpendingLimit: @pytest.mark.skip(reason="HTTP 404 error from prism") @parametrize def test_method_create(self, client: Cloudflare) -> None: - spending_limit = client.ai_gateway.billing.spending_limit.create( - account_id="account_id", - amount=10000, - duration="monthly", - strategy="fixed", - ) + with pytest.warns(DeprecationWarning): + spending_limit = client.ai_gateway.billing.spending_limit.create( + account_id="account_id", + amount=10000, + duration="monthly", + strategy="fixed", + ) + assert_matches_type(object, spending_limit, path=["response"]) @pytest.mark.skip(reason="HTTP 404 error from prism") @parametrize def test_raw_response_create(self, client: Cloudflare) -> None: - response = client.ai_gateway.billing.spending_limit.with_raw_response.create( - account_id="account_id", - amount=10000, - duration="monthly", - strategy="fixed", - ) + with pytest.warns(DeprecationWarning): + response = client.ai_gateway.billing.spending_limit.with_raw_response.create( + account_id="account_id", + amount=10000, + duration="monthly", + strategy="fixed", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -46,30 +51,32 @@ def test_raw_response_create(self, client: Cloudflare) -> None: @pytest.mark.skip(reason="HTTP 404 error from prism") @parametrize def test_streaming_response_create(self, client: Cloudflare) -> None: - with client.ai_gateway.billing.spending_limit.with_streaming_response.create( - account_id="account_id", - amount=10000, - duration="monthly", - strategy="fixed", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + with client.ai_gateway.billing.spending_limit.with_streaming_response.create( + account_id="account_id", + amount=10000, + duration="monthly", + strategy="fixed", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - spending_limit = response.parse() - assert_matches_type(object, spending_limit, path=["response"]) + spending_limit = response.parse() + assert_matches_type(object, spending_limit, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="HTTP 404 error from prism") @parametrize def test_path_params_create(self, client: Cloudflare) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): - client.ai_gateway.billing.spending_limit.with_raw_response.create( - account_id="", - amount=10000, - duration="monthly", - strategy="fixed", - ) + with pytest.warns(DeprecationWarning): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.ai_gateway.billing.spending_limit.with_raw_response.create( + account_id="", + amount=10000, + duration="monthly", + strategy="fixed", + ) @pytest.mark.skip(reason="HTTP 404 error from prism") @parametrize @@ -164,23 +171,26 @@ class TestAsyncSpendingLimit: @pytest.mark.skip(reason="HTTP 404 error from prism") @parametrize async def test_method_create(self, async_client: AsyncCloudflare) -> None: - spending_limit = await async_client.ai_gateway.billing.spending_limit.create( - account_id="account_id", - amount=10000, - duration="monthly", - strategy="fixed", - ) + with pytest.warns(DeprecationWarning): + spending_limit = await async_client.ai_gateway.billing.spending_limit.create( + account_id="account_id", + amount=10000, + duration="monthly", + strategy="fixed", + ) + assert_matches_type(object, spending_limit, path=["response"]) @pytest.mark.skip(reason="HTTP 404 error from prism") @parametrize async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None: - response = await async_client.ai_gateway.billing.spending_limit.with_raw_response.create( - account_id="account_id", - amount=10000, - duration="monthly", - strategy="fixed", - ) + with pytest.warns(DeprecationWarning): + response = await async_client.ai_gateway.billing.spending_limit.with_raw_response.create( + account_id="account_id", + amount=10000, + duration="monthly", + strategy="fixed", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -190,30 +200,32 @@ async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None: @pytest.mark.skip(reason="HTTP 404 error from prism") @parametrize async def test_streaming_response_create(self, async_client: AsyncCloudflare) -> None: - async with async_client.ai_gateway.billing.spending_limit.with_streaming_response.create( - account_id="account_id", - amount=10000, - duration="monthly", - strategy="fixed", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + async with async_client.ai_gateway.billing.spending_limit.with_streaming_response.create( + account_id="account_id", + amount=10000, + duration="monthly", + strategy="fixed", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - spending_limit = await response.parse() - assert_matches_type(object, spending_limit, path=["response"]) + spending_limit = await response.parse() + assert_matches_type(object, spending_limit, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="HTTP 404 error from prism") @parametrize async def test_path_params_create(self, async_client: AsyncCloudflare) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): - await async_client.ai_gateway.billing.spending_limit.with_raw_response.create( - account_id="", - amount=10000, - duration="monthly", - strategy="fixed", - ) + with pytest.warns(DeprecationWarning): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.ai_gateway.billing.spending_limit.with_raw_response.create( + account_id="", + amount=10000, + duration="monthly", + strategy="fixed", + ) @pytest.mark.skip(reason="HTTP 404 error from prism") @parametrize From 43fb66a7e770130df219304f4cf35c4d3f083d92 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 14:44:49 +0000 Subject: [PATCH 19/29] feat: feat: ES-13122 Add email-auth API endpoints * feat(email_auth): Add email-auth API endpoints --- .stats.yml | 6 +- api.md | 2 + src/cloudflare/_client.py | 38 +++ .../resources/email_auth/__init__.py | 47 +++ src/cloudflare/resources/email_auth/api.md | 28 ++ .../resources/email_auth/dmarc_reports.py | 307 ++++++++++++++++++ .../resources/email_auth/email_auth.py | 134 ++++++++ .../resources/email_auth/spf/__init__.py | 33 ++ .../resources/email_auth/spf/inspect.py | 208 ++++++++++++ .../resources/email_auth/spf/spf.py | 102 ++++++ src/cloudflare/types/email_auth/__init__.py | 7 + .../email_auth/dmarc_report_edit_params.py | 19 ++ .../email_auth/dmarc_report_edit_response.py | 251 ++++++++++++++ .../email_auth/dmarc_report_get_response.py | 251 ++++++++++++++ .../types/email_auth/spf/__init__.py | 6 + .../email_auth/spf/inspect_get_params.py | 15 + .../email_auth/spf/inspect_get_response.py | 66 ++++ tests/api_resources/email_auth/__init__.py | 1 + .../api_resources/email_auth/spf/__init__.py | 1 + .../email_auth/spf/test_inspect.py | 108 ++++++ .../email_auth/test_dmarc_reports.py | 194 +++++++++++ 21 files changed, 1821 insertions(+), 3 deletions(-) create mode 100644 src/cloudflare/resources/email_auth/__init__.py create mode 100644 src/cloudflare/resources/email_auth/api.md create mode 100644 src/cloudflare/resources/email_auth/dmarc_reports.py create mode 100644 src/cloudflare/resources/email_auth/email_auth.py create mode 100644 src/cloudflare/resources/email_auth/spf/__init__.py create mode 100644 src/cloudflare/resources/email_auth/spf/inspect.py create mode 100644 src/cloudflare/resources/email_auth/spf/spf.py create mode 100644 src/cloudflare/types/email_auth/__init__.py create mode 100644 src/cloudflare/types/email_auth/dmarc_report_edit_params.py create mode 100644 src/cloudflare/types/email_auth/dmarc_report_edit_response.py create mode 100644 src/cloudflare/types/email_auth/dmarc_report_get_response.py create mode 100644 src/cloudflare/types/email_auth/spf/__init__.py create mode 100644 src/cloudflare/types/email_auth/spf/inspect_get_params.py create mode 100644 src/cloudflare/types/email_auth/spf/inspect_get_response.py create mode 100644 tests/api_resources/email_auth/__init__.py create mode 100644 tests/api_resources/email_auth/spf/__init__.py create mode 100644 tests/api_resources/email_auth/spf/test_inspect.py create mode 100644 tests/api_resources/email_auth/test_dmarc_reports.py diff --git a/.stats.yml b/.stats.yml index 334f6ded852..eeacd7de227 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 2409 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-5e7a38305b9b369ac76d03acb28e80d6e0becfa5d830183c5d05ca3acbe57d97.yml +configured_endpoints: 2412 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-e56a17fb7ee5adefef7f21e0b58738f69ac4632480b6c66893779ec3392cd2fa.yml openapi_spec_hash: ce574d5447a6a01dde1d36709a279533 -config_hash: 517f3639f0355eae78476ce617456698 +config_hash: 0d0f8a52544b19024930eb8039e5f50a diff --git a/api.md b/api.md index 0269445d227..1abe5e33c0f 100644 --- a/api.md +++ b/api.md @@ -75,6 +75,8 @@ from cloudflare.types import ( # [EmailSecurity](src/cloudflare/resources/email_security/api.md) +# [EmailAuth](src/cloudflare/resources/email_auth/api.md) + # [EmailRouting](src/cloudflare/resources/email_routing/api.md) # [EmailSending](src/cloudflare/resources/email_sending/api.md) diff --git a/src/cloudflare/_client.py b/src/cloudflare/_client.py index b25df3317a5..51991d7bc79 100644 --- a/src/cloudflare/_client.py +++ b/src/cloudflare/_client.py @@ -89,6 +89,7 @@ addressing, ai_gateway, audit_logs, + email_auth, hyperdrive, page_rules, zero_trust, @@ -206,6 +207,7 @@ from .resources.addressing.addressing import AddressingResource, AsyncAddressingResource from .resources.ai_gateway.ai_gateway import AIGatewayResource, AsyncAIGatewayResource from .resources.audit_logs.audit_logs import AuditLogsResource, AsyncAuditLogsResource + from .resources.email_auth.email_auth import EmailAuthResource, AsyncEmailAuthResource from .resources.hyperdrive.hyperdrive import HyperdriveResource, AsyncHyperdriveResource from .resources.page_rules.page_rules import PageRulesResource, AsyncPageRulesResource from .resources.zero_trust.zero_trust import ZeroTrustResource, AsyncZeroTrustResource @@ -559,6 +561,12 @@ def email_security(self) -> EmailSecurityResource: return EmailSecurityResource(self) + @cached_property + def email_auth(self) -> EmailAuthResource: + from .resources.email_auth import EmailAuthResource + + return EmailAuthResource(self) + @cached_property def email_routing(self) -> EmailRoutingResource: from .resources.email_routing import EmailRoutingResource @@ -1517,6 +1525,12 @@ def email_security(self) -> AsyncEmailSecurityResource: return AsyncEmailSecurityResource(self) + @cached_property + def email_auth(self) -> AsyncEmailAuthResource: + from .resources.email_auth import AsyncEmailAuthResource + + return AsyncEmailAuthResource(self) + @cached_property def email_routing(self) -> AsyncEmailRoutingResource: from .resources.email_routing import AsyncEmailRoutingResource @@ -2395,6 +2409,12 @@ def email_security(self) -> email_security.EmailSecurityResourceWithRawResponse: return EmailSecurityResourceWithRawResponse(self._client.email_security) + @cached_property + def email_auth(self) -> email_auth.EmailAuthResourceWithRawResponse: + from .resources.email_auth import EmailAuthResourceWithRawResponse + + return EmailAuthResourceWithRawResponse(self._client.email_auth) + @cached_property def email_routing(self) -> email_routing.EmailRoutingResourceWithRawResponse: from .resources.email_routing import EmailRoutingResourceWithRawResponse @@ -3104,6 +3124,12 @@ def email_security(self) -> email_security.AsyncEmailSecurityResourceWithRawResp return AsyncEmailSecurityResourceWithRawResponse(self._client.email_security) + @cached_property + def email_auth(self) -> email_auth.AsyncEmailAuthResourceWithRawResponse: + from .resources.email_auth import AsyncEmailAuthResourceWithRawResponse + + return AsyncEmailAuthResourceWithRawResponse(self._client.email_auth) + @cached_property def email_routing(self) -> email_routing.AsyncEmailRoutingResourceWithRawResponse: from .resources.email_routing import AsyncEmailRoutingResourceWithRawResponse @@ -3813,6 +3839,12 @@ def email_security(self) -> email_security.EmailSecurityResourceWithStreamingRes return EmailSecurityResourceWithStreamingResponse(self._client.email_security) + @cached_property + def email_auth(self) -> email_auth.EmailAuthResourceWithStreamingResponse: + from .resources.email_auth import EmailAuthResourceWithStreamingResponse + + return EmailAuthResourceWithStreamingResponse(self._client.email_auth) + @cached_property def email_routing(self) -> email_routing.EmailRoutingResourceWithStreamingResponse: from .resources.email_routing import EmailRoutingResourceWithStreamingResponse @@ -4524,6 +4556,12 @@ def email_security(self) -> email_security.AsyncEmailSecurityResourceWithStreami return AsyncEmailSecurityResourceWithStreamingResponse(self._client.email_security) + @cached_property + def email_auth(self) -> email_auth.AsyncEmailAuthResourceWithStreamingResponse: + from .resources.email_auth import AsyncEmailAuthResourceWithStreamingResponse + + return AsyncEmailAuthResourceWithStreamingResponse(self._client.email_auth) + @cached_property def email_routing(self) -> email_routing.AsyncEmailRoutingResourceWithStreamingResponse: from .resources.email_routing import AsyncEmailRoutingResourceWithStreamingResponse diff --git a/src/cloudflare/resources/email_auth/__init__.py b/src/cloudflare/resources/email_auth/__init__.py new file mode 100644 index 00000000000..92bb375f807 --- /dev/null +++ b/src/cloudflare/resources/email_auth/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .spf import ( + SPFResource, + AsyncSPFResource, + SPFResourceWithRawResponse, + AsyncSPFResourceWithRawResponse, + SPFResourceWithStreamingResponse, + AsyncSPFResourceWithStreamingResponse, +) +from .email_auth import ( + EmailAuthResource, + AsyncEmailAuthResource, + EmailAuthResourceWithRawResponse, + AsyncEmailAuthResourceWithRawResponse, + EmailAuthResourceWithStreamingResponse, + AsyncEmailAuthResourceWithStreamingResponse, +) +from .dmarc_reports import ( + DMARCReportsResource, + AsyncDMARCReportsResource, + DMARCReportsResourceWithRawResponse, + AsyncDMARCReportsResourceWithRawResponse, + DMARCReportsResourceWithStreamingResponse, + AsyncDMARCReportsResourceWithStreamingResponse, +) + +__all__ = [ + "DMARCReportsResource", + "AsyncDMARCReportsResource", + "DMARCReportsResourceWithRawResponse", + "AsyncDMARCReportsResourceWithRawResponse", + "DMARCReportsResourceWithStreamingResponse", + "AsyncDMARCReportsResourceWithStreamingResponse", + "SPFResource", + "AsyncSPFResource", + "SPFResourceWithRawResponse", + "AsyncSPFResourceWithRawResponse", + "SPFResourceWithStreamingResponse", + "AsyncSPFResourceWithStreamingResponse", + "EmailAuthResource", + "AsyncEmailAuthResource", + "EmailAuthResourceWithRawResponse", + "AsyncEmailAuthResourceWithRawResponse", + "EmailAuthResourceWithStreamingResponse", + "AsyncEmailAuthResourceWithStreamingResponse", +] diff --git a/src/cloudflare/resources/email_auth/api.md b/src/cloudflare/resources/email_auth/api.md new file mode 100644 index 00000000000..08ac48bd434 --- /dev/null +++ b/src/cloudflare/resources/email_auth/api.md @@ -0,0 +1,28 @@ +# EmailAuth + +## DMARCReports + +Types: + +```python +from cloudflare.types.email_auth import DMARCReportEditResponse, DMARCReportGetResponse +``` + +Methods: + +- client.email_auth.dmarc_reports.edit(\*, zone_id, \*\*params) -> Optional[DMARCReportEditResponse] +- client.email_auth.dmarc_reports.get(\*, zone_id) -> Optional[DMARCReportGetResponse] + +## SPF + +### Inspect + +Types: + +```python +from cloudflare.types.email_auth.spf import InspectGetResponse +``` + +Methods: + +- client.email_auth.spf.inspect.get(\*, zone_id, \*\*params) -> Optional[InspectGetResponse] diff --git a/src/cloudflare/resources/email_auth/dmarc_reports.py b/src/cloudflare/resources/email_auth/dmarc_reports.py new file mode 100644 index 00000000000..a5cc3ae70d7 --- /dev/null +++ b/src/cloudflare/resources/email_auth/dmarc_reports.py @@ -0,0 +1,307 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Type, Optional, cast + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._wrappers import ResultWrapper +from ..._base_client import make_request_options +from ...types.email_auth import dmarc_report_edit_params +from ...types.email_auth.dmarc_report_get_response import DMARCReportGetResponse +from ...types.email_auth.dmarc_report_edit_response import DMARCReportEditResponse + +__all__ = ["DMARCReportsResource", "AsyncDMARCReportsResource"] + + +class DMARCReportsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DMARCReportsResourceWithRawResponse: + """ + 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 DMARCReportsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DMARCReportsResourceWithStreamingResponse: + """ + 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 DMARCReportsResourceWithStreamingResponse(self) + + def edit( + self, + *, + zone_id: str, + enabled: Optional[bool] | Omit = omit, + skip_wizard: Optional[bool] | Omit = omit, + # 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, + ) -> Optional[DMARCReportEditResponse]: + """Updates the DMARC report configuration for a zone. + + At least one of `enabled` or + `skip_wizard` must be provided. When enabling, the handler will ensure the DMARC + RUA record exists in DNS. + + Args: + zone_id: Identifier. + + enabled: Enable or disable DMARC reports for this zone + + skip_wizard: Skip the DMARC setup wizard + + 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._patch( + path_template("/zones/{zone_id}/email/auth/dmarc-reports", zone_id=zone_id), + body=maybe_transform( + { + "enabled": enabled, + "skip_wizard": skip_wizard, + }, + dmarc_report_edit_params.DMARCReportEditParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[DMARCReportEditResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[DMARCReportEditResponse]], ResultWrapper[DMARCReportEditResponse]), + ) + + 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, + ) -> Optional[DMARCReportGetResponse]: + """Retrieves the current DMARC report configuration and status for a zone. + + Returns + the RUA prefix, enabled status, approved sources, and DNS records. + + Args: + zone_id: Identifier. + + 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( + path_template("/zones/{zone_id}/email/auth/dmarc-reports", zone_id=zone_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[DMARCReportGetResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[DMARCReportGetResponse]], ResultWrapper[DMARCReportGetResponse]), + ) + + +class AsyncDMARCReportsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDMARCReportsResourceWithRawResponse: + """ + 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 AsyncDMARCReportsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDMARCReportsResourceWithStreamingResponse: + """ + 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 AsyncDMARCReportsResourceWithStreamingResponse(self) + + async def edit( + self, + *, + zone_id: str, + enabled: Optional[bool] | Omit = omit, + skip_wizard: Optional[bool] | Omit = omit, + # 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, + ) -> Optional[DMARCReportEditResponse]: + """Updates the DMARC report configuration for a zone. + + At least one of `enabled` or + `skip_wizard` must be provided. When enabling, the handler will ensure the DMARC + RUA record exists in DNS. + + Args: + zone_id: Identifier. + + enabled: Enable or disable DMARC reports for this zone + + skip_wizard: Skip the DMARC setup wizard + + 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._patch( + path_template("/zones/{zone_id}/email/auth/dmarc-reports", zone_id=zone_id), + body=await async_maybe_transform( + { + "enabled": enabled, + "skip_wizard": skip_wizard, + }, + dmarc_report_edit_params.DMARCReportEditParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[DMARCReportEditResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[DMARCReportEditResponse]], ResultWrapper[DMARCReportEditResponse]), + ) + + 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, + ) -> Optional[DMARCReportGetResponse]: + """Retrieves the current DMARC report configuration and status for a zone. + + Returns + the RUA prefix, enabled status, approved sources, and DNS records. + + Args: + zone_id: Identifier. + + 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( + path_template("/zones/{zone_id}/email/auth/dmarc-reports", zone_id=zone_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[Optional[DMARCReportGetResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[DMARCReportGetResponse]], ResultWrapper[DMARCReportGetResponse]), + ) + + +class DMARCReportsResourceWithRawResponse: + def __init__(self, dmarc_reports: DMARCReportsResource) -> None: + self._dmarc_reports = dmarc_reports + + self.edit = to_raw_response_wrapper( + dmarc_reports.edit, + ) + self.get = to_raw_response_wrapper( + dmarc_reports.get, + ) + + +class AsyncDMARCReportsResourceWithRawResponse: + def __init__(self, dmarc_reports: AsyncDMARCReportsResource) -> None: + self._dmarc_reports = dmarc_reports + + self.edit = async_to_raw_response_wrapper( + dmarc_reports.edit, + ) + self.get = async_to_raw_response_wrapper( + dmarc_reports.get, + ) + + +class DMARCReportsResourceWithStreamingResponse: + def __init__(self, dmarc_reports: DMARCReportsResource) -> None: + self._dmarc_reports = dmarc_reports + + self.edit = to_streamed_response_wrapper( + dmarc_reports.edit, + ) + self.get = to_streamed_response_wrapper( + dmarc_reports.get, + ) + + +class AsyncDMARCReportsResourceWithStreamingResponse: + def __init__(self, dmarc_reports: AsyncDMARCReportsResource) -> None: + self._dmarc_reports = dmarc_reports + + self.edit = async_to_streamed_response_wrapper( + dmarc_reports.edit, + ) + self.get = async_to_streamed_response_wrapper( + dmarc_reports.get, + ) diff --git a/src/cloudflare/resources/email_auth/email_auth.py b/src/cloudflare/resources/email_auth/email_auth.py new file mode 100644 index 00000000000..9a9e4be4906 --- /dev/null +++ b/src/cloudflare/resources/email_auth/email_auth.py @@ -0,0 +1,134 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .spf.spf import ( + SPFResource, + AsyncSPFResource, + SPFResourceWithRawResponse, + AsyncSPFResourceWithRawResponse, + SPFResourceWithStreamingResponse, + AsyncSPFResourceWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .dmarc_reports import ( + DMARCReportsResource, + AsyncDMARCReportsResource, + DMARCReportsResourceWithRawResponse, + AsyncDMARCReportsResourceWithRawResponse, + DMARCReportsResourceWithStreamingResponse, + AsyncDMARCReportsResourceWithStreamingResponse, +) + +__all__ = ["EmailAuthResource", "AsyncEmailAuthResource"] + + +class EmailAuthResource(SyncAPIResource): + @cached_property + def dmarc_reports(self) -> DMARCReportsResource: + return DMARCReportsResource(self._client) + + @cached_property + def spf(self) -> SPFResource: + return SPFResource(self._client) + + @cached_property + def with_raw_response(self) -> EmailAuthResourceWithRawResponse: + """ + 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 EmailAuthResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EmailAuthResourceWithStreamingResponse: + """ + 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 EmailAuthResourceWithStreamingResponse(self) + + +class AsyncEmailAuthResource(AsyncAPIResource): + @cached_property + def dmarc_reports(self) -> AsyncDMARCReportsResource: + return AsyncDMARCReportsResource(self._client) + + @cached_property + def spf(self) -> AsyncSPFResource: + return AsyncSPFResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncEmailAuthResourceWithRawResponse: + """ + 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 AsyncEmailAuthResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEmailAuthResourceWithStreamingResponse: + """ + 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 AsyncEmailAuthResourceWithStreamingResponse(self) + + +class EmailAuthResourceWithRawResponse: + def __init__(self, email_auth: EmailAuthResource) -> None: + self._email_auth = email_auth + + @cached_property + def dmarc_reports(self) -> DMARCReportsResourceWithRawResponse: + return DMARCReportsResourceWithRawResponse(self._email_auth.dmarc_reports) + + @cached_property + def spf(self) -> SPFResourceWithRawResponse: + return SPFResourceWithRawResponse(self._email_auth.spf) + + +class AsyncEmailAuthResourceWithRawResponse: + def __init__(self, email_auth: AsyncEmailAuthResource) -> None: + self._email_auth = email_auth + + @cached_property + def dmarc_reports(self) -> AsyncDMARCReportsResourceWithRawResponse: + return AsyncDMARCReportsResourceWithRawResponse(self._email_auth.dmarc_reports) + + @cached_property + def spf(self) -> AsyncSPFResourceWithRawResponse: + return AsyncSPFResourceWithRawResponse(self._email_auth.spf) + + +class EmailAuthResourceWithStreamingResponse: + def __init__(self, email_auth: EmailAuthResource) -> None: + self._email_auth = email_auth + + @cached_property + def dmarc_reports(self) -> DMARCReportsResourceWithStreamingResponse: + return DMARCReportsResourceWithStreamingResponse(self._email_auth.dmarc_reports) + + @cached_property + def spf(self) -> SPFResourceWithStreamingResponse: + return SPFResourceWithStreamingResponse(self._email_auth.spf) + + +class AsyncEmailAuthResourceWithStreamingResponse: + def __init__(self, email_auth: AsyncEmailAuthResource) -> None: + self._email_auth = email_auth + + @cached_property + def dmarc_reports(self) -> AsyncDMARCReportsResourceWithStreamingResponse: + return AsyncDMARCReportsResourceWithStreamingResponse(self._email_auth.dmarc_reports) + + @cached_property + def spf(self) -> AsyncSPFResourceWithStreamingResponse: + return AsyncSPFResourceWithStreamingResponse(self._email_auth.spf) diff --git a/src/cloudflare/resources/email_auth/spf/__init__.py b/src/cloudflare/resources/email_auth/spf/__init__.py new file mode 100644 index 00000000000..a4cb6ce4d95 --- /dev/null +++ b/src/cloudflare/resources/email_auth/spf/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .spf import ( + SPFResource, + AsyncSPFResource, + SPFResourceWithRawResponse, + AsyncSPFResourceWithRawResponse, + SPFResourceWithStreamingResponse, + AsyncSPFResourceWithStreamingResponse, +) +from .inspect import ( + InspectResource, + AsyncInspectResource, + InspectResourceWithRawResponse, + AsyncInspectResourceWithRawResponse, + InspectResourceWithStreamingResponse, + AsyncInspectResourceWithStreamingResponse, +) + +__all__ = [ + "InspectResource", + "AsyncInspectResource", + "InspectResourceWithRawResponse", + "AsyncInspectResourceWithRawResponse", + "InspectResourceWithStreamingResponse", + "AsyncInspectResourceWithStreamingResponse", + "SPFResource", + "AsyncSPFResource", + "SPFResourceWithRawResponse", + "AsyncSPFResourceWithRawResponse", + "SPFResourceWithStreamingResponse", + "AsyncSPFResourceWithStreamingResponse", +] diff --git a/src/cloudflare/resources/email_auth/spf/inspect.py b/src/cloudflare/resources/email_auth/spf/inspect.py new file mode 100644 index 00000000000..8d52113efe9 --- /dev/null +++ b/src/cloudflare/resources/email_auth/spf/inspect.py @@ -0,0 +1,208 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Type, Optional, cast + +import httpx + +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._wrappers import ResultWrapper +from ...._base_client import make_request_options +from ....types.email_auth.spf import inspect_get_params +from ....types.email_auth.spf.inspect_get_response import InspectGetResponse + +__all__ = ["InspectResource", "AsyncInspectResource"] + + +class InspectResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InspectResourceWithRawResponse: + """ + 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 InspectResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InspectResourceWithStreamingResponse: + """ + 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 InspectResourceWithStreamingResponse(self) + + def get( + self, + *, + zone_id: str, + 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, + ) -> Optional[InspectGetResponse]: + """ + Inspects a specific SPF TXT record and returns a parsed tree structure in the + spflimit-worker format. + + The record ID must be provided via the `id` query parameter. + + Returns a recursive tree showing: + + - Parsed components with their qualifiers and types + - Nested includes recursively resolved within components + - Per-component and total lookup counts + - Detailed error information with context + + Args: + zone_id: Identifier. + + id: DNS record ID (rec_tag) to inspect + + 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( + path_template("/zones/{zone_id}/email/auth/spf/inspect", zone_id=zone_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"id": id}, inspect_get_params.InspectGetParams), + post_parser=ResultWrapper[Optional[InspectGetResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[InspectGetResponse]], ResultWrapper[InspectGetResponse]), + ) + + +class AsyncInspectResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInspectResourceWithRawResponse: + """ + 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 AsyncInspectResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInspectResourceWithStreamingResponse: + """ + 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 AsyncInspectResourceWithStreamingResponse(self) + + async def get( + self, + *, + zone_id: str, + 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, + ) -> Optional[InspectGetResponse]: + """ + Inspects a specific SPF TXT record and returns a parsed tree structure in the + spflimit-worker format. + + The record ID must be provided via the `id` query parameter. + + Returns a recursive tree showing: + + - Parsed components with their qualifiers and types + - Nested includes recursively resolved within components + - Per-component and total lookup counts + - Detailed error information with context + + Args: + zone_id: Identifier. + + id: DNS record ID (rec_tag) to inspect + + 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( + path_template("/zones/{zone_id}/email/auth/spf/inspect", zone_id=zone_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"id": id}, inspect_get_params.InspectGetParams), + post_parser=ResultWrapper[Optional[InspectGetResponse]]._unwrapper, + ), + cast_to=cast(Type[Optional[InspectGetResponse]], ResultWrapper[InspectGetResponse]), + ) + + +class InspectResourceWithRawResponse: + def __init__(self, inspect: InspectResource) -> None: + self._inspect = inspect + + self.get = to_raw_response_wrapper( + inspect.get, + ) + + +class AsyncInspectResourceWithRawResponse: + def __init__(self, inspect: AsyncInspectResource) -> None: + self._inspect = inspect + + self.get = async_to_raw_response_wrapper( + inspect.get, + ) + + +class InspectResourceWithStreamingResponse: + def __init__(self, inspect: InspectResource) -> None: + self._inspect = inspect + + self.get = to_streamed_response_wrapper( + inspect.get, + ) + + +class AsyncInspectResourceWithStreamingResponse: + def __init__(self, inspect: AsyncInspectResource) -> None: + self._inspect = inspect + + self.get = async_to_streamed_response_wrapper( + inspect.get, + ) diff --git a/src/cloudflare/resources/email_auth/spf/spf.py b/src/cloudflare/resources/email_auth/spf/spf.py new file mode 100644 index 00000000000..ef37938656b --- /dev/null +++ b/src/cloudflare/resources/email_auth/spf/spf.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .inspect import ( + InspectResource, + AsyncInspectResource, + InspectResourceWithRawResponse, + AsyncInspectResourceWithRawResponse, + InspectResourceWithStreamingResponse, + AsyncInspectResourceWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["SPFResource", "AsyncSPFResource"] + + +class SPFResource(SyncAPIResource): + @cached_property + def inspect(self) -> InspectResource: + return InspectResource(self._client) + + @cached_property + def with_raw_response(self) -> SPFResourceWithRawResponse: + """ + 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 SPFResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SPFResourceWithStreamingResponse: + """ + 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 SPFResourceWithStreamingResponse(self) + + +class AsyncSPFResource(AsyncAPIResource): + @cached_property + def inspect(self) -> AsyncInspectResource: + return AsyncInspectResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncSPFResourceWithRawResponse: + """ + 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 AsyncSPFResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSPFResourceWithStreamingResponse: + """ + 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 AsyncSPFResourceWithStreamingResponse(self) + + +class SPFResourceWithRawResponse: + def __init__(self, spf: SPFResource) -> None: + self._spf = spf + + @cached_property + def inspect(self) -> InspectResourceWithRawResponse: + return InspectResourceWithRawResponse(self._spf.inspect) + + +class AsyncSPFResourceWithRawResponse: + def __init__(self, spf: AsyncSPFResource) -> None: + self._spf = spf + + @cached_property + def inspect(self) -> AsyncInspectResourceWithRawResponse: + return AsyncInspectResourceWithRawResponse(self._spf.inspect) + + +class SPFResourceWithStreamingResponse: + def __init__(self, spf: SPFResource) -> None: + self._spf = spf + + @cached_property + def inspect(self) -> InspectResourceWithStreamingResponse: + return InspectResourceWithStreamingResponse(self._spf.inspect) + + +class AsyncSPFResourceWithStreamingResponse: + def __init__(self, spf: AsyncSPFResource) -> None: + self._spf = spf + + @cached_property + def inspect(self) -> AsyncInspectResourceWithStreamingResponse: + return AsyncInspectResourceWithStreamingResponse(self._spf.inspect) diff --git a/src/cloudflare/types/email_auth/__init__.py b/src/cloudflare/types/email_auth/__init__.py new file mode 100644 index 00000000000..e0d836b6f81 --- /dev/null +++ b/src/cloudflare/types/email_auth/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .dmarc_report_edit_params import DMARCReportEditParams as DMARCReportEditParams +from .dmarc_report_get_response import DMARCReportGetResponse as DMARCReportGetResponse +from .dmarc_report_edit_response import DMARCReportEditResponse as DMARCReportEditResponse diff --git a/src/cloudflare/types/email_auth/dmarc_report_edit_params.py b/src/cloudflare/types/email_auth/dmarc_report_edit_params.py new file mode 100644 index 00000000000..4d154790632 --- /dev/null +++ b/src/cloudflare/types/email_auth/dmarc_report_edit_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["DMARCReportEditParams"] + + +class DMARCReportEditParams(TypedDict, total=False): + zone_id: Required[str] + """Identifier.""" + + enabled: Optional[bool] + """Enable or disable DMARC reports for this zone""" + + skip_wizard: Optional[bool] + """Skip the DMARC setup wizard""" diff --git a/src/cloudflare/types/email_auth/dmarc_report_edit_response.py b/src/cloudflare/types/email_auth/dmarc_report_edit_response.py new file mode 100644 index 00000000000..5e8b1598303 --- /dev/null +++ b/src/cloudflare/types/email_auth/dmarc_report_edit_response.py @@ -0,0 +1,251 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "DMARCReportEditResponse", + "ApprovedSource", + "Records", + "RecordsBimiRecord", + "RecordsCnamedkimRecord", + "RecordsCnamedmarcRecord", + "RecordsCnamespfRecord", + "RecordsDKIMRecord", + "RecordsDMARCRecord", + "RecordsSPFRecord", +] + + +class ApprovedSource(BaseModel): + """A single approved sending source""" + + created: Optional[datetime] = None + """Deprecated, use created_at""" + + created_at: Optional[datetime] = None + """Creation timestamp""" + + domain: Optional[str] = None + """The source domain""" + + ips: Optional[List[str]] = None + """Resolved IP addresses from SPF""" + + modified: Optional[datetime] = None + """Deprecated, use modified_at""" + + modified_at: Optional[datetime] = None + """Last modification timestamp""" + + name: Optional[str] = None + """Source name (typically same as domain)""" + + slug: Optional[str] = None + """URL-friendly identifier""" + + tag: Optional[str] = None + """Source UUID""" + + +class RecordsBimiRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsCnamedkimRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsCnamedmarcRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsCnamespfRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsDKIMRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsDMARCRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsSPFRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class Records(BaseModel): + """Live DNS records for the zone, grouped by type""" + + bimi_records: Optional[List[RecordsBimiRecord]] = None + """BIMI TXT records""" + + cname_dkim_records: Optional[List[RecordsCnamedkimRecord]] = None + """CNAME records for DKIM""" + + cname_dmarc_records: Optional[List[RecordsCnamedmarcRecord]] = None + """CNAME records at \\__dmarc (problematic)""" + + cname_spf_records: Optional[List[RecordsCnamespfRecord]] = None + """CNAME records for SPF""" + + dkim_records: Optional[List[RecordsDKIMRecord]] = None + """DKIM TXT records""" + + dmarc_records: Optional[List[RecordsDMARCRecord]] = None + """DMARC TXT records""" + + spf_records: Optional[List[RecordsSPFRecord]] = None + """SPF TXT records""" + + +class DMARCReportEditResponse(BaseModel): + """Response for GET/PATCH /dmarc-reports""" + + approved_sources: Optional[List[ApprovedSource]] = None + """List of approved sending sources (omitted when empty)""" + + created: Optional[datetime] = None + """Deprecated, use created_at""" + + created_at: Optional[datetime] = None + """Creation timestamp""" + + enabled: Optional[bool] = None + """Whether DMARC reports are enabled""" + + modified: Optional[datetime] = None + """Deprecated, use modified_at""" + + modified_at: Optional[datetime] = None + """Last modification timestamp""" + + records: Optional[Records] = None + """Live DNS records for the zone, grouped by type""" + + rua_prefix: Optional[str] = None + """Prefix for DMARC RUA addresses (32-char hex string)""" + + skip_wizard: Optional[bool] = None + """Whether to skip the setup wizard""" + + status: Optional[ + Literal["missing-dmarc-report", "multiple-dmarc-reports", "missing-dmarc-rua", "cname-on-dmarc-record"] + ] = None + """DMARC configuration status""" + + tag: Optional[str] = None + """Use `zone_id` instead""" + + zone_id: Optional[str] = None + """Zone identifier""" diff --git a/src/cloudflare/types/email_auth/dmarc_report_get_response.py b/src/cloudflare/types/email_auth/dmarc_report_get_response.py new file mode 100644 index 00000000000..c8457be9bbb --- /dev/null +++ b/src/cloudflare/types/email_auth/dmarc_report_get_response.py @@ -0,0 +1,251 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "DMARCReportGetResponse", + "ApprovedSource", + "Records", + "RecordsBimiRecord", + "RecordsCnamedkimRecord", + "RecordsCnamedmarcRecord", + "RecordsCnamespfRecord", + "RecordsDKIMRecord", + "RecordsDMARCRecord", + "RecordsSPFRecord", +] + + +class ApprovedSource(BaseModel): + """A single approved sending source""" + + created: Optional[datetime] = None + """Deprecated, use created_at""" + + created_at: Optional[datetime] = None + """Creation timestamp""" + + domain: Optional[str] = None + """The source domain""" + + ips: Optional[List[str]] = None + """Resolved IP addresses from SPF""" + + modified: Optional[datetime] = None + """Deprecated, use modified_at""" + + modified_at: Optional[datetime] = None + """Last modification timestamp""" + + name: Optional[str] = None + """Source name (typically same as domain)""" + + slug: Optional[str] = None + """URL-friendly identifier""" + + tag: Optional[str] = None + """Source UUID""" + + +class RecordsBimiRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsCnamedkimRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsCnamedmarcRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsCnamespfRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsDKIMRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsDMARCRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class RecordsSPFRecord(BaseModel): + """Summary of a single DNS record""" + + id: Optional[str] = None + """DNS record ID""" + + content: Optional[str] = None + """Record content""" + + name: Optional[str] = None + """DNS record name""" + + ttl: Optional[int] = None + """Time to live in seconds""" + + type: Optional[str] = None + """Record type""" + + +class Records(BaseModel): + """Live DNS records for the zone, grouped by type""" + + bimi_records: Optional[List[RecordsBimiRecord]] = None + """BIMI TXT records""" + + cname_dkim_records: Optional[List[RecordsCnamedkimRecord]] = None + """CNAME records for DKIM""" + + cname_dmarc_records: Optional[List[RecordsCnamedmarcRecord]] = None + """CNAME records at \\__dmarc (problematic)""" + + cname_spf_records: Optional[List[RecordsCnamespfRecord]] = None + """CNAME records for SPF""" + + dkim_records: Optional[List[RecordsDKIMRecord]] = None + """DKIM TXT records""" + + dmarc_records: Optional[List[RecordsDMARCRecord]] = None + """DMARC TXT records""" + + spf_records: Optional[List[RecordsSPFRecord]] = None + """SPF TXT records""" + + +class DMARCReportGetResponse(BaseModel): + """Response for GET/PATCH /dmarc-reports""" + + approved_sources: Optional[List[ApprovedSource]] = None + """List of approved sending sources (omitted when empty)""" + + created: Optional[datetime] = None + """Deprecated, use created_at""" + + created_at: Optional[datetime] = None + """Creation timestamp""" + + enabled: Optional[bool] = None + """Whether DMARC reports are enabled""" + + modified: Optional[datetime] = None + """Deprecated, use modified_at""" + + modified_at: Optional[datetime] = None + """Last modification timestamp""" + + records: Optional[Records] = None + """Live DNS records for the zone, grouped by type""" + + rua_prefix: Optional[str] = None + """Prefix for DMARC RUA addresses (32-char hex string)""" + + skip_wizard: Optional[bool] = None + """Whether to skip the setup wizard""" + + status: Optional[ + Literal["missing-dmarc-report", "multiple-dmarc-reports", "missing-dmarc-rua", "cname-on-dmarc-record"] + ] = None + """DMARC configuration status""" + + tag: Optional[str] = None + """Use `zone_id` instead""" + + zone_id: Optional[str] = None + """Zone identifier""" diff --git a/src/cloudflare/types/email_auth/spf/__init__.py b/src/cloudflare/types/email_auth/spf/__init__.py new file mode 100644 index 00000000000..bbfb6b9d3ca --- /dev/null +++ b/src/cloudflare/types/email_auth/spf/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .inspect_get_params import InspectGetParams as InspectGetParams +from .inspect_get_response import InspectGetResponse as InspectGetResponse diff --git a/src/cloudflare/types/email_auth/spf/inspect_get_params.py b/src/cloudflare/types/email_auth/spf/inspect_get_params.py new file mode 100644 index 00000000000..594a4932b37 --- /dev/null +++ b/src/cloudflare/types/email_auth/spf/inspect_get_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["InspectGetParams"] + + +class InspectGetParams(TypedDict, total=False): + zone_id: Required[str] + """Identifier.""" + + id: Required[str] + """DNS record ID (rec_tag) to inspect""" diff --git a/src/cloudflare/types/email_auth/spf/inspect_get_response.py b/src/cloudflare/types/email_auth/spf/inspect_get_response.py new file mode 100644 index 00000000000..0d743a61602 --- /dev/null +++ b/src/cloudflare/types/email_auth/spf/inspect_get_response.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ...._models import BaseModel + +__all__ = ["InspectGetResponse", "Error"] + + +class Error(BaseModel): + """An error encountered during SPF inspection""" + + code: str + """Error code. Known values: + + - `lookup_failed` — DNS TXT lookup failed + - `spf_not_found` — no SPF record found + - `invalid_spf` — record does not start with `v=spf1` + - `invalid_domain` — PSL validation failed + - `loop_detected` — include/redirect cycle detected + - `invalid_mechanism` — unrecognised or malformed mechanism + - `resource_limit_exceeded` — internal resource protection limits exceeded + (recursion depth or query budget) + - `max_lookups` — RFC 7208 10-lookup limit exceeded + """ + + domain: str + """Domain where the error occurred""" + + message: str + """Human-readable error message""" + + details: Optional[str] = None + """Additional error-specific details (optional). + + - For `invalid_domain` errors: the invalid domain string + - For `invalid_mechanism` errors: the invalid mechanism text (e.g., + "invalidmech123") + - For `loop_detected` errors: the domain that caused the loop + - For other error types: not present + """ + + +class InspectGetResponse(BaseModel): + """Recursive SPF inspection tree""" + + components: List[object] + """Parsed SPF components (mechanisms)""" + + domain: str + """Domain being inspected""" + + record: str + """Raw SPF record content""" + + total_lookups: int + """Total number of DNS lookups performed across all includes""" + + errors: Optional[List[Error]] = None + """ + All errors encountered during inspection, collected from the entire tree. This + includes errors from nested includes at any depth, providing a quick overview of + all issues without needing to traverse the nested structure. Each error includes + a `domain` field to identify where it occurred. Empty array if no errors + (omitted from JSON when empty). + """ diff --git a/tests/api_resources/email_auth/__init__.py b/tests/api_resources/email_auth/__init__.py new file mode 100644 index 00000000000..fd8019a9a1a --- /dev/null +++ b/tests/api_resources/email_auth/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/email_auth/spf/__init__.py b/tests/api_resources/email_auth/spf/__init__.py new file mode 100644 index 00000000000..fd8019a9a1a --- /dev/null +++ b/tests/api_resources/email_auth/spf/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/email_auth/spf/test_inspect.py b/tests/api_resources/email_auth/spf/test_inspect.py new file mode 100644 index 00000000000..c18654d8d3a --- /dev/null +++ b/tests/api_resources/email_auth/spf/test_inspect.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, Optional, cast + +import pytest + +from cloudflare import Cloudflare, AsyncCloudflare +from tests.utils import assert_matches_type +from cloudflare.types.email_auth.spf import InspectGetResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestInspect: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_get(self, client: Cloudflare) -> None: + inspect = client.email_auth.spf.inspect.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + id="id", + ) + assert_matches_type(Optional[InspectGetResponse], inspect, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Cloudflare) -> None: + response = client.email_auth.spf.inspect.with_raw_response.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inspect = response.parse() + assert_matches_type(Optional[InspectGetResponse], inspect, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Cloudflare) -> None: + with client.email_auth.spf.inspect.with_streaming_response.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inspect = response.parse() + assert_matches_type(Optional[InspectGetResponse], inspect, 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.email_auth.spf.inspect.with_raw_response.get( + zone_id="", + id="id", + ) + + +class TestAsyncInspect: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_get(self, async_client: AsyncCloudflare) -> None: + inspect = await async_client.email_auth.spf.inspect.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + id="id", + ) + assert_matches_type(Optional[InspectGetResponse], inspect, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None: + response = await async_client.email_auth.spf.inspect.with_raw_response.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + inspect = await response.parse() + assert_matches_type(Optional[InspectGetResponse], inspect, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None: + async with async_client.email_auth.spf.inspect.with_streaming_response.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + inspect = await response.parse() + assert_matches_type(Optional[InspectGetResponse], inspect, 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.email_auth.spf.inspect.with_raw_response.get( + zone_id="", + id="id", + ) diff --git a/tests/api_resources/email_auth/test_dmarc_reports.py b/tests/api_resources/email_auth/test_dmarc_reports.py new file mode 100644 index 00000000000..85d7655496e --- /dev/null +++ b/tests/api_resources/email_auth/test_dmarc_reports.py @@ -0,0 +1,194 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, Optional, cast + +import pytest + +from cloudflare import Cloudflare, AsyncCloudflare +from tests.utils import assert_matches_type +from cloudflare.types.email_auth import DMARCReportGetResponse, DMARCReportEditResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDMARCReports: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_edit(self, client: Cloudflare) -> None: + dmarc_report = client.email_auth.dmarc_reports.edit( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"]) + + @parametrize + def test_method_edit_with_all_params(self, client: Cloudflare) -> None: + dmarc_report = client.email_auth.dmarc_reports.edit( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + enabled=True, + skip_wizard=False, + ) + assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"]) + + @parametrize + def test_raw_response_edit(self, client: Cloudflare) -> None: + response = client.email_auth.dmarc_reports.with_raw_response.edit( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + dmarc_report = response.parse() + assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"]) + + @parametrize + def test_streaming_response_edit(self, client: Cloudflare) -> None: + with client.email_auth.dmarc_reports.with_streaming_response.edit( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + dmarc_report = response.parse() + assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_edit(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"): + client.email_auth.dmarc_reports.with_raw_response.edit( + zone_id="", + ) + + @parametrize + def test_method_get(self, client: Cloudflare) -> None: + dmarc_report = client.email_auth.dmarc_reports.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Cloudflare) -> None: + response = client.email_auth.dmarc_reports.with_raw_response.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + dmarc_report = response.parse() + assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Cloudflare) -> None: + with client.email_auth.dmarc_reports.with_streaming_response.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + dmarc_report = response.parse() + assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, 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.email_auth.dmarc_reports.with_raw_response.get( + zone_id="", + ) + + +class TestAsyncDMARCReports: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_edit(self, async_client: AsyncCloudflare) -> None: + dmarc_report = await async_client.email_auth.dmarc_reports.edit( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"]) + + @parametrize + async def test_method_edit_with_all_params(self, async_client: AsyncCloudflare) -> None: + dmarc_report = await async_client.email_auth.dmarc_reports.edit( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + enabled=True, + skip_wizard=False, + ) + assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"]) + + @parametrize + async def test_raw_response_edit(self, async_client: AsyncCloudflare) -> None: + response = await async_client.email_auth.dmarc_reports.with_raw_response.edit( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + dmarc_report = await response.parse() + assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"]) + + @parametrize + async def test_streaming_response_edit(self, async_client: AsyncCloudflare) -> None: + async with async_client.email_auth.dmarc_reports.with_streaming_response.edit( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + dmarc_report = await response.parse() + assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_edit(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"): + await async_client.email_auth.dmarc_reports.with_raw_response.edit( + zone_id="", + ) + + @parametrize + async def test_method_get(self, async_client: AsyncCloudflare) -> None: + dmarc_report = await async_client.email_auth.dmarc_reports.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None: + response = await async_client.email_auth.dmarc_reports.with_raw_response.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + dmarc_report = await response.parse() + assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None: + async with async_client.email_auth.dmarc_reports.with_streaming_response.get( + zone_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + dmarc_report = await response.parse() + assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, 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.email_auth.dmarc_reports.with_raw_response.get( + zone_id="", + ) From 03377088e81f4d336db3d2a2823c4c0acfa42b02 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 15:27:24 +0000 Subject: [PATCH 20/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index eeacd7de227..67bc894f95f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2412 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-e56a17fb7ee5adefef7f21e0b58738f69ac4632480b6c66893779ec3392cd2fa.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-ee348bae3bc19d58249c0a0334ea1be47869fd3c73ea0cbbf505f185acc85393.yml openapi_spec_hash: ce574d5447a6a01dde1d36709a279533 -config_hash: 0d0f8a52544b19024930eb8039e5f50a +config_hash: a23f4c71ccd6c6117c24f63ae415dcf7 From f70dfa8bdd71f9cda838256ce91f7ccab62b3e49 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 22:35:38 +0000 Subject: [PATCH 21/29] chore(api): update composite API spec --- .stats.yml | 4 +-- .../resources/aisearch/instances/instances.py | 4 +++ .../namespaces/instances/instances.py | 4 +++ .../access/ai_controls/mcp/servers.py | 24 ++++++------- src/cloudflare/resources/zero_trust/api.md | 2 +- .../zero_trust/devices/ip_profiles.py | 32 +++++++++++++---- .../devices/policies/custom/custom.py | 28 +++++++++++++++ .../devices/policies/default/default.py | 14 ++++++++ .../types/aisearch/instance_create_params.py | 14 +------- .../aisearch/instance_create_response.py | 14 +------- .../aisearch/instance_delete_response.py | 14 +------- .../types/aisearch/instance_list_response.py | 14 +------- .../types/aisearch/instance_read_response.py | 14 +------- .../types/aisearch/instance_update_params.py | 16 ++------- .../aisearch/instance_update_response.py | 14 +------- .../namespaces/instance_create_params.py | 14 +------- .../namespaces/instance_create_response.py | 14 +------- .../namespaces/instance_delete_response.py | 14 +------- .../namespaces/instance_list_response.py | 14 +------- .../namespaces/instance_read_response.py | 14 +------- .../namespaces/instance_update_params.py | 16 ++------- .../namespaces/instance_update_response.py | 14 +------- .../ai_controls/mcp/portal_create_response.py | 6 ++-- .../ai_controls/mcp/portal_list_response.py | 6 ++-- .../ai_controls/mcp/portal_read_response.py | 6 ++-- .../ai_controls/mcp/portal_update_response.py | 6 ++-- .../ai_controls/mcp/server_create_params.py | 6 ++-- .../ai_controls/mcp/server_create_response.py | 6 ++-- .../ai_controls/mcp/server_delete_response.py | 6 ++-- .../ai_controls/mcp/server_list_response.py | 6 ++-- .../ai_controls/mcp/server_read_response.py | 6 ++-- .../ai_controls/mcp/server_update_params.py | 6 ++-- .../ai_controls/mcp/server_update_response.py | 6 ++-- .../devices/ip_profile_list_params.py | 3 ++ .../devices/policies/custom_create_params.py | 36 ++++++++++++++++++- .../devices/policies/custom_edit_params.py | 36 ++++++++++++++++++- .../devices/policies/default_edit_params.py | 36 ++++++++++++++++++- .../devices/policies/default_edit_response.py | 36 ++++++++++++++++++- .../devices/policies/default_get_response.py | 36 ++++++++++++++++++- .../zero_trust/devices/settings_policy.py | 36 ++++++++++++++++++- .../aisearch/namespaces/test_instances.py | 22 ++---------- .../api_resources/aisearch/test_instances.py | 22 ++---------- .../devices/policies/test_custom.py | 24 +++++++++++++ .../devices/policies/test_default.py | 12 +++++++ .../zero_trust/devices/test_ip_profiles.py | 20 ++++++----- 45 files changed, 405 insertions(+), 292 deletions(-) diff --git a/.stats.yml b/.stats.yml index 67bc894f95f..e37516681b4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2412 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-ee348bae3bc19d58249c0a0334ea1be47869fd3c73ea0cbbf505f185acc85393.yml -openapi_spec_hash: ce574d5447a6a01dde1d36709a279533 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-9cadb6f47eb3d145352d0e7112605e553321aa88991fc55ba6efbe665bb2b52f.yml +openapi_spec_hash: 653808896077b2132c43a0a8a383679c config_hash: a23f4c71ccd6c6117c24f63ae415dcf7 diff --git a/src/cloudflare/resources/aisearch/instances/instances.py b/src/cloudflare/resources/aisearch/instances/instances.py index 58da83de22e..2dc3bd32b12 100644 --- a/src/cloudflare/resources/aisearch/instances/instances.py +++ b/src/cloudflare/resources/aisearch/instances/instances.py @@ -378,6 +378,7 @@ def update( | Omit = omit, rewrite_query: bool | Omit = omit, score_threshold: float | Omit = omit, + source: Optional[str] | Omit = omit, source_params: Optional[instance_update_params.SourceParams] | Omit = omit, summarization: bool | Omit = omit, summarization_model: Optional[ @@ -481,6 +482,7 @@ def update( "rewrite_model": rewrite_model, "rewrite_query": rewrite_query, "score_threshold": score_threshold, + "source": source, "source_params": source_params, "summarization": summarization, "summarization_model": summarization_model, @@ -1165,6 +1167,7 @@ async def update( | Omit = omit, rewrite_query: bool | Omit = omit, score_threshold: float | Omit = omit, + source: Optional[str] | Omit = omit, source_params: Optional[instance_update_params.SourceParams] | Omit = omit, summarization: bool | Omit = omit, summarization_model: Optional[ @@ -1268,6 +1271,7 @@ async def update( "rewrite_model": rewrite_model, "rewrite_query": rewrite_query, "score_threshold": score_threshold, + "source": source, "source_params": source_params, "summarization": summarization, "summarization_model": summarization_model, diff --git a/src/cloudflare/resources/aisearch/namespaces/instances/instances.py b/src/cloudflare/resources/aisearch/namespaces/instances/instances.py index c0859761db9..cff0f819bdf 100644 --- a/src/cloudflare/resources/aisearch/namespaces/instances/instances.py +++ b/src/cloudflare/resources/aisearch/namespaces/instances/instances.py @@ -396,6 +396,7 @@ def update( | Omit = omit, rewrite_query: bool | Omit = omit, score_threshold: float | Omit = omit, + source: Optional[str] | Omit = omit, source_params: Optional[instance_update_params.SourceParams] | Omit = omit, summarization: bool | Omit = omit, summarization_model: Optional[ @@ -506,6 +507,7 @@ def update( "rewrite_model": rewrite_model, "rewrite_query": rewrite_query, "score_threshold": score_threshold, + "source": source, "source_params": source_params, "summarization": summarization, "summarization_model": summarization_model, @@ -1243,6 +1245,7 @@ async def update( | Omit = omit, rewrite_query: bool | Omit = omit, score_threshold: float | Omit = omit, + source: Optional[str] | Omit = omit, source_params: Optional[instance_update_params.SourceParams] | Omit = omit, summarization: bool | Omit = omit, summarization_model: Optional[ @@ -1353,6 +1356,7 @@ async def update( "rewrite_model": rewrite_model, "rewrite_query": rewrite_query, "score_threshold": score_threshold, + "source": source, "source_params": source_params, "summarization": summarization, "summarization_model": summarization_model, diff --git a/src/cloudflare/resources/zero_trust/access/ai_controls/mcp/servers.py b/src/cloudflare/resources/zero_trust/access/ai_controls/mcp/servers.py index a8333e30d00..1f349da1562 100644 --- a/src/cloudflare/resources/zero_trust/access/ai_controls/mcp/servers.py +++ b/src/cloudflare/resources/zero_trust/access/ai_controls/mcp/servers.py @@ -80,9 +80,9 @@ def create( is_shared_oauth_callback_enabled: When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. secure_web_gateway: Route outbound traffic to this MCP server through Zero Trust Secure Web Gateway @@ -150,9 +150,9 @@ def update( is_shared_oauth_callback_enabled: When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. secure_web_gateway: Route outbound traffic to this MCP server through Zero Trust Secure Web Gateway @@ -421,9 +421,9 @@ async def create( is_shared_oauth_callback_enabled: When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. secure_web_gateway: Route outbound traffic to this MCP server through Zero Trust Secure Web Gateway @@ -491,9 +491,9 @@ async def update( is_shared_oauth_callback_enabled: When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. secure_web_gateway: Route outbound traffic to this MCP server through Zero Trust Secure Web Gateway diff --git a/src/cloudflare/resources/zero_trust/api.md b/src/cloudflare/resources/zero_trust/api.md index e635ba306be..0d6ba9bc839 100644 --- a/src/cloudflare/resources/zero_trust/api.md +++ b/src/cloudflare/resources/zero_trust/api.md @@ -99,7 +99,7 @@ Methods: - client.zero_trust.devices.ip_profiles.create(\*, account_id, \*\*params) -> IPProfile - client.zero_trust.devices.ip_profiles.update(profile_id, \*, account_id, \*\*params) -> IPProfile -- client.zero_trust.devices.ip_profiles.list(\*, account_id, \*\*params) -> SyncSinglePage[IPProfile] +- client.zero_trust.devices.ip_profiles.list(\*, account_id, \*\*params) -> SyncV4PagePaginationArray[IPProfile] - client.zero_trust.devices.ip_profiles.delete(profile_id, \*, account_id) -> IPProfileDeleteResponse - client.zero_trust.devices.ip_profiles.get(profile_id, \*, account_id) -> IPProfile diff --git a/src/cloudflare/resources/zero_trust/devices/ip_profiles.py b/src/cloudflare/resources/zero_trust/devices/ip_profiles.py index f44ec908fba..ead1f4b6d30 100644 --- a/src/cloudflare/resources/zero_trust/devices/ip_profiles.py +++ b/src/cloudflare/resources/zero_trust/devices/ip_profiles.py @@ -17,7 +17,7 @@ async_to_streamed_response_wrapper, ) from ...._wrappers import ResultWrapper -from ....pagination import SyncSinglePage, AsyncSinglePage +from ....pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray from ...._base_client import AsyncPaginator, make_request_options from ....types.zero_trust.devices import ip_profile_list_params, ip_profile_create_params, ip_profile_update_params from ....types.zero_trust.devices.ip_profile import IPProfile @@ -201,6 +201,7 @@ def list( self, *, account_id: str, + page: int | Omit = omit, per_page: int | Omit = omit, # 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. @@ -208,11 +209,13 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncSinglePage[IPProfile]: + ) -> SyncV4PagePaginationArray[IPProfile]: """ Lists WARP Device IP profiles. Args: + page: The page number to return. + per_page: The number of IP profiles to return per page. extra_headers: Send extra headers @@ -227,13 +230,19 @@ def list( raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") return self._get_api_list( path_template("/accounts/{account_id}/devices/ip-profiles", account_id=account_id), - page=SyncSinglePage[IPProfile], + page=SyncV4PagePaginationArray[IPProfile], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"per_page": per_page}, ip_profile_list_params.IPProfileListParams), + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + ip_profile_list_params.IPProfileListParams, + ), ), model=IPProfile, ) @@ -498,6 +507,7 @@ def list( self, *, account_id: str, + page: int | Omit = omit, per_page: int | Omit = omit, # 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. @@ -505,11 +515,13 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[IPProfile, AsyncSinglePage[IPProfile]]: + ) -> AsyncPaginator[IPProfile, AsyncV4PagePaginationArray[IPProfile]]: """ Lists WARP Device IP profiles. Args: + page: The page number to return. + per_page: The number of IP profiles to return per page. extra_headers: Send extra headers @@ -524,13 +536,19 @@ def list( raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") return self._get_api_list( path_template("/accounts/{account_id}/devices/ip-profiles", account_id=account_id), - page=AsyncSinglePage[IPProfile], + page=AsyncV4PagePaginationArray[IPProfile], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"per_page": per_page}, ip_profile_list_params.IPProfileListParams), + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + ip_profile_list_params.IPProfileListParams, + ), ), model=IPProfile, ) diff --git a/src/cloudflare/resources/zero_trust/devices/policies/custom/custom.py b/src/cloudflare/resources/zero_trust/devices/policies/custom/custom.py index f0b1ab7688d..c4557cef51a 100644 --- a/src/cloudflare/resources/zero_trust/devices/policies/custom/custom.py +++ b/src/cloudflare/resources/zero_trust/devices/policies/custom/custom.py @@ -101,6 +101,7 @@ def create( enabled: bool | Omit = omit, exclude: Iterable[SplitTunnelExcludeParam] | Omit = omit, exclude_office_ips: bool | Omit = omit, + global_acceleration: Optional[custom_create_params.GlobalAcceleration] | Omit = omit, include: Iterable[SplitTunnelIncludeParam] | Omit = omit, lan_allow_minutes: float | Omit = omit, lan_allow_subnet_size: float | Omit = omit, @@ -160,6 +161,11 @@ def create( exclude_office_ips: Whether to add Microsoft IPs to Split Tunnel exclusions. + global_acceleration: Global Acceleration settings for China. When configured, WARP clients connect to + the Global Accelerator addresses instead of the default ones. Please contact + your account representative to enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + include: List of routes included in the WARP client's tunnel. Both 'exclude' and 'include' cannot be set in the same request. @@ -213,6 +219,7 @@ def create( "enabled": enabled, "exclude": exclude, "exclude_office_ips": exclude_office_ips, + "global_acceleration": global_acceleration, "include": include, "lan_allow_minutes": lan_allow_minutes, "lan_allow_subnet_size": lan_allow_subnet_size, @@ -327,6 +334,7 @@ def edit( enabled: bool | Omit = omit, exclude: Iterable[SplitTunnelExcludeParam] | Omit = omit, exclude_office_ips: bool | Omit = omit, + global_acceleration: Optional[custom_edit_params.GlobalAcceleration] | Omit = omit, include: Iterable[SplitTunnelIncludeParam] | Omit = omit, lan_allow_minutes: float | Omit = omit, lan_allow_subnet_size: float | Omit = omit, @@ -378,6 +386,11 @@ def edit( exclude_office_ips: Whether to add Microsoft IPs to Split Tunnel exclusions. + global_acceleration: Global Acceleration settings for China. When configured, WARP clients connect to + the Global Accelerator addresses instead of the default ones. Please contact + your account representative to enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + include: List of routes included in the WARP client's tunnel. Both 'exclude' and 'include' cannot be set in the same request. @@ -442,6 +455,7 @@ def edit( "enabled": enabled, "exclude": exclude, "exclude_office_ips": exclude_office_ips, + "global_acceleration": global_acceleration, "include": include, "lan_allow_minutes": lan_allow_minutes, "lan_allow_subnet_size": lan_allow_subnet_size, @@ -561,6 +575,7 @@ async def create( enabled: bool | Omit = omit, exclude: Iterable[SplitTunnelExcludeParam] | Omit = omit, exclude_office_ips: bool | Omit = omit, + global_acceleration: Optional[custom_create_params.GlobalAcceleration] | Omit = omit, include: Iterable[SplitTunnelIncludeParam] | Omit = omit, lan_allow_minutes: float | Omit = omit, lan_allow_subnet_size: float | Omit = omit, @@ -620,6 +635,11 @@ async def create( exclude_office_ips: Whether to add Microsoft IPs to Split Tunnel exclusions. + global_acceleration: Global Acceleration settings for China. When configured, WARP clients connect to + the Global Accelerator addresses instead of the default ones. Please contact + your account representative to enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + include: List of routes included in the WARP client's tunnel. Both 'exclude' and 'include' cannot be set in the same request. @@ -673,6 +693,7 @@ async def create( "enabled": enabled, "exclude": exclude, "exclude_office_ips": exclude_office_ips, + "global_acceleration": global_acceleration, "include": include, "lan_allow_minutes": lan_allow_minutes, "lan_allow_subnet_size": lan_allow_subnet_size, @@ -787,6 +808,7 @@ async def edit( enabled: bool | Omit = omit, exclude: Iterable[SplitTunnelExcludeParam] | Omit = omit, exclude_office_ips: bool | Omit = omit, + global_acceleration: Optional[custom_edit_params.GlobalAcceleration] | Omit = omit, include: Iterable[SplitTunnelIncludeParam] | Omit = omit, lan_allow_minutes: float | Omit = omit, lan_allow_subnet_size: float | Omit = omit, @@ -838,6 +860,11 @@ async def edit( exclude_office_ips: Whether to add Microsoft IPs to Split Tunnel exclusions. + global_acceleration: Global Acceleration settings for China. When configured, WARP clients connect to + the Global Accelerator addresses instead of the default ones. Please contact + your account representative to enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + include: List of routes included in the WARP client's tunnel. Both 'exclude' and 'include' cannot be set in the same request. @@ -902,6 +929,7 @@ async def edit( "enabled": enabled, "exclude": exclude, "exclude_office_ips": exclude_office_ips, + "global_acceleration": global_acceleration, "include": include, "lan_allow_minutes": lan_allow_minutes, "lan_allow_subnet_size": lan_allow_subnet_size, diff --git a/src/cloudflare/resources/zero_trust/devices/policies/default/default.py b/src/cloudflare/resources/zero_trust/devices/policies/default/default.py index 4a0c60aa0e2..1dc49161425 100644 --- a/src/cloudflare/resources/zero_trust/devices/policies/default/default.py +++ b/src/cloudflare/resources/zero_trust/devices/policies/default/default.py @@ -108,6 +108,7 @@ def edit( dns_search_suffixes: Iterable[default_edit_params.DNSSearchSuffix] | Omit = omit, exclude: Iterable[SplitTunnelExcludeParam] | Omit = omit, exclude_office_ips: bool | Omit = omit, + global_acceleration: Optional[default_edit_params.GlobalAcceleration] | Omit = omit, include: Iterable[SplitTunnelIncludeParam] | Omit = omit, lan_allow_minutes: float | Omit = omit, lan_allow_subnet_size: float | Omit = omit, @@ -152,6 +153,11 @@ def edit( exclude_office_ips: Whether to add Microsoft IPs to Split Tunnel exclusions. + global_acceleration: Global Acceleration settings for China. When configured, WARP clients connect to + the Global Accelerator addresses instead of the default ones. Please contact + your account representative to enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + include: List of routes included in the WARP client's tunnel. Both 'exclude' and 'include' cannot be set in the same request. @@ -200,6 +206,7 @@ def edit( "dns_search_suffixes": dns_search_suffixes, "exclude": exclude, "exclude_office_ips": exclude_office_ips, + "global_acceleration": global_acceleration, "include": include, "lan_allow_minutes": lan_allow_minutes, "lan_allow_subnet_size": lan_allow_subnet_size, @@ -310,6 +317,7 @@ async def edit( dns_search_suffixes: Iterable[default_edit_params.DNSSearchSuffix] | Omit = omit, exclude: Iterable[SplitTunnelExcludeParam] | Omit = omit, exclude_office_ips: bool | Omit = omit, + global_acceleration: Optional[default_edit_params.GlobalAcceleration] | Omit = omit, include: Iterable[SplitTunnelIncludeParam] | Omit = omit, lan_allow_minutes: float | Omit = omit, lan_allow_subnet_size: float | Omit = omit, @@ -354,6 +362,11 @@ async def edit( exclude_office_ips: Whether to add Microsoft IPs to Split Tunnel exclusions. + global_acceleration: Global Acceleration settings for China. When configured, WARP clients connect to + the Global Accelerator addresses instead of the default ones. Please contact + your account representative to enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + include: List of routes included in the WARP client's tunnel. Both 'exclude' and 'include' cannot be set in the same request. @@ -402,6 +415,7 @@ async def edit( "dns_search_suffixes": dns_search_suffixes, "exclude": exclude, "exclude_office_ips": exclude_office_ips, + "global_acceleration": global_acceleration, "include": include, "lan_allow_minutes": lan_allow_minutes, "lan_allow_subnet_size": lan_allow_subnet_size, diff --git a/src/cloudflare/types/aisearch/instance_create_params.py b/src/cloudflare/types/aisearch/instance_create_params.py index be79caf516a..d82f1458011 100644 --- a/src/cloudflare/types/aisearch/instance_create_params.py +++ b/src/cloudflare/types/aisearch/instance_create_params.py @@ -7,7 +7,6 @@ from ..._types import SequenceNotStr from ..._utils import PropertyInfo -from ..r2.buckets.provider import Provider __all__ = [ "InstanceCreateParams", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -345,20 +343,10 @@ class SourceParamsWebCrawlerParseOptions(TypedDict, total=False): use_browser_rendering: bool -class SourceParamsWebCrawlerStoreOptions(TypedDict, total=False): - storage_id: Required[str] - - r2_jurisdiction: str - - storage_type: Provider - - class SourceParamsWebCrawler(TypedDict, total=False): parse_options: SourceParamsWebCrawlerParseOptions - parse_type: Literal["sitemap", "feed-rss", "crawl"] - - store_options: SourceParamsWebCrawlerStoreOptions + parse_type: Literal["sitemap", "crawl"] class SourceParams(TypedDict, total=False): diff --git a/src/cloudflare/types/aisearch/instance_create_response.py b/src/cloudflare/types/aisearch/instance_create_response.py index 37a4b5b463c..0d07ae548ef 100644 --- a/src/cloudflare/types/aisearch/instance_create_response.py +++ b/src/cloudflare/types/aisearch/instance_create_response.py @@ -7,7 +7,6 @@ from pydantic import Field as FieldInfo from ..._models import BaseModel -from ..r2.buckets.provider import Provider __all__ = [ "InstanceCreateResponse", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -186,20 +184,10 @@ class SourceParamsWebCrawlerParseOptions(BaseModel): use_browser_rendering: Optional[bool] = None -class SourceParamsWebCrawlerStoreOptions(BaseModel): - storage_id: str - - r2_jurisdiction: Optional[str] = None - - storage_type: Optional[Provider] = None - - class SourceParamsWebCrawler(BaseModel): parse_options: Optional[SourceParamsWebCrawlerParseOptions] = None - parse_type: Optional[Literal["sitemap", "feed-rss", "crawl"]] = None - - store_options: Optional[SourceParamsWebCrawlerStoreOptions] = None + parse_type: Optional[Literal["sitemap", "crawl"]] = None class SourceParams(BaseModel): diff --git a/src/cloudflare/types/aisearch/instance_delete_response.py b/src/cloudflare/types/aisearch/instance_delete_response.py index 6d7604dac1f..0665900ea11 100644 --- a/src/cloudflare/types/aisearch/instance_delete_response.py +++ b/src/cloudflare/types/aisearch/instance_delete_response.py @@ -7,7 +7,6 @@ from pydantic import Field as FieldInfo from ..._models import BaseModel -from ..r2.buckets.provider import Provider __all__ = [ "InstanceDeleteResponse", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -186,20 +184,10 @@ class SourceParamsWebCrawlerParseOptions(BaseModel): use_browser_rendering: Optional[bool] = None -class SourceParamsWebCrawlerStoreOptions(BaseModel): - storage_id: str - - r2_jurisdiction: Optional[str] = None - - storage_type: Optional[Provider] = None - - class SourceParamsWebCrawler(BaseModel): parse_options: Optional[SourceParamsWebCrawlerParseOptions] = None - parse_type: Optional[Literal["sitemap", "feed-rss", "crawl"]] = None - - store_options: Optional[SourceParamsWebCrawlerStoreOptions] = None + parse_type: Optional[Literal["sitemap", "crawl"]] = None class SourceParams(BaseModel): diff --git a/src/cloudflare/types/aisearch/instance_list_response.py b/src/cloudflare/types/aisearch/instance_list_response.py index b20a20234d0..11514e4d5f2 100644 --- a/src/cloudflare/types/aisearch/instance_list_response.py +++ b/src/cloudflare/types/aisearch/instance_list_response.py @@ -7,7 +7,6 @@ from pydantic import Field as FieldInfo from ..._models import BaseModel -from ..r2.buckets.provider import Provider __all__ = [ "InstanceListResponse", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -186,20 +184,10 @@ class SourceParamsWebCrawlerParseOptions(BaseModel): use_browser_rendering: Optional[bool] = None -class SourceParamsWebCrawlerStoreOptions(BaseModel): - storage_id: str - - r2_jurisdiction: Optional[str] = None - - storage_type: Optional[Provider] = None - - class SourceParamsWebCrawler(BaseModel): parse_options: Optional[SourceParamsWebCrawlerParseOptions] = None - parse_type: Optional[Literal["sitemap", "feed-rss", "crawl"]] = None - - store_options: Optional[SourceParamsWebCrawlerStoreOptions] = None + parse_type: Optional[Literal["sitemap", "crawl"]] = None class SourceParams(BaseModel): diff --git a/src/cloudflare/types/aisearch/instance_read_response.py b/src/cloudflare/types/aisearch/instance_read_response.py index 4a6f5acc7f5..ef2df1ba76c 100644 --- a/src/cloudflare/types/aisearch/instance_read_response.py +++ b/src/cloudflare/types/aisearch/instance_read_response.py @@ -7,7 +7,6 @@ from pydantic import Field as FieldInfo from ..._models import BaseModel -from ..r2.buckets.provider import Provider __all__ = [ "InstanceReadResponse", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -186,20 +184,10 @@ class SourceParamsWebCrawlerParseOptions(BaseModel): use_browser_rendering: Optional[bool] = None -class SourceParamsWebCrawlerStoreOptions(BaseModel): - storage_id: str - - r2_jurisdiction: Optional[str] = None - - storage_type: Optional[Provider] = None - - class SourceParamsWebCrawler(BaseModel): parse_options: Optional[SourceParamsWebCrawlerParseOptions] = None - parse_type: Optional[Literal["sitemap", "feed-rss", "crawl"]] = None - - store_options: Optional[SourceParamsWebCrawlerStoreOptions] = None + parse_type: Optional[Literal["sitemap", "crawl"]] = None class SourceParams(BaseModel): diff --git a/src/cloudflare/types/aisearch/instance_update_params.py b/src/cloudflare/types/aisearch/instance_update_params.py index 8a18d50c332..2c245ab4d23 100644 --- a/src/cloudflare/types/aisearch/instance_update_params.py +++ b/src/cloudflare/types/aisearch/instance_update_params.py @@ -7,7 +7,6 @@ from ..._types import SequenceNotStr from ..._utils import PropertyInfo -from ..r2.buckets.provider import Provider __all__ = [ "InstanceUpdateParams", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -169,6 +167,8 @@ class InstanceUpdateParams(TypedDict, total=False): score_threshold: float + source: Optional[str] + source_params: Optional[SourceParams] summarization: bool @@ -380,20 +380,10 @@ class SourceParamsWebCrawlerParseOptions(TypedDict, total=False): use_browser_rendering: bool -class SourceParamsWebCrawlerStoreOptions(TypedDict, total=False): - storage_id: Required[str] - - r2_jurisdiction: str - - storage_type: Provider - - class SourceParamsWebCrawler(TypedDict, total=False): parse_options: SourceParamsWebCrawlerParseOptions - parse_type: Literal["sitemap", "feed-rss", "crawl"] - - store_options: SourceParamsWebCrawlerStoreOptions + parse_type: Literal["sitemap", "crawl"] class SourceParams(TypedDict, total=False): diff --git a/src/cloudflare/types/aisearch/instance_update_response.py b/src/cloudflare/types/aisearch/instance_update_response.py index 7df0d7a354d..e7419fe448b 100644 --- a/src/cloudflare/types/aisearch/instance_update_response.py +++ b/src/cloudflare/types/aisearch/instance_update_response.py @@ -7,7 +7,6 @@ from pydantic import Field as FieldInfo from ..._models import BaseModel -from ..r2.buckets.provider import Provider __all__ = [ "InstanceUpdateResponse", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -186,20 +184,10 @@ class SourceParamsWebCrawlerParseOptions(BaseModel): use_browser_rendering: Optional[bool] = None -class SourceParamsWebCrawlerStoreOptions(BaseModel): - storage_id: str - - r2_jurisdiction: Optional[str] = None - - storage_type: Optional[Provider] = None - - class SourceParamsWebCrawler(BaseModel): parse_options: Optional[SourceParamsWebCrawlerParseOptions] = None - parse_type: Optional[Literal["sitemap", "feed-rss", "crawl"]] = None - - store_options: Optional[SourceParamsWebCrawlerStoreOptions] = None + parse_type: Optional[Literal["sitemap", "crawl"]] = None class SourceParams(BaseModel): diff --git a/src/cloudflare/types/aisearch/namespaces/instance_create_params.py b/src/cloudflare/types/aisearch/namespaces/instance_create_params.py index 77e564862d3..5741edf6ff8 100644 --- a/src/cloudflare/types/aisearch/namespaces/instance_create_params.py +++ b/src/cloudflare/types/aisearch/namespaces/instance_create_params.py @@ -7,7 +7,6 @@ from ...._types import SequenceNotStr from ...._utils import PropertyInfo -from ...r2.buckets.provider import Provider __all__ = [ "InstanceCreateParams", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -345,20 +343,10 @@ class SourceParamsWebCrawlerParseOptions(TypedDict, total=False): use_browser_rendering: bool -class SourceParamsWebCrawlerStoreOptions(TypedDict, total=False): - storage_id: Required[str] - - r2_jurisdiction: str - - storage_type: Provider - - class SourceParamsWebCrawler(TypedDict, total=False): parse_options: SourceParamsWebCrawlerParseOptions - parse_type: Literal["sitemap", "feed-rss", "crawl"] - - store_options: SourceParamsWebCrawlerStoreOptions + parse_type: Literal["sitemap", "crawl"] class SourceParams(TypedDict, total=False): diff --git a/src/cloudflare/types/aisearch/namespaces/instance_create_response.py b/src/cloudflare/types/aisearch/namespaces/instance_create_response.py index 964180501b5..a9214810536 100644 --- a/src/cloudflare/types/aisearch/namespaces/instance_create_response.py +++ b/src/cloudflare/types/aisearch/namespaces/instance_create_response.py @@ -7,7 +7,6 @@ from pydantic import Field as FieldInfo from ...._models import BaseModel -from ...r2.buckets.provider import Provider __all__ = [ "InstanceCreateResponse", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -186,20 +184,10 @@ class SourceParamsWebCrawlerParseOptions(BaseModel): use_browser_rendering: Optional[bool] = None -class SourceParamsWebCrawlerStoreOptions(BaseModel): - storage_id: str - - r2_jurisdiction: Optional[str] = None - - storage_type: Optional[Provider] = None - - class SourceParamsWebCrawler(BaseModel): parse_options: Optional[SourceParamsWebCrawlerParseOptions] = None - parse_type: Optional[Literal["sitemap", "feed-rss", "crawl"]] = None - - store_options: Optional[SourceParamsWebCrawlerStoreOptions] = None + parse_type: Optional[Literal["sitemap", "crawl"]] = None class SourceParams(BaseModel): diff --git a/src/cloudflare/types/aisearch/namespaces/instance_delete_response.py b/src/cloudflare/types/aisearch/namespaces/instance_delete_response.py index a2b6149ee80..8cda88a8bd3 100644 --- a/src/cloudflare/types/aisearch/namespaces/instance_delete_response.py +++ b/src/cloudflare/types/aisearch/namespaces/instance_delete_response.py @@ -7,7 +7,6 @@ from pydantic import Field as FieldInfo from ...._models import BaseModel -from ...r2.buckets.provider import Provider __all__ = [ "InstanceDeleteResponse", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -186,20 +184,10 @@ class SourceParamsWebCrawlerParseOptions(BaseModel): use_browser_rendering: Optional[bool] = None -class SourceParamsWebCrawlerStoreOptions(BaseModel): - storage_id: str - - r2_jurisdiction: Optional[str] = None - - storage_type: Optional[Provider] = None - - class SourceParamsWebCrawler(BaseModel): parse_options: Optional[SourceParamsWebCrawlerParseOptions] = None - parse_type: Optional[Literal["sitemap", "feed-rss", "crawl"]] = None - - store_options: Optional[SourceParamsWebCrawlerStoreOptions] = None + parse_type: Optional[Literal["sitemap", "crawl"]] = None class SourceParams(BaseModel): diff --git a/src/cloudflare/types/aisearch/namespaces/instance_list_response.py b/src/cloudflare/types/aisearch/namespaces/instance_list_response.py index 3b8e7ddf359..51d8335a2ef 100644 --- a/src/cloudflare/types/aisearch/namespaces/instance_list_response.py +++ b/src/cloudflare/types/aisearch/namespaces/instance_list_response.py @@ -7,7 +7,6 @@ from pydantic import Field as FieldInfo from ...._models import BaseModel -from ...r2.buckets.provider import Provider __all__ = [ "InstanceListResponse", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -186,20 +184,10 @@ class SourceParamsWebCrawlerParseOptions(BaseModel): use_browser_rendering: Optional[bool] = None -class SourceParamsWebCrawlerStoreOptions(BaseModel): - storage_id: str - - r2_jurisdiction: Optional[str] = None - - storage_type: Optional[Provider] = None - - class SourceParamsWebCrawler(BaseModel): parse_options: Optional[SourceParamsWebCrawlerParseOptions] = None - parse_type: Optional[Literal["sitemap", "feed-rss", "crawl"]] = None - - store_options: Optional[SourceParamsWebCrawlerStoreOptions] = None + parse_type: Optional[Literal["sitemap", "crawl"]] = None class SourceParams(BaseModel): diff --git a/src/cloudflare/types/aisearch/namespaces/instance_read_response.py b/src/cloudflare/types/aisearch/namespaces/instance_read_response.py index 589d5cc29a5..2fb6ae29cee 100644 --- a/src/cloudflare/types/aisearch/namespaces/instance_read_response.py +++ b/src/cloudflare/types/aisearch/namespaces/instance_read_response.py @@ -7,7 +7,6 @@ from pydantic import Field as FieldInfo from ...._models import BaseModel -from ...r2.buckets.provider import Provider __all__ = [ "InstanceReadResponse", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -186,20 +184,10 @@ class SourceParamsWebCrawlerParseOptions(BaseModel): use_browser_rendering: Optional[bool] = None -class SourceParamsWebCrawlerStoreOptions(BaseModel): - storage_id: str - - r2_jurisdiction: Optional[str] = None - - storage_type: Optional[Provider] = None - - class SourceParamsWebCrawler(BaseModel): parse_options: Optional[SourceParamsWebCrawlerParseOptions] = None - parse_type: Optional[Literal["sitemap", "feed-rss", "crawl"]] = None - - store_options: Optional[SourceParamsWebCrawlerStoreOptions] = None + parse_type: Optional[Literal["sitemap", "crawl"]] = None class SourceParams(BaseModel): diff --git a/src/cloudflare/types/aisearch/namespaces/instance_update_params.py b/src/cloudflare/types/aisearch/namespaces/instance_update_params.py index 835af3a276a..34678a6245b 100644 --- a/src/cloudflare/types/aisearch/namespaces/instance_update_params.py +++ b/src/cloudflare/types/aisearch/namespaces/instance_update_params.py @@ -7,7 +7,6 @@ from ...._types import SequenceNotStr from ...._utils import PropertyInfo -from ...r2.buckets.provider import Provider __all__ = [ "InstanceUpdateParams", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -171,6 +169,8 @@ class InstanceUpdateParams(TypedDict, total=False): score_threshold: float + source: Optional[str] + source_params: Optional[SourceParams] summarization: bool @@ -382,20 +382,10 @@ class SourceParamsWebCrawlerParseOptions(TypedDict, total=False): use_browser_rendering: bool -class SourceParamsWebCrawlerStoreOptions(TypedDict, total=False): - storage_id: Required[str] - - r2_jurisdiction: str - - storage_type: Provider - - class SourceParamsWebCrawler(TypedDict, total=False): parse_options: SourceParamsWebCrawlerParseOptions - parse_type: Literal["sitemap", "feed-rss", "crawl"] - - store_options: SourceParamsWebCrawlerStoreOptions + parse_type: Literal["sitemap", "crawl"] class SourceParams(TypedDict, total=False): diff --git a/src/cloudflare/types/aisearch/namespaces/instance_update_response.py b/src/cloudflare/types/aisearch/namespaces/instance_update_response.py index c16ba2b1a67..b425907f486 100644 --- a/src/cloudflare/types/aisearch/namespaces/instance_update_response.py +++ b/src/cloudflare/types/aisearch/namespaces/instance_update_response.py @@ -7,7 +7,6 @@ from pydantic import Field as FieldInfo from ...._models import BaseModel -from ...r2.buckets.provider import Provider __all__ = [ "InstanceUpdateResponse", @@ -26,7 +25,6 @@ "SourceParamsWebCrawler", "SourceParamsWebCrawlerParseOptions", "SourceParamsWebCrawlerParseOptionsContentSelector", - "SourceParamsWebCrawlerStoreOptions", ] @@ -186,20 +184,10 @@ class SourceParamsWebCrawlerParseOptions(BaseModel): use_browser_rendering: Optional[bool] = None -class SourceParamsWebCrawlerStoreOptions(BaseModel): - storage_id: str - - r2_jurisdiction: Optional[str] = None - - storage_type: Optional[Provider] = None - - class SourceParamsWebCrawler(BaseModel): parse_options: Optional[SourceParamsWebCrawlerParseOptions] = None - parse_type: Optional[Literal["sitemap", "feed-rss", "crawl"]] = None - - store_options: Optional[SourceParamsWebCrawlerStoreOptions] = None + parse_type: Optional[Literal["sitemap", "crawl"]] = None class SourceParams(BaseModel): diff --git a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_create_response.py b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_create_response.py index 92b486b5f0d..f91fc83adc9 100644 --- a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_create_response.py +++ b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_create_response.py @@ -84,9 +84,9 @@ class Server(BaseModel): """ When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. """ last_successful_sync: Optional[datetime] = None diff --git a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_list_response.py b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_list_response.py index 6de62558668..d5f41b17a7d 100644 --- a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_list_response.py +++ b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_list_response.py @@ -84,9 +84,9 @@ class Server(BaseModel): """ When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. """ last_successful_sync: Optional[datetime] = None diff --git a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_read_response.py b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_read_response.py index 4df2e8eb20e..dfd1d4ec3ae 100644 --- a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_read_response.py +++ b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_read_response.py @@ -84,9 +84,9 @@ class Server(BaseModel): """ When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. """ last_successful_sync: Optional[datetime] = None diff --git a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_update_response.py b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_update_response.py index 1f53e112b32..de8437af611 100644 --- a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_update_response.py +++ b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/portal_update_response.py @@ -84,9 +84,9 @@ class Server(BaseModel): """ When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. """ last_successful_sync: Optional[datetime] = None diff --git a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_create_params.py b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_create_params.py index 4ccd3c71a3d..3cdab958821 100644 --- a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_create_params.py +++ b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_create_params.py @@ -28,9 +28,9 @@ class ServerCreateParams(TypedDict, total=False): """ When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. """ secure_web_gateway: bool diff --git a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_create_response.py b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_create_response.py index db01ca275b5..9e6440902b9 100644 --- a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_create_response.py +++ b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_create_response.py @@ -74,9 +74,9 @@ class ServerCreateResponse(BaseModel): """ When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. """ last_successful_sync: Optional[datetime] = None diff --git a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_delete_response.py b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_delete_response.py index ac459345c3e..8f95e68616e 100644 --- a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_delete_response.py +++ b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_delete_response.py @@ -74,9 +74,9 @@ class ServerDeleteResponse(BaseModel): """ When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. """ last_successful_sync: Optional[datetime] = None diff --git a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_list_response.py b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_list_response.py index d782f777b55..4faee3778bd 100644 --- a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_list_response.py +++ b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_list_response.py @@ -74,9 +74,9 @@ class ServerListResponse(BaseModel): """ When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. """ last_successful_sync: Optional[datetime] = None diff --git a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_read_response.py b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_read_response.py index a12d32a5bc9..afd49938dc2 100644 --- a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_read_response.py +++ b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_read_response.py @@ -74,9 +74,9 @@ class ServerReadResponse(BaseModel): """ When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. """ last_successful_sync: Optional[datetime] = None diff --git a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_update_params.py b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_update_params.py index f05072d243d..18803c768c6 100644 --- a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_update_params.py +++ b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_update_params.py @@ -19,9 +19,9 @@ class ServerUpdateParams(TypedDict, total=False): """ When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. """ name: str diff --git a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_update_response.py b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_update_response.py index 6ab98c14808..b3c80fd1074 100644 --- a/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_update_response.py +++ b/src/cloudflare/types/zero_trust/access/ai_controls/mcp/server_update_response.py @@ -74,9 +74,9 @@ class ServerUpdateResponse(BaseModel): """ When true, the gateway worker uses the shared Cloudflare-owned OAuth callback endpoint as the redirect_uri for upstream on-behalf OAuth, instead of the - customer portal hostname. New public server creates default to true; existing - servers default to false from migration until explicitly updated. Effective - behavior is gated by the gateway worker's per-env rollout mode KV key. + customer portal hostname. Defaults to false (off); opt in per server by setting + true. Effective behavior is gated by the gateway worker's per-env rollout mode + KV key. """ last_successful_sync: Optional[datetime] = None diff --git a/src/cloudflare/types/zero_trust/devices/ip_profile_list_params.py b/src/cloudflare/types/zero_trust/devices/ip_profile_list_params.py index 184eba34c9b..6531d6ad7ea 100644 --- a/src/cloudflare/types/zero_trust/devices/ip_profile_list_params.py +++ b/src/cloudflare/types/zero_trust/devices/ip_profile_list_params.py @@ -10,5 +10,8 @@ class IPProfileListParams(TypedDict, total=False): account_id: Required[str] + page: int + """The page number to return.""" + per_page: int """The number of IP profiles to return per page.""" diff --git a/src/cloudflare/types/zero_trust/devices/policies/custom_create_params.py b/src/cloudflare/types/zero_trust/devices/policies/custom_create_params.py index f04c3a41a07..1f3f9b66970 100644 --- a/src/cloudflare/types/zero_trust/devices/policies/custom_create_params.py +++ b/src/cloudflare/types/zero_trust/devices/policies/custom_create_params.py @@ -9,7 +9,7 @@ from ..split_tunnel_exclude_param import SplitTunnelExcludeParam from ..split_tunnel_include_param import SplitTunnelIncludeParam -__all__ = ["CustomCreateParams", "DNSSearchSuffix", "ServiceModeV2", "VirtualNetworks"] +__all__ = ["CustomCreateParams", "DNSSearchSuffix", "GlobalAcceleration", "ServiceModeV2", "VirtualNetworks"] class CustomCreateParams(TypedDict, total=False): @@ -79,6 +79,15 @@ class CustomCreateParams(TypedDict, total=False): exclude_office_ips: bool """Whether to add Microsoft IPs to Split Tunnel exclusions.""" + global_acceleration: Optional[GlobalAcceleration] + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses + instead of the default ones. Please contact your account representative to + enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + include: Iterable[SplitTunnelIncludeParam] """List of routes included in the WARP client's tunnel. @@ -136,6 +145,31 @@ class DNSSearchSuffix(TypedDict, total=False): """A description of the DNS search suffix.""" +class GlobalAcceleration(TypedDict, total=False): + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses instead of the default ones. Please contact your account representative to enable this feature on your account. See https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + + api_endpoints: Required[SequenceNotStr[str]] + """IP:port entries for the API endpoints.""" + + enabled: Required[bool] + """Global acceleration settings are used only when "enabled".""" + + masque_endpoints: Required[SequenceNotStr[str]] + """IP:port entries for the MASQUE tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + wireguard_endpoints: Required[SequenceNotStr[str]] + """IP:port entries for the WireGuard tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + class ServiceModeV2(TypedDict, total=False): mode: str """The mode to run the WARP client under.""" diff --git a/src/cloudflare/types/zero_trust/devices/policies/custom_edit_params.py b/src/cloudflare/types/zero_trust/devices/policies/custom_edit_params.py index 2a76f340359..a56b6b4089b 100644 --- a/src/cloudflare/types/zero_trust/devices/policies/custom_edit_params.py +++ b/src/cloudflare/types/zero_trust/devices/policies/custom_edit_params.py @@ -9,7 +9,7 @@ from ..split_tunnel_exclude_param import SplitTunnelExcludeParam from ..split_tunnel_include_param import SplitTunnelIncludeParam -__all__ = ["CustomEditParams", "DNSSearchSuffix", "ServiceModeV2", "VirtualNetworks"] +__all__ = ["CustomEditParams", "DNSSearchSuffix", "GlobalAcceleration", "ServiceModeV2", "VirtualNetworks"] class CustomEditParams(TypedDict, total=False): @@ -61,6 +61,15 @@ class CustomEditParams(TypedDict, total=False): exclude_office_ips: bool """Whether to add Microsoft IPs to Split Tunnel exclusions.""" + global_acceleration: Optional[GlobalAcceleration] + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses + instead of the default ones. Please contact your account representative to + enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + include: Iterable[SplitTunnelIncludeParam] """List of routes included in the WARP client's tunnel. @@ -136,6 +145,31 @@ class DNSSearchSuffix(TypedDict, total=False): """A description of the DNS search suffix.""" +class GlobalAcceleration(TypedDict, total=False): + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses instead of the default ones. Please contact your account representative to enable this feature on your account. See https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + + api_endpoints: Required[SequenceNotStr[str]] + """IP:port entries for the API endpoints.""" + + enabled: Required[bool] + """Global acceleration settings are used only when "enabled".""" + + masque_endpoints: Required[SequenceNotStr[str]] + """IP:port entries for the MASQUE tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + wireguard_endpoints: Required[SequenceNotStr[str]] + """IP:port entries for the WireGuard tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + class ServiceModeV2(TypedDict, total=False): mode: str """The mode to run the WARP client under.""" diff --git a/src/cloudflare/types/zero_trust/devices/policies/default_edit_params.py b/src/cloudflare/types/zero_trust/devices/policies/default_edit_params.py index 62fa238f495..83b91dbfc2b 100644 --- a/src/cloudflare/types/zero_trust/devices/policies/default_edit_params.py +++ b/src/cloudflare/types/zero_trust/devices/policies/default_edit_params.py @@ -9,7 +9,7 @@ from ..split_tunnel_exclude_param import SplitTunnelExcludeParam from ..split_tunnel_include_param import SplitTunnelIncludeParam -__all__ = ["DefaultEditParams", "DNSSearchSuffix", "ServiceModeV2", "VirtualNetworks"] +__all__ = ["DefaultEditParams", "DNSSearchSuffix", "GlobalAcceleration", "ServiceModeV2", "VirtualNetworks"] class DefaultEditParams(TypedDict, total=False): @@ -55,6 +55,15 @@ class DefaultEditParams(TypedDict, total=False): exclude_office_ips: bool """Whether to add Microsoft IPs to Split Tunnel exclusions.""" + global_acceleration: Optional[GlobalAcceleration] + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses + instead of the default ones. Please contact your account representative to + enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + include: Iterable[SplitTunnelIncludeParam] """List of routes included in the WARP client's tunnel. @@ -112,6 +121,31 @@ class DNSSearchSuffix(TypedDict, total=False): """A description of the DNS search suffix.""" +class GlobalAcceleration(TypedDict, total=False): + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses instead of the default ones. Please contact your account representative to enable this feature on your account. See https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + + api_endpoints: Required[SequenceNotStr[str]] + """IP:port entries for the API endpoints.""" + + enabled: Required[bool] + """Global acceleration settings are used only when "enabled".""" + + masque_endpoints: Required[SequenceNotStr[str]] + """IP:port entries for the MASQUE tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + wireguard_endpoints: Required[SequenceNotStr[str]] + """IP:port entries for the WireGuard tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + class ServiceModeV2(TypedDict, total=False): mode: str """The mode to run the WARP client under.""" diff --git a/src/cloudflare/types/zero_trust/devices/policies/default_edit_response.py b/src/cloudflare/types/zero_trust/devices/policies/default_edit_response.py index ed264b24dd8..35352bf6f4b 100644 --- a/src/cloudflare/types/zero_trust/devices/policies/default_edit_response.py +++ b/src/cloudflare/types/zero_trust/devices/policies/default_edit_response.py @@ -7,7 +7,7 @@ from ..split_tunnel_exclude import SplitTunnelExclude from ..split_tunnel_include import SplitTunnelInclude -__all__ = ["DefaultEditResponse", "DNSSearchSuffix", "ServiceModeV2", "VirtualNetworks"] +__all__ = ["DefaultEditResponse", "DNSSearchSuffix", "GlobalAcceleration", "ServiceModeV2", "VirtualNetworks"] class DNSSearchSuffix(BaseModel): @@ -18,6 +18,31 @@ class DNSSearchSuffix(BaseModel): """A description of the DNS search suffix.""" +class GlobalAcceleration(BaseModel): + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses instead of the default ones. Please contact your account representative to enable this feature on your account. See https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + + api_endpoints: List[str] + """IP:port entries for the API endpoints.""" + + enabled: bool + """Global acceleration settings are used only when "enabled".""" + + masque_endpoints: List[str] + """IP:port entries for the MASQUE tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + wireguard_endpoints: List[str] + """IP:port entries for the WireGuard tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + class ServiceModeV2(BaseModel): mode: Optional[str] = None """The mode to run the WARP client under.""" @@ -87,6 +112,15 @@ class DefaultEditResponse(BaseModel): gateway_unique_id: Optional[str] = None + global_acceleration: Optional[GlobalAcceleration] = None + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses + instead of the default ones. Please contact your account representative to + enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + include: Optional[List[SplitTunnelInclude]] = None """List of routes included in the WARP client's tunnel.""" diff --git a/src/cloudflare/types/zero_trust/devices/policies/default_get_response.py b/src/cloudflare/types/zero_trust/devices/policies/default_get_response.py index 6555e2846af..c47209a98d5 100644 --- a/src/cloudflare/types/zero_trust/devices/policies/default_get_response.py +++ b/src/cloudflare/types/zero_trust/devices/policies/default_get_response.py @@ -7,7 +7,7 @@ from ..split_tunnel_exclude import SplitTunnelExclude from ..split_tunnel_include import SplitTunnelInclude -__all__ = ["DefaultGetResponse", "DNSSearchSuffix", "ServiceModeV2", "VirtualNetworks"] +__all__ = ["DefaultGetResponse", "DNSSearchSuffix", "GlobalAcceleration", "ServiceModeV2", "VirtualNetworks"] class DNSSearchSuffix(BaseModel): @@ -18,6 +18,31 @@ class DNSSearchSuffix(BaseModel): """A description of the DNS search suffix.""" +class GlobalAcceleration(BaseModel): + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses instead of the default ones. Please contact your account representative to enable this feature on your account. See https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + + api_endpoints: List[str] + """IP:port entries for the API endpoints.""" + + enabled: bool + """Global acceleration settings are used only when "enabled".""" + + masque_endpoints: List[str] + """IP:port entries for the MASQUE tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + wireguard_endpoints: List[str] + """IP:port entries for the WireGuard tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + class ServiceModeV2(BaseModel): mode: Optional[str] = None """The mode to run the WARP client under.""" @@ -87,6 +112,15 @@ class DefaultGetResponse(BaseModel): gateway_unique_id: Optional[str] = None + global_acceleration: Optional[GlobalAcceleration] = None + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses + instead of the default ones. Please contact your account representative to + enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + include: Optional[List[SplitTunnelInclude]] = None """List of routes included in the WARP client's tunnel.""" diff --git a/src/cloudflare/types/zero_trust/devices/settings_policy.py b/src/cloudflare/types/zero_trust/devices/settings_policy.py index 7746b5346b8..4dd47495139 100644 --- a/src/cloudflare/types/zero_trust/devices/settings_policy.py +++ b/src/cloudflare/types/zero_trust/devices/settings_policy.py @@ -7,7 +7,7 @@ from .split_tunnel_exclude import SplitTunnelExclude from .split_tunnel_include import SplitTunnelInclude -__all__ = ["SettingsPolicy", "DNSSearchSuffix", "ServiceModeV2", "TargetTest", "VirtualNetworks"] +__all__ = ["SettingsPolicy", "DNSSearchSuffix", "GlobalAcceleration", "ServiceModeV2", "TargetTest", "VirtualNetworks"] class DNSSearchSuffix(BaseModel): @@ -18,6 +18,31 @@ class DNSSearchSuffix(BaseModel): """A description of the DNS search suffix.""" +class GlobalAcceleration(BaseModel): + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses instead of the default ones. Please contact your account representative to enable this feature on your account. See https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + + api_endpoints: List[str] + """IP:port entries for the API endpoints.""" + + enabled: bool + """Global acceleration settings are used only when "enabled".""" + + masque_endpoints: List[str] + """IP:port entries for the MASQUE tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + wireguard_endpoints: List[str] + """IP:port entries for the WireGuard tunnel endpoints. + + Either wireguard_endpoints or masque_endpoints must be provided. + """ + + class ServiceModeV2(BaseModel): mode: Optional[str] = None """The mode to run the WARP client under.""" @@ -98,6 +123,15 @@ class SettingsPolicy(BaseModel): gateway_unique_id: Optional[str] = None + global_acceleration: Optional[GlobalAcceleration] = None + """Global Acceleration settings for China. + + When configured, WARP clients connect to the Global Accelerator addresses + instead of the default ones. Please contact your account representative to + enable this feature on your account. See + https://developers.cloudflare.com/china-network/concepts/global-acceleration/. + """ + include: Optional[List[SplitTunnelInclude]] = None """List of routes included in the WARP client's tunnel.""" diff --git a/tests/api_resources/aisearch/namespaces/test_instances.py b/tests/api_resources/aisearch/namespaces/test_instances.py index 6662d33e399..9a4fedddf76 100644 --- a/tests/api_resources/aisearch/namespaces/test_instances.py +++ b/tests/api_resources/aisearch/namespaces/test_instances.py @@ -125,11 +125,6 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None: "use_browser_rendering": True, }, "parse_type": "sitemap", - "store_options": { - "storage_id": "storage_id", - "r2_jurisdiction": "r2_jurisdiction", - "storage_type": "r2", - }, }, }, sync_interval=900, @@ -253,6 +248,7 @@ def test_method_update_with_all_params(self, client: Cloudflare) -> None: rewrite_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast", rewrite_query=True, score_threshold=0, + source="source", source_params={ "exclude_items": ["/admin/**", "/private/**", "**\\temp\\**"], "include_items": ["/blog/**", "/docs/**/*.html", "**\\blog\\**.html"], @@ -279,11 +275,6 @@ def test_method_update_with_all_params(self, client: Cloudflare) -> None: "use_browser_rendering": True, }, "parse_type": "sitemap", - "store_options": { - "storage_id": "storage_id", - "r2_jurisdiction": "r2_jurisdiction", - "storage_type": "r2", - }, }, }, summarization=True, @@ -946,11 +937,6 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare "use_browser_rendering": True, }, "parse_type": "sitemap", - "store_options": { - "storage_id": "storage_id", - "r2_jurisdiction": "r2_jurisdiction", - "storage_type": "r2", - }, }, }, sync_interval=900, @@ -1074,6 +1060,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare rewrite_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast", rewrite_query=True, score_threshold=0, + source="source", source_params={ "exclude_items": ["/admin/**", "/private/**", "**\\temp\\**"], "include_items": ["/blog/**", "/docs/**/*.html", "**\\blog\\**.html"], @@ -1100,11 +1087,6 @@ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare "use_browser_rendering": True, }, "parse_type": "sitemap", - "store_options": { - "storage_id": "storage_id", - "r2_jurisdiction": "r2_jurisdiction", - "storage_type": "r2", - }, }, }, summarization=True, diff --git a/tests/api_resources/aisearch/test_instances.py b/tests/api_resources/aisearch/test_instances.py index 458d4f14faa..d83d756cec4 100644 --- a/tests/api_resources/aisearch/test_instances.py +++ b/tests/api_resources/aisearch/test_instances.py @@ -123,11 +123,6 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None: "use_browser_rendering": True, }, "parse_type": "sitemap", - "store_options": { - "storage_id": "storage_id", - "r2_jurisdiction": "r2_jurisdiction", - "storage_type": "r2", - }, }, }, sync_interval=900, @@ -239,6 +234,7 @@ def test_method_update_with_all_params(self, client: Cloudflare) -> None: rewrite_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast", rewrite_query=True, score_threshold=0, + source="source", source_params={ "exclude_items": ["/admin/**", "/private/**", "**\\temp\\**"], "include_items": ["/blog/**", "/docs/**/*.html", "**\\blog\\**.html"], @@ -265,11 +261,6 @@ def test_method_update_with_all_params(self, client: Cloudflare) -> None: "use_browser_rendering": True, }, "parse_type": "sitemap", - "store_options": { - "storage_id": "storage_id", - "r2_jurisdiction": "r2_jurisdiction", - "storage_type": "r2", - }, }, }, summarization=True, @@ -840,11 +831,6 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare "use_browser_rendering": True, }, "parse_type": "sitemap", - "store_options": { - "storage_id": "storage_id", - "r2_jurisdiction": "r2_jurisdiction", - "storage_type": "r2", - }, }, }, sync_interval=900, @@ -956,6 +942,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare rewrite_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast", rewrite_query=True, score_threshold=0, + source="source", source_params={ "exclude_items": ["/admin/**", "/private/**", "**\\temp\\**"], "include_items": ["/blog/**", "/docs/**/*.html", "**\\blog\\**.html"], @@ -982,11 +969,6 @@ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare "use_browser_rendering": True, }, "parse_type": "sitemap", - "store_options": { - "storage_id": "storage_id", - "r2_jurisdiction": "r2_jurisdiction", - "storage_type": "r2", - }, }, }, summarization=True, diff --git a/tests/api_resources/zero_trust/devices/policies/test_custom.py b/tests/api_resources/zero_trust/devices/policies/test_custom.py index 41c99047406..074f29d1f61 100644 --- a/tests/api_resources/zero_trust/devices/policies/test_custom.py +++ b/tests/api_resources/zero_trust/devices/policies/test_custom.py @@ -58,6 +58,12 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None: } ], exclude_office_ips=True, + global_acceleration={ + "api_endpoints": ["198.51.100.1:443"], + "enabled": True, + "masque_endpoints": ["198.51.100.1:443"], + "wireguard_endpoints": ["198.51.100.1:2408"], + }, include=[ { "address": "192.0.2.0/24", @@ -247,6 +253,12 @@ def test_method_edit_with_all_params(self, client: Cloudflare) -> None: } ], exclude_office_ips=True, + global_acceleration={ + "api_endpoints": ["198.51.100.1:443"], + "enabled": True, + "masque_endpoints": ["198.51.100.1:443"], + "wireguard_endpoints": ["198.51.100.1:2408"], + }, include=[ { "address": "192.0.2.0/24", @@ -415,6 +427,12 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare } ], exclude_office_ips=True, + global_acceleration={ + "api_endpoints": ["198.51.100.1:443"], + "enabled": True, + "masque_endpoints": ["198.51.100.1:443"], + "wireguard_endpoints": ["198.51.100.1:2408"], + }, include=[ { "address": "192.0.2.0/24", @@ -604,6 +622,12 @@ async def test_method_edit_with_all_params(self, async_client: AsyncCloudflare) } ], exclude_office_ips=True, + global_acceleration={ + "api_endpoints": ["198.51.100.1:443"], + "enabled": True, + "masque_endpoints": ["198.51.100.1:443"], + "wireguard_endpoints": ["198.51.100.1:2408"], + }, include=[ { "address": "192.0.2.0/24", diff --git a/tests/api_resources/zero_trust/devices/policies/test_default.py b/tests/api_resources/zero_trust/devices/policies/test_default.py index 56775dbbe99..39d7ab56e92 100644 --- a/tests/api_resources/zero_trust/devices/policies/test_default.py +++ b/tests/api_resources/zero_trust/devices/policies/test_default.py @@ -47,6 +47,12 @@ def test_method_edit_with_all_params(self, client: Cloudflare) -> None: } ], exclude_office_ips=True, + global_acceleration={ + "api_endpoints": ["198.51.100.1:443"], + "enabled": True, + "masque_endpoints": ["198.51.100.1:443"], + "wireguard_endpoints": ["198.51.100.1:2408"], + }, include=[ { "address": "192.0.2.0/24", @@ -176,6 +182,12 @@ async def test_method_edit_with_all_params(self, async_client: AsyncCloudflare) } ], exclude_office_ips=True, + global_acceleration={ + "api_endpoints": ["198.51.100.1:443"], + "enabled": True, + "masque_endpoints": ["198.51.100.1:443"], + "wireguard_endpoints": ["198.51.100.1:2408"], + }, include=[ { "address": "192.0.2.0/24", diff --git a/tests/api_resources/zero_trust/devices/test_ip_profiles.py b/tests/api_resources/zero_trust/devices/test_ip_profiles.py index 3bafccc9b0d..5e85e909988 100644 --- a/tests/api_resources/zero_trust/devices/test_ip_profiles.py +++ b/tests/api_resources/zero_trust/devices/test_ip_profiles.py @@ -9,7 +9,7 @@ from cloudflare import Cloudflare, AsyncCloudflare from tests.utils import assert_matches_type -from cloudflare.pagination import SyncSinglePage, AsyncSinglePage +from cloudflare.pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray from cloudflare.types.zero_trust.devices import ( IPProfile, IPProfileDeleteResponse, @@ -166,16 +166,17 @@ def test_method_list(self, client: Cloudflare) -> None: ip_profile = client.zero_trust.devices.ip_profiles.list( account_id="account_id", ) - assert_matches_type(SyncSinglePage[IPProfile], ip_profile, path=["response"]) + assert_matches_type(SyncV4PagePaginationArray[IPProfile], ip_profile, path=["response"]) @pytest.mark.skip(reason="401 Unauthorized: Prism doesnt handle api tokens") @parametrize def test_method_list_with_all_params(self, client: Cloudflare) -> None: ip_profile = client.zero_trust.devices.ip_profiles.list( account_id="account_id", + page=1, per_page=1, ) - assert_matches_type(SyncSinglePage[IPProfile], ip_profile, path=["response"]) + assert_matches_type(SyncV4PagePaginationArray[IPProfile], ip_profile, path=["response"]) @pytest.mark.skip(reason="401 Unauthorized: Prism doesnt handle api tokens") @parametrize @@ -187,7 +188,7 @@ def test_raw_response_list(self, client: Cloudflare) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" ip_profile = response.parse() - assert_matches_type(SyncSinglePage[IPProfile], ip_profile, path=["response"]) + assert_matches_type(SyncV4PagePaginationArray[IPProfile], ip_profile, path=["response"]) @pytest.mark.skip(reason="401 Unauthorized: Prism doesnt handle api tokens") @parametrize @@ -199,7 +200,7 @@ def test_streaming_response_list(self, client: Cloudflare) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" ip_profile = response.parse() - assert_matches_type(SyncSinglePage[IPProfile], ip_profile, path=["response"]) + assert_matches_type(SyncV4PagePaginationArray[IPProfile], ip_profile, path=["response"]) assert cast(Any, response.is_closed) is True @@ -466,16 +467,17 @@ async def test_method_list(self, async_client: AsyncCloudflare) -> None: ip_profile = await async_client.zero_trust.devices.ip_profiles.list( account_id="account_id", ) - assert_matches_type(AsyncSinglePage[IPProfile], ip_profile, path=["response"]) + assert_matches_type(AsyncV4PagePaginationArray[IPProfile], ip_profile, path=["response"]) @pytest.mark.skip(reason="401 Unauthorized: Prism doesnt handle api tokens") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None: ip_profile = await async_client.zero_trust.devices.ip_profiles.list( account_id="account_id", + page=1, per_page=1, ) - assert_matches_type(AsyncSinglePage[IPProfile], ip_profile, path=["response"]) + assert_matches_type(AsyncV4PagePaginationArray[IPProfile], ip_profile, path=["response"]) @pytest.mark.skip(reason="401 Unauthorized: Prism doesnt handle api tokens") @parametrize @@ -487,7 +489,7 @@ async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" ip_profile = await response.parse() - assert_matches_type(AsyncSinglePage[IPProfile], ip_profile, path=["response"]) + assert_matches_type(AsyncV4PagePaginationArray[IPProfile], ip_profile, path=["response"]) @pytest.mark.skip(reason="401 Unauthorized: Prism doesnt handle api tokens") @parametrize @@ -499,7 +501,7 @@ async def test_streaming_response_list(self, async_client: AsyncCloudflare) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" ip_profile = await response.parse() - assert_matches_type(AsyncSinglePage[IPProfile], ip_profile, path=["response"]) + assert_matches_type(AsyncV4PagePaginationArray[IPProfile], ip_profile, path=["response"]) assert cast(Any, response.is_closed) is True From ce3088786bd3b5828d6e879f5222207b99ec0c4e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 13:31:48 +0000 Subject: [PATCH 22/29] chore(api): update composite API spec --- .stats.yml | 4 +-- .../types/rulesets/rule_create_params.py | 28 +++++-------------- .../types/rulesets/rule_edit_params.py | 28 +++++-------------- .../types/rulesets/set_cache_settings_rule.py | 28 +++++-------------- .../rulesets/set_cache_settings_rule_param.py | 28 +++++-------------- tests/api_resources/rulesets/test_rules.py | 24 +++------------- 6 files changed, 34 insertions(+), 106 deletions(-) diff --git a/.stats.yml b/.stats.yml index e37516681b4..bb82835162b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2412 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-9cadb6f47eb3d145352d0e7112605e553321aa88991fc55ba6efbe665bb2b52f.yml -openapi_spec_hash: 653808896077b2132c43a0a8a383679c +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-77454b7f4d8a6c1b61e16e7f01c2b7af393011c053722940267b38937660140b.yml +openapi_spec_hash: 5929bc536ceca35c4f3ff7af07094fcb config_hash: a23f4c71ccd6c6117c24f63ae415dcf7 diff --git a/src/cloudflare/types/rulesets/rule_create_params.py b/src/cloudflare/types/rulesets/rule_create_params.py index 9c5b7d9ea6b..84d8041253c 100644 --- a/src/cloudflare/types/rulesets/rule_create_params.py +++ b/src/cloudflare/types/rulesets/rule_create_params.py @@ -3430,29 +3430,15 @@ class SetCacheSettingsRuleActionParametersSharedDictionary(TypedDict, total=Fals class SetCacheSettingsRuleActionParametersVaryDefault(TypedDict, total=False): """ - Controls how a single request header (or the default for all headers) contributes to the cache key. + Controls how response Vary headers without a per-header override contribute to the cache key. """ action: Required[Literal["bypass", "passthrough", "normalize"]] """How the header value is treated when building the cache key.""" - languages: SequenceNotStr[str] - """The set of languages to normalize against. - - Only valid for the `accept-language` header. - """ - - media_types: SequenceNotStr[str] - """The set of media types to normalize against. - - Only valid for the `accept` header. - """ - class SetCacheSettingsRuleActionParametersVaryHeaders(TypedDict, total=False): - """ - Controls how a single request header (or the default for all headers) contributes to the cache key. - """ + """Controls how a single request header contributes to the cache key.""" action: Required[Literal["bypass", "passthrough", "normalize"]] """How the header value is treated when building the cache key.""" @@ -3473,13 +3459,13 @@ class SetCacheSettingsRuleActionParametersVaryHeaders(TypedDict, total=False): class SetCacheSettingsRuleActionParametersVary(TypedDict, total=False): """Controls how cached responses vary based on request headers. - At least one of `default` or `headers` must be set, and `default` is required when `headers` is set. + `default` is required by the API and applies to any Vary response header that does not have a per-header override. """ default: SetCacheSettingsRuleActionParametersVaryDefault """ - Controls how a single request header (or the default for all headers) - contributes to the cache key. + Controls how response Vary headers without a per-header override contribute to + the cache key. """ headers: Dict[str, SetCacheSettingsRuleActionParametersVaryHeaders] @@ -3564,8 +3550,8 @@ class SetCacheSettingsRuleActionParameters(TypedDict, total=False): vary: SetCacheSettingsRuleActionParametersVary """Controls how cached responses vary based on request headers. - At least one of `default` or `headers` must be set, and `default` is required - when `headers` is set. + `default` is required by the API and applies to any Vary response header that + does not have a per-header override. """ diff --git a/src/cloudflare/types/rulesets/rule_edit_params.py b/src/cloudflare/types/rulesets/rule_edit_params.py index 272ed9c3a2f..2ab66cea32e 100644 --- a/src/cloudflare/types/rulesets/rule_edit_params.py +++ b/src/cloudflare/types/rulesets/rule_edit_params.py @@ -3481,29 +3481,15 @@ class SetCacheSettingsRuleActionParametersSharedDictionary(TypedDict, total=Fals class SetCacheSettingsRuleActionParametersVaryDefault(TypedDict, total=False): """ - Controls how a single request header (or the default for all headers) contributes to the cache key. + Controls how response Vary headers without a per-header override contribute to the cache key. """ action: Required[Literal["bypass", "passthrough", "normalize"]] """How the header value is treated when building the cache key.""" - languages: SequenceNotStr[str] - """The set of languages to normalize against. - - Only valid for the `accept-language` header. - """ - - media_types: SequenceNotStr[str] - """The set of media types to normalize against. - - Only valid for the `accept` header. - """ - class SetCacheSettingsRuleActionParametersVaryHeaders(TypedDict, total=False): - """ - Controls how a single request header (or the default for all headers) contributes to the cache key. - """ + """Controls how a single request header contributes to the cache key.""" action: Required[Literal["bypass", "passthrough", "normalize"]] """How the header value is treated when building the cache key.""" @@ -3524,13 +3510,13 @@ class SetCacheSettingsRuleActionParametersVaryHeaders(TypedDict, total=False): class SetCacheSettingsRuleActionParametersVary(TypedDict, total=False): """Controls how cached responses vary based on request headers. - At least one of `default` or `headers` must be set, and `default` is required when `headers` is set. + `default` is required by the API and applies to any Vary response header that does not have a per-header override. """ default: SetCacheSettingsRuleActionParametersVaryDefault """ - Controls how a single request header (or the default for all headers) - contributes to the cache key. + Controls how response Vary headers without a per-header override contribute to + the cache key. """ headers: Dict[str, SetCacheSettingsRuleActionParametersVaryHeaders] @@ -3615,8 +3601,8 @@ class SetCacheSettingsRuleActionParameters(TypedDict, total=False): vary: SetCacheSettingsRuleActionParametersVary """Controls how cached responses vary based on request headers. - At least one of `default` or `headers` must be set, and `default` is required - when `headers` is set. + `default` is required by the API and applies to any Vary response header that + does not have a per-header override. """ diff --git a/src/cloudflare/types/rulesets/set_cache_settings_rule.py b/src/cloudflare/types/rulesets/set_cache_settings_rule.py index c5c8c06032f..9d2db7c0dbc 100644 --- a/src/cloudflare/types/rulesets/set_cache_settings_rule.py +++ b/src/cloudflare/types/rulesets/set_cache_settings_rule.py @@ -258,29 +258,15 @@ class ActionParametersSharedDictionary(BaseModel): class ActionParametersVaryDefault(BaseModel): """ - Controls how a single request header (or the default for all headers) contributes to the cache key. + Controls how response Vary headers without a per-header override contribute to the cache key. """ action: Literal["bypass", "passthrough", "normalize"] """How the header value is treated when building the cache key.""" - languages: Optional[List[str]] = None - """The set of languages to normalize against. - - Only valid for the `accept-language` header. - """ - - media_types: Optional[List[str]] = None - """The set of media types to normalize against. - - Only valid for the `accept` header. - """ - class ActionParametersVaryHeaders(BaseModel): - """ - Controls how a single request header (or the default for all headers) contributes to the cache key. - """ + """Controls how a single request header contributes to the cache key.""" action: Literal["bypass", "passthrough", "normalize"] """How the header value is treated when building the cache key.""" @@ -301,13 +287,13 @@ class ActionParametersVaryHeaders(BaseModel): class ActionParametersVary(BaseModel): """Controls how cached responses vary based on request headers. - At least one of `default` or `headers` must be set, and `default` is required when `headers` is set. + `default` is required by the API and applies to any Vary response header that does not have a per-header override. """ default: Optional[ActionParametersVaryDefault] = None """ - Controls how a single request header (or the default for all headers) - contributes to the cache key. + Controls how response Vary headers without a per-header override contribute to + the cache key. """ headers: Optional[Dict[str, ActionParametersVaryHeaders]] = None @@ -392,8 +378,8 @@ class ActionParameters(BaseModel): vary: Optional[ActionParametersVary] = None """Controls how cached responses vary based on request headers. - At least one of `default` or `headers` must be set, and `default` is required - when `headers` is set. + `default` is required by the API and applies to any Vary response header that + does not have a per-header override. """ diff --git a/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py b/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py index 3ed01b1a11e..f89b41371ba 100644 --- a/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py +++ b/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py @@ -265,29 +265,15 @@ class ActionParametersSharedDictionary(TypedDict, total=False): class ActionParametersVaryDefault(TypedDict, total=False): """ - Controls how a single request header (or the default for all headers) contributes to the cache key. + Controls how response Vary headers without a per-header override contribute to the cache key. """ action: Required[Literal["bypass", "passthrough", "normalize"]] """How the header value is treated when building the cache key.""" - languages: SequenceNotStr[str] - """The set of languages to normalize against. - - Only valid for the `accept-language` header. - """ - - media_types: SequenceNotStr[str] - """The set of media types to normalize against. - - Only valid for the `accept` header. - """ - class ActionParametersVaryHeaders(TypedDict, total=False): - """ - Controls how a single request header (or the default for all headers) contributes to the cache key. - """ + """Controls how a single request header contributes to the cache key.""" action: Required[Literal["bypass", "passthrough", "normalize"]] """How the header value is treated when building the cache key.""" @@ -308,13 +294,13 @@ class ActionParametersVaryHeaders(TypedDict, total=False): class ActionParametersVary(TypedDict, total=False): """Controls how cached responses vary based on request headers. - At least one of `default` or `headers` must be set, and `default` is required when `headers` is set. + `default` is required by the API and applies to any Vary response header that does not have a per-header override. """ default: ActionParametersVaryDefault """ - Controls how a single request header (or the default for all headers) - contributes to the cache key. + Controls how response Vary headers without a per-header override contribute to + the cache key. """ headers: Dict[str, ActionParametersVaryHeaders] @@ -399,8 +385,8 @@ class ActionParameters(TypedDict, total=False): vary: ActionParametersVary """Controls how cached responses vary based on request headers. - At least one of `default` or `headers` must be set, and `default` is required - when `headers` is set. + `default` is required by the API and applies to any Vary response header that + does not have a per-header override. """ diff --git a/tests/api_resources/rulesets/test_rules.py b/tests/api_resources/rulesets/test_rules.py index 2b88ce437de..2b107425f97 100644 --- a/tests/api_resources/rulesets/test_rules.py +++ b/tests/api_resources/rulesets/test_rules.py @@ -1689,11 +1689,7 @@ def test_method_create_with_all_params_overload_17(self, client: Cloudflare) -> "strip_last_modified": True, "strip_set_cookie": True, "vary": { - "default": { - "action": "normalize", - "languages": ["en"], - "media_types": ["image/webp"], - }, + "default": {"action": "normalize"}, "headers": { "accept": { "action": "normalize", @@ -4057,11 +4053,7 @@ def test_method_edit_with_all_params_overload_17(self, client: Cloudflare) -> No "strip_last_modified": True, "strip_set_cookie": True, "vary": { - "default": { - "action": "normalize", - "languages": ["en"], - "media_types": ["image/webp"], - }, + "default": {"action": "normalize"}, "headers": { "accept": { "action": "normalize", @@ -6178,11 +6170,7 @@ async def test_method_create_with_all_params_overload_17(self, async_client: Asy "strip_last_modified": True, "strip_set_cookie": True, "vary": { - "default": { - "action": "normalize", - "languages": ["en"], - "media_types": ["image/webp"], - }, + "default": {"action": "normalize"}, "headers": { "accept": { "action": "normalize", @@ -8546,11 +8534,7 @@ async def test_method_edit_with_all_params_overload_17(self, async_client: Async "strip_last_modified": True, "strip_set_cookie": True, "vary": { - "default": { - "action": "normalize", - "languages": ["en"], - "media_types": ["image/webp"], - }, + "default": {"action": "normalize"}, "headers": { "accept": { "action": "normalize", From 335fe0f9342850be9f6ee799e722d1675b9e3156 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 13:58:33 +0000 Subject: [PATCH 23/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index bb82835162b..7e5c5870683 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2412 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-77454b7f4d8a6c1b61e16e7f01c2b7af393011c053722940267b38937660140b.yml -openapi_spec_hash: 5929bc536ceca35c4f3ff7af07094fcb +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-b0abdd4a64b07a83c31a1a448f44c578d98b0409dd0c0984289e2c4bdbd47e60.yml +openapi_spec_hash: dc0cabd7c381a18f0327edbb8d358934 config_hash: a23f4c71ccd6c6117c24f63ae415dcf7 From 583042b6033d9676e3248dc764877c11efaa77fb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:57:53 +0000 Subject: [PATCH 24/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7e5c5870683..298e6790fd3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2412 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-b0abdd4a64b07a83c31a1a448f44c578d98b0409dd0c0984289e2c4bdbd47e60.yml -openapi_spec_hash: dc0cabd7c381a18f0327edbb8d358934 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-aea449e729e8f21233664cd0c798c1be0f085fa8c6f3f09d4106a9e2a8a91ceb.yml +openapi_spec_hash: bacabcf09c5206d41639afc39af8f2ac config_hash: a23f4c71ccd6c6117c24f63ae415dcf7 From e96350d9a2077159ade5a64a072e8e4ef0622ccc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 15:03:44 +0000 Subject: [PATCH 25/29] feat: feat(email_security): Add API to do bulk message movements and quarantine releases * feat(email_security): Add API to do bulk message movements and quarantine releases --- .stats.yml | 6 +- .../resources/email_security/api.md | 44 ++ .../email_security/investigate/__init__.py | 14 + .../investigate/bulk/__init__.py | 47 ++ .../email_security/investigate/bulk/bulk.py | 643 ++++++++++++++++++ .../email_security/investigate/bulk/cancel.py | 197 ++++++ .../investigate/bulk/messages.py | 226 ++++++ .../email_security/investigate/investigate.py | 32 + .../email_security/investigate/__init__.py | 6 + .../investigate/bulk/__init__.py | 7 + .../bulk/cancel_create_response.py | 126 ++++ .../investigate/bulk/message_list_params.py | 20 + .../investigate/bulk/message_list_response.py | 69 ++ .../investigate/bulk_create_params.py | 73 ++ .../investigate/bulk_create_response.py | 126 ++++ .../investigate/bulk_delete_response.py | 9 + .../investigate/bulk_get_response.py | 126 ++++ .../investigate/bulk_list_params.py | 22 + .../investigate/bulk_list_response.py | 126 ++++ .../investigate/bulk/__init__.py | 1 + .../investigate/bulk/test_cancel.py | 120 ++++ .../investigate/bulk/test_messages.py | 143 ++++ .../email_security/investigate/test_bulk.py | 473 +++++++++++++ 23 files changed, 2653 insertions(+), 3 deletions(-) create mode 100644 src/cloudflare/resources/email_security/investigate/bulk/__init__.py create mode 100644 src/cloudflare/resources/email_security/investigate/bulk/bulk.py create mode 100644 src/cloudflare/resources/email_security/investigate/bulk/cancel.py create mode 100644 src/cloudflare/resources/email_security/investigate/bulk/messages.py create mode 100644 src/cloudflare/types/email_security/investigate/bulk/__init__.py create mode 100644 src/cloudflare/types/email_security/investigate/bulk/cancel_create_response.py create mode 100644 src/cloudflare/types/email_security/investigate/bulk/message_list_params.py create mode 100644 src/cloudflare/types/email_security/investigate/bulk/message_list_response.py create mode 100644 src/cloudflare/types/email_security/investigate/bulk_create_params.py create mode 100644 src/cloudflare/types/email_security/investigate/bulk_create_response.py create mode 100644 src/cloudflare/types/email_security/investigate/bulk_delete_response.py create mode 100644 src/cloudflare/types/email_security/investigate/bulk_get_response.py create mode 100644 src/cloudflare/types/email_security/investigate/bulk_list_params.py create mode 100644 src/cloudflare/types/email_security/investigate/bulk_list_response.py create mode 100644 tests/api_resources/email_security/investigate/bulk/__init__.py create mode 100644 tests/api_resources/email_security/investigate/bulk/test_cancel.py create mode 100644 tests/api_resources/email_security/investigate/bulk/test_messages.py create mode 100644 tests/api_resources/email_security/investigate/test_bulk.py diff --git a/.stats.yml b/.stats.yml index 298e6790fd3..206e0199837 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 2412 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-aea449e729e8f21233664cd0c798c1be0f085fa8c6f3f09d4106a9e2a8a91ceb.yml +configured_endpoints: 2418 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-390a1858f30b8d927f55b608de32c0af9117819b1b17ef35376447cd537b91ae.yml openapi_spec_hash: bacabcf09c5206d41639afc39af8f2ac -config_hash: a23f4c71ccd6c6117c24f63ae415dcf7 +config_hash: cd80863b2a094f3805409e4a5676b0b8 diff --git a/src/cloudflare/resources/email_security/api.md b/src/cloudflare/resources/email_security/api.md index 9ee4ed96b7b..fba445ca28d 100644 --- a/src/cloudflare/resources/email_security/api.md +++ b/src/cloudflare/resources/email_security/api.md @@ -93,6 +93,50 @@ Methods: - client.email_security.investigate.release.bulk(\*, account_id, \*\*params) -> SyncSinglePage[ReleaseBulkResponse] +### Bulk + +Types: + +```python +from cloudflare.types.email_security.investigate import ( + BulkCreateResponse, + BulkListResponse, + BulkDeleteResponse, + BulkGetResponse, +) +``` + +Methods: + +- client.email_security.investigate.bulk.create(\*, account_id, \*\*params) -> BulkCreateResponse +- client.email_security.investigate.bulk.list(\*, account_id, \*\*params) -> SyncV4PagePaginationArray[BulkListResponse] +- client.email_security.investigate.bulk.delete(job_id, \*, account_id) -> BulkDeleteResponse +- client.email_security.investigate.bulk.get(job_id, \*, account_id) -> BulkGetResponse + +#### Cancel + +Types: + +```python +from cloudflare.types.email_security.investigate.bulk import CancelCreateResponse +``` + +Methods: + +- client.email_security.investigate.bulk.cancel.create(job_id, \*, account_id) -> CancelCreateResponse + +#### Messages + +Types: + +```python +from cloudflare.types.email_security.investigate.bulk import MessageListResponse +``` + +Methods: + +- client.email_security.investigate.bulk.messages.list(job_id, \*, account_id, \*\*params) -> SyncV4PagePaginationArray[MessageListResponse] + ## Phishguard ### Reports diff --git a/src/cloudflare/resources/email_security/investigate/__init__.py b/src/cloudflare/resources/email_security/investigate/__init__.py index 5b206205771..e3209aa1179 100644 --- a/src/cloudflare/resources/email_security/investigate/__init__.py +++ b/src/cloudflare/resources/email_security/investigate/__init__.py @@ -8,6 +8,14 @@ RawResourceWithStreamingResponse, AsyncRawResourceWithStreamingResponse, ) +from .bulk import ( + BulkResource, + AsyncBulkResource, + BulkResourceWithRawResponse, + AsyncBulkResourceWithRawResponse, + BulkResourceWithStreamingResponse, + AsyncBulkResourceWithStreamingResponse, +) from .move import ( MoveResource, AsyncMoveResource, @@ -108,6 +116,12 @@ "AsyncReleaseResourceWithRawResponse", "ReleaseResourceWithStreamingResponse", "AsyncReleaseResourceWithStreamingResponse", + "BulkResource", + "AsyncBulkResource", + "BulkResourceWithRawResponse", + "AsyncBulkResourceWithRawResponse", + "BulkResourceWithStreamingResponse", + "AsyncBulkResourceWithStreamingResponse", "InvestigateResource", "AsyncInvestigateResource", "InvestigateResourceWithRawResponse", diff --git a/src/cloudflare/resources/email_security/investigate/bulk/__init__.py b/src/cloudflare/resources/email_security/investigate/bulk/__init__.py new file mode 100644 index 00000000000..679d6bf3f6b --- /dev/null +++ b/src/cloudflare/resources/email_security/investigate/bulk/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .bulk import ( + BulkResource, + AsyncBulkResource, + BulkResourceWithRawResponse, + AsyncBulkResourceWithRawResponse, + BulkResourceWithStreamingResponse, + AsyncBulkResourceWithStreamingResponse, +) +from .cancel import ( + CancelResource, + AsyncCancelResource, + CancelResourceWithRawResponse, + AsyncCancelResourceWithRawResponse, + CancelResourceWithStreamingResponse, + AsyncCancelResourceWithStreamingResponse, +) +from .messages import ( + MessagesResource, + AsyncMessagesResource, + MessagesResourceWithRawResponse, + AsyncMessagesResourceWithRawResponse, + MessagesResourceWithStreamingResponse, + AsyncMessagesResourceWithStreamingResponse, +) + +__all__ = [ + "CancelResource", + "AsyncCancelResource", + "CancelResourceWithRawResponse", + "AsyncCancelResourceWithRawResponse", + "CancelResourceWithStreamingResponse", + "AsyncCancelResourceWithStreamingResponse", + "MessagesResource", + "AsyncMessagesResource", + "MessagesResourceWithRawResponse", + "AsyncMessagesResourceWithRawResponse", + "MessagesResourceWithStreamingResponse", + "AsyncMessagesResourceWithStreamingResponse", + "BulkResource", + "AsyncBulkResource", + "BulkResourceWithRawResponse", + "AsyncBulkResourceWithRawResponse", + "BulkResourceWithStreamingResponse", + "AsyncBulkResourceWithStreamingResponse", +] diff --git a/src/cloudflare/resources/email_security/investigate/bulk/bulk.py b/src/cloudflare/resources/email_security/investigate/bulk/bulk.py new file mode 100644 index 00000000000..f4c62074808 --- /dev/null +++ b/src/cloudflare/resources/email_security/investigate/bulk/bulk.py @@ -0,0 +1,643 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Type, Optional, cast +from typing_extensions import Literal + +import httpx + +from .cancel import ( + CancelResource, + AsyncCancelResource, + CancelResourceWithRawResponse, + AsyncCancelResourceWithRawResponse, + CancelResourceWithStreamingResponse, + AsyncCancelResourceWithStreamingResponse, +) +from .messages import ( + MessagesResource, + AsyncMessagesResource, + MessagesResourceWithRawResponse, + AsyncMessagesResourceWithRawResponse, + MessagesResourceWithStreamingResponse, + AsyncMessagesResourceWithStreamingResponse, +) +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....._wrappers import ResultWrapper +from .....pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray +from ....._base_client import AsyncPaginator, make_request_options +from .....types.email_security.investigate import bulk_list_params, bulk_create_params +from .....types.email_security.investigate.bulk_get_response import BulkGetResponse +from .....types.email_security.investigate.bulk_list_response import BulkListResponse +from .....types.email_security.investigate.bulk_create_response import BulkCreateResponse +from .....types.email_security.investigate.bulk_delete_response import BulkDeleteResponse + +__all__ = ["BulkResource", "AsyncBulkResource"] + + +class BulkResource(SyncAPIResource): + @cached_property + def cancel(self) -> CancelResource: + return CancelResource(self._client) + + @cached_property + def messages(self) -> MessagesResource: + return MessagesResource(self._client) + + @cached_property + def with_raw_response(self) -> BulkResourceWithRawResponse: + """ + 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 BulkResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BulkResourceWithStreamingResponse: + """ + 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 BulkResourceWithStreamingResponse(self) + + def create( + self, + *, + account_id: str, + action: Literal["MOVE", "RELEASE"], + search_params: bulk_create_params.SearchParams, + comment: Optional[str] | Omit = omit, + destination: Literal[ + "Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges" + ] + | Omit = omit, + expected_disposition: Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + | Omit = omit, + # 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, + ) -> BulkCreateResponse: + """ + Create a bulk action job + + Args: + account_id: Identifier. + + 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}") + return self._post( + path_template("/accounts/{account_id}/email-security/investigate/bulk", account_id=account_id), + body=maybe_transform( + { + "action": action, + "search_params": search_params, + "comment": comment, + "destination": destination, + "expected_disposition": expected_disposition, + }, + bulk_create_params.BulkCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[BulkCreateResponse]._unwrapper, + ), + cast_to=cast(Type[BulkCreateResponse], ResultWrapper[BulkCreateResponse]), + ) + + def list( + self, + *, + account_id: str, + action_type: Literal["MOVE", "RELEASE"] | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + status: Literal["PENDING", "DISCOVERING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "SKIPPED"] + | Omit = omit, + # 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, + ) -> SyncV4PagePaginationArray[BulkListResponse]: + """ + List bulk action jobs + + Args: + account_id: Identifier. + + page: Current page within paginated list of results. + + per_page: The number of results per page. Maximum value is 1000. + + 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}") + return self._get_api_list( + path_template("/accounts/{account_id}/email-security/investigate/bulk", account_id=account_id), + page=SyncV4PagePaginationArray[BulkListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "action_type": action_type, + "page": page, + "per_page": per_page, + "status": status, + }, + bulk_list_params.BulkListParams, + ), + ), + model=BulkListResponse, + ) + + def delete( + self, + job_id: str, + *, + account_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, + ) -> BulkDeleteResponse: + """Soft-deletes the job, hiding it from all list and detail endpoints. + + Only jobs in + a terminal state (`COMPLETED`, `CANCELLED`, `FAILED`, or `SKIPPED`) can be + deleted. To stop an in-progress job without removing it, use the cancel endpoint + instead. + + Args: + account_id: Identifier. + + 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 job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return self._delete( + path_template( + "/accounts/{account_id}/email-security/investigate/bulk/{job_id}", account_id=account_id, job_id=job_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[BulkDeleteResponse]._unwrapper, + ), + cast_to=cast(Type[BulkDeleteResponse], ResultWrapper[BulkDeleteResponse]), + ) + + def get( + self, + job_id: str, + *, + account_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, + ) -> BulkGetResponse: + """ + Get bulk action job details + + Args: + account_id: Identifier. + + 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 job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return self._get( + path_template( + "/accounts/{account_id}/email-security/investigate/bulk/{job_id}", account_id=account_id, job_id=job_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[BulkGetResponse]._unwrapper, + ), + cast_to=cast(Type[BulkGetResponse], ResultWrapper[BulkGetResponse]), + ) + + +class AsyncBulkResource(AsyncAPIResource): + @cached_property + def cancel(self) -> AsyncCancelResource: + return AsyncCancelResource(self._client) + + @cached_property + def messages(self) -> AsyncMessagesResource: + return AsyncMessagesResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncBulkResourceWithRawResponse: + """ + 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 AsyncBulkResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBulkResourceWithStreamingResponse: + """ + 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 AsyncBulkResourceWithStreamingResponse(self) + + async def create( + self, + *, + account_id: str, + action: Literal["MOVE", "RELEASE"], + search_params: bulk_create_params.SearchParams, + comment: Optional[str] | Omit = omit, + destination: Literal[ + "Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges" + ] + | Omit = omit, + expected_disposition: Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + | Omit = omit, + # 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, + ) -> BulkCreateResponse: + """ + Create a bulk action job + + Args: + account_id: Identifier. + + 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}") + return await self._post( + path_template("/accounts/{account_id}/email-security/investigate/bulk", account_id=account_id), + body=await async_maybe_transform( + { + "action": action, + "search_params": search_params, + "comment": comment, + "destination": destination, + "expected_disposition": expected_disposition, + }, + bulk_create_params.BulkCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[BulkCreateResponse]._unwrapper, + ), + cast_to=cast(Type[BulkCreateResponse], ResultWrapper[BulkCreateResponse]), + ) + + def list( + self, + *, + account_id: str, + action_type: Literal["MOVE", "RELEASE"] | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + status: Literal["PENDING", "DISCOVERING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "SKIPPED"] + | Omit = omit, + # 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, + ) -> AsyncPaginator[BulkListResponse, AsyncV4PagePaginationArray[BulkListResponse]]: + """ + List bulk action jobs + + Args: + account_id: Identifier. + + page: Current page within paginated list of results. + + per_page: The number of results per page. Maximum value is 1000. + + 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}") + return self._get_api_list( + path_template("/accounts/{account_id}/email-security/investigate/bulk", account_id=account_id), + page=AsyncV4PagePaginationArray[BulkListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "action_type": action_type, + "page": page, + "per_page": per_page, + "status": status, + }, + bulk_list_params.BulkListParams, + ), + ), + model=BulkListResponse, + ) + + async def delete( + self, + job_id: str, + *, + account_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, + ) -> BulkDeleteResponse: + """Soft-deletes the job, hiding it from all list and detail endpoints. + + Only jobs in + a terminal state (`COMPLETED`, `CANCELLED`, `FAILED`, or `SKIPPED`) can be + deleted. To stop an in-progress job without removing it, use the cancel endpoint + instead. + + Args: + account_id: Identifier. + + 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 job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return await self._delete( + path_template( + "/accounts/{account_id}/email-security/investigate/bulk/{job_id}", account_id=account_id, job_id=job_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[BulkDeleteResponse]._unwrapper, + ), + cast_to=cast(Type[BulkDeleteResponse], ResultWrapper[BulkDeleteResponse]), + ) + + async def get( + self, + job_id: str, + *, + account_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, + ) -> BulkGetResponse: + """ + Get bulk action job details + + Args: + account_id: Identifier. + + 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 job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return await self._get( + path_template( + "/accounts/{account_id}/email-security/investigate/bulk/{job_id}", account_id=account_id, job_id=job_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[BulkGetResponse]._unwrapper, + ), + cast_to=cast(Type[BulkGetResponse], ResultWrapper[BulkGetResponse]), + ) + + +class BulkResourceWithRawResponse: + def __init__(self, bulk: BulkResource) -> None: + self._bulk = bulk + + self.create = to_raw_response_wrapper( + bulk.create, + ) + self.list = to_raw_response_wrapper( + bulk.list, + ) + self.delete = to_raw_response_wrapper( + bulk.delete, + ) + self.get = to_raw_response_wrapper( + bulk.get, + ) + + @cached_property + def cancel(self) -> CancelResourceWithRawResponse: + return CancelResourceWithRawResponse(self._bulk.cancel) + + @cached_property + def messages(self) -> MessagesResourceWithRawResponse: + return MessagesResourceWithRawResponse(self._bulk.messages) + + +class AsyncBulkResourceWithRawResponse: + def __init__(self, bulk: AsyncBulkResource) -> None: + self._bulk = bulk + + self.create = async_to_raw_response_wrapper( + bulk.create, + ) + self.list = async_to_raw_response_wrapper( + bulk.list, + ) + self.delete = async_to_raw_response_wrapper( + bulk.delete, + ) + self.get = async_to_raw_response_wrapper( + bulk.get, + ) + + @cached_property + def cancel(self) -> AsyncCancelResourceWithRawResponse: + return AsyncCancelResourceWithRawResponse(self._bulk.cancel) + + @cached_property + def messages(self) -> AsyncMessagesResourceWithRawResponse: + return AsyncMessagesResourceWithRawResponse(self._bulk.messages) + + +class BulkResourceWithStreamingResponse: + def __init__(self, bulk: BulkResource) -> None: + self._bulk = bulk + + self.create = to_streamed_response_wrapper( + bulk.create, + ) + self.list = to_streamed_response_wrapper( + bulk.list, + ) + self.delete = to_streamed_response_wrapper( + bulk.delete, + ) + self.get = to_streamed_response_wrapper( + bulk.get, + ) + + @cached_property + def cancel(self) -> CancelResourceWithStreamingResponse: + return CancelResourceWithStreamingResponse(self._bulk.cancel) + + @cached_property + def messages(self) -> MessagesResourceWithStreamingResponse: + return MessagesResourceWithStreamingResponse(self._bulk.messages) + + +class AsyncBulkResourceWithStreamingResponse: + def __init__(self, bulk: AsyncBulkResource) -> None: + self._bulk = bulk + + self.create = async_to_streamed_response_wrapper( + bulk.create, + ) + self.list = async_to_streamed_response_wrapper( + bulk.list, + ) + self.delete = async_to_streamed_response_wrapper( + bulk.delete, + ) + self.get = async_to_streamed_response_wrapper( + bulk.get, + ) + + @cached_property + def cancel(self) -> AsyncCancelResourceWithStreamingResponse: + return AsyncCancelResourceWithStreamingResponse(self._bulk.cancel) + + @cached_property + def messages(self) -> AsyncMessagesResourceWithStreamingResponse: + return AsyncMessagesResourceWithStreamingResponse(self._bulk.messages) diff --git a/src/cloudflare/resources/email_security/investigate/bulk/cancel.py b/src/cloudflare/resources/email_security/investigate/bulk/cancel.py new file mode 100644 index 00000000000..eaea489bf0f --- /dev/null +++ b/src/cloudflare/resources/email_security/investigate/bulk/cancel.py @@ -0,0 +1,197 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Type, cast + +import httpx + +from ....._types import Body, Query, Headers, NotGiven, not_given +from ....._utils import path_template +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....._wrappers import ResultWrapper +from ....._base_client import make_request_options +from .....types.email_security.investigate.bulk.cancel_create_response import CancelCreateResponse + +__all__ = ["CancelResource", "AsyncCancelResource"] + + +class CancelResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CancelResourceWithRawResponse: + """ + 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 CancelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CancelResourceWithStreamingResponse: + """ + 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 CancelResourceWithStreamingResponse(self) + + def create( + self, + job_id: str, + *, + account_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, + ) -> CancelCreateResponse: + """Marks the job as cancelled and stops any pending message processing. + + The job + record remains visible in list and detail endpoints. + + Args: + account_id: Identifier. + + 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 job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return self._post( + path_template( + "/accounts/{account_id}/email-security/investigate/bulk/{job_id}/cancel", + account_id=account_id, + job_id=job_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[CancelCreateResponse]._unwrapper, + ), + cast_to=cast(Type[CancelCreateResponse], ResultWrapper[CancelCreateResponse]), + ) + + +class AsyncCancelResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCancelResourceWithRawResponse: + """ + 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 AsyncCancelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCancelResourceWithStreamingResponse: + """ + 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 AsyncCancelResourceWithStreamingResponse(self) + + async def create( + self, + job_id: str, + *, + account_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, + ) -> CancelCreateResponse: + """Marks the job as cancelled and stops any pending message processing. + + The job + record remains visible in list and detail endpoints. + + Args: + account_id: Identifier. + + 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 job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return await self._post( + path_template( + "/accounts/{account_id}/email-security/investigate/bulk/{job_id}/cancel", + account_id=account_id, + job_id=job_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[CancelCreateResponse]._unwrapper, + ), + cast_to=cast(Type[CancelCreateResponse], ResultWrapper[CancelCreateResponse]), + ) + + +class CancelResourceWithRawResponse: + def __init__(self, cancel: CancelResource) -> None: + self._cancel = cancel + + self.create = to_raw_response_wrapper( + cancel.create, + ) + + +class AsyncCancelResourceWithRawResponse: + def __init__(self, cancel: AsyncCancelResource) -> None: + self._cancel = cancel + + self.create = async_to_raw_response_wrapper( + cancel.create, + ) + + +class CancelResourceWithStreamingResponse: + def __init__(self, cancel: CancelResource) -> None: + self._cancel = cancel + + self.create = to_streamed_response_wrapper( + cancel.create, + ) + + +class AsyncCancelResourceWithStreamingResponse: + def __init__(self, cancel: AsyncCancelResource) -> None: + self._cancel = cancel + + self.create = async_to_streamed_response_wrapper( + cancel.create, + ) diff --git a/src/cloudflare/resources/email_security/investigate/bulk/messages.py b/src/cloudflare/resources/email_security/investigate/bulk/messages.py new file mode 100644 index 00000000000..84223efb0b4 --- /dev/null +++ b/src/cloudflare/resources/email_security/investigate/bulk/messages.py @@ -0,0 +1,226 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, 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 .....pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray +from ....._base_client import AsyncPaginator, make_request_options +from .....types.email_security.investigate.bulk import message_list_params +from .....types.email_security.investigate.bulk.message_list_response import MessageListResponse + +__all__ = ["MessagesResource", "AsyncMessagesResource"] + + +class MessagesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> MessagesResourceWithRawResponse: + """ + 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 MessagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> MessagesResourceWithStreamingResponse: + """ + 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 MessagesResourceWithStreamingResponse(self) + + def list( + self, + job_id: str, + *, + account_id: str, + page: int | Omit = omit, + per_page: int | Omit = omit, + status: Literal["PENDING", "DISCOVERING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "SKIPPED"] + | Omit = omit, + # 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, + ) -> SyncV4PagePaginationArray[MessageListResponse]: + """ + List messages for a bulk action job + + Args: + account_id: Identifier. + + page: Current page within paginated list of results. + + per_page: The number of results per page. Maximum value is 1000. + + 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 job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return self._get_api_list( + path_template( + "/accounts/{account_id}/email-security/investigate/bulk/{job_id}/messages", + account_id=account_id, + job_id=job_id, + ), + page=SyncV4PagePaginationArray[MessageListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + "status": status, + }, + message_list_params.MessageListParams, + ), + ), + model=MessageListResponse, + ) + + +class AsyncMessagesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncMessagesResourceWithRawResponse: + """ + 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 AsyncMessagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncMessagesResourceWithStreamingResponse: + """ + 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 AsyncMessagesResourceWithStreamingResponse(self) + + def list( + self, + job_id: str, + *, + account_id: str, + page: int | Omit = omit, + per_page: int | Omit = omit, + status: Literal["PENDING", "DISCOVERING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "SKIPPED"] + | Omit = omit, + # 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, + ) -> AsyncPaginator[MessageListResponse, AsyncV4PagePaginationArray[MessageListResponse]]: + """ + List messages for a bulk action job + + Args: + account_id: Identifier. + + page: Current page within paginated list of results. + + per_page: The number of results per page. Maximum value is 1000. + + 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 job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return self._get_api_list( + path_template( + "/accounts/{account_id}/email-security/investigate/bulk/{job_id}/messages", + account_id=account_id, + job_id=job_id, + ), + page=AsyncV4PagePaginationArray[MessageListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + "status": status, + }, + message_list_params.MessageListParams, + ), + ), + model=MessageListResponse, + ) + + +class MessagesResourceWithRawResponse: + def __init__(self, messages: MessagesResource) -> None: + self._messages = messages + + self.list = to_raw_response_wrapper( + messages.list, + ) + + +class AsyncMessagesResourceWithRawResponse: + def __init__(self, messages: AsyncMessagesResource) -> None: + self._messages = messages + + self.list = async_to_raw_response_wrapper( + messages.list, + ) + + +class MessagesResourceWithStreamingResponse: + def __init__(self, messages: MessagesResource) -> None: + self._messages = messages + + self.list = to_streamed_response_wrapper( + messages.list, + ) + + +class AsyncMessagesResourceWithStreamingResponse: + def __init__(self, messages: AsyncMessagesResource) -> None: + self._messages = messages + + self.list = async_to_streamed_response_wrapper( + messages.list, + ) diff --git a/src/cloudflare/resources/email_security/investigate/investigate.py b/src/cloudflare/resources/email_security/investigate/investigate.py index 6a06fdf611b..0ede93136fc 100644 --- a/src/cloudflare/resources/email_security/investigate/investigate.py +++ b/src/cloudflare/resources/email_security/investigate/investigate.py @@ -50,6 +50,14 @@ ) from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import path_template, maybe_transform, async_maybe_transform +from .bulk.bulk import ( + BulkResource, + AsyncBulkResource, + BulkResourceWithRawResponse, + AsyncBulkResourceWithRawResponse, + BulkResourceWithStreamingResponse, + AsyncBulkResourceWithStreamingResponse, +) from ...._compat import cached_property from .detections import ( DetectionsResource, @@ -113,6 +121,10 @@ def reclassify(self) -> ReclassifyResource: def release(self) -> ReleaseResource: return ReleaseResource(self._client) + @cached_property + def bulk(self) -> BulkResource: + return BulkResource(self._client) + @cached_property def with_raw_response(self) -> InvestigateResourceWithRawResponse: """ @@ -318,6 +330,10 @@ def reclassify(self) -> AsyncReclassifyResource: def release(self) -> AsyncReleaseResource: return AsyncReleaseResource(self._client) + @cached_property + def bulk(self) -> AsyncBulkResource: + return AsyncBulkResource(self._client) + @cached_property def with_raw_response(self) -> AsyncInvestigateResourceWithRawResponse: """ @@ -535,6 +551,10 @@ def reclassify(self) -> ReclassifyResourceWithRawResponse: def release(self) -> ReleaseResourceWithRawResponse: return ReleaseResourceWithRawResponse(self._investigate.release) + @cached_property + def bulk(self) -> BulkResourceWithRawResponse: + return BulkResourceWithRawResponse(self._investigate.bulk) + class AsyncInvestigateResourceWithRawResponse: def __init__(self, investigate: AsyncInvestigateResource) -> None: @@ -575,6 +595,10 @@ def reclassify(self) -> AsyncReclassifyResourceWithRawResponse: def release(self) -> AsyncReleaseResourceWithRawResponse: return AsyncReleaseResourceWithRawResponse(self._investigate.release) + @cached_property + def bulk(self) -> AsyncBulkResourceWithRawResponse: + return AsyncBulkResourceWithRawResponse(self._investigate.bulk) + class InvestigateResourceWithStreamingResponse: def __init__(self, investigate: InvestigateResource) -> None: @@ -615,6 +639,10 @@ def reclassify(self) -> ReclassifyResourceWithStreamingResponse: def release(self) -> ReleaseResourceWithStreamingResponse: return ReleaseResourceWithStreamingResponse(self._investigate.release) + @cached_property + def bulk(self) -> BulkResourceWithStreamingResponse: + return BulkResourceWithStreamingResponse(self._investigate.bulk) + class AsyncInvestigateResourceWithStreamingResponse: def __init__(self, investigate: AsyncInvestigateResource) -> None: @@ -654,3 +682,7 @@ def reclassify(self) -> AsyncReclassifyResourceWithStreamingResponse: @cached_property def release(self) -> AsyncReleaseResourceWithStreamingResponse: return AsyncReleaseResourceWithStreamingResponse(self._investigate.release) + + @cached_property + def bulk(self) -> AsyncBulkResourceWithStreamingResponse: + return AsyncBulkResourceWithStreamingResponse(self._investigate.bulk) diff --git a/src/cloudflare/types/email_security/investigate/__init__.py b/src/cloudflare/types/email_security/investigate/__init__.py index e694dd47b9b..bdc2a1528ea 100644 --- a/src/cloudflare/types/email_security/investigate/__init__.py +++ b/src/cloudflare/types/email_security/investigate/__init__.py @@ -2,12 +2,18 @@ from __future__ import annotations +from .bulk_list_params import BulkListParams as BulkListParams from .move_bulk_params import MoveBulkParams as MoveBulkParams from .raw_get_response import RawGetResponse as RawGetResponse +from .bulk_get_response import BulkGetResponse as BulkGetResponse +from .bulk_create_params import BulkCreateParams as BulkCreateParams +from .bulk_list_response import BulkListResponse as BulkListResponse from .move_bulk_response import MoveBulkResponse as MoveBulkResponse from .move_create_params import MoveCreateParams as MoveCreateParams from .trace_get_response import TraceGetResponse as TraceGetResponse from .release_bulk_params import ReleaseBulkParams as ReleaseBulkParams +from .bulk_create_response import BulkCreateResponse as BulkCreateResponse +from .bulk_delete_response import BulkDeleteResponse as BulkDeleteResponse from .move_create_response import MoveCreateResponse as MoveCreateResponse from .preview_get_response import PreviewGetResponse as PreviewGetResponse from .preview_create_params import PreviewCreateParams as PreviewCreateParams diff --git a/src/cloudflare/types/email_security/investigate/bulk/__init__.py b/src/cloudflare/types/email_security/investigate/bulk/__init__.py new file mode 100644 index 00000000000..fb1770e63d3 --- /dev/null +++ b/src/cloudflare/types/email_security/investigate/bulk/__init__.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .message_list_params import MessageListParams as MessageListParams +from .message_list_response import MessageListResponse as MessageListResponse +from .cancel_create_response import CancelCreateResponse as CancelCreateResponse diff --git a/src/cloudflare/types/email_security/investigate/bulk/cancel_create_response.py b/src/cloudflare/types/email_security/investigate/bulk/cancel_create_response.py new file mode 100644 index 00000000000..bacf4125cfd --- /dev/null +++ b/src/cloudflare/types/email_security/investigate/bulk/cancel_create_response.py @@ -0,0 +1,126 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from ....._utils import PropertyInfo +from ....._models import BaseModel + +__all__ = ["CancelCreateResponse", "ActionParams", "ActionParamsMove", "ActionParamsRelease", "SearchParams"] + + +class ActionParamsMove(BaseModel): + destination: Literal["Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"] + + type: Literal["MOVE"] + + expected_disposition: Optional[ + Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + ] = None + + +class ActionParamsRelease(BaseModel): + type: Literal["RELEASE"] + + +ActionParams: TypeAlias = Annotated[Union[ActionParamsMove, ActionParamsRelease], PropertyInfo(discriminator="type")] + + +class SearchParams(BaseModel): + action_log: Optional[bool] = None + """Deprecated, use `GET /investigate/{investigate_id}/action_log` instead. + + End of life: November 1, 2026. + """ + + alert_id: Optional[str] = None + + delivery_status: Optional[ + Literal["delivered", "moved", "quarantined", "rejected", "deferred", "bounced", "queued"] + ] = None + """Delivery status of the message.""" + + detections_only: Optional[bool] = None + + domain: Optional[str] = None + + end: Optional[datetime] = None + """End of search date range""" + + exact_subject: Optional[str] = None + + final_disposition: Optional[ + Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + ] = None + + message_action: Optional[Literal["PREVIEW", "QUARANTINE_RELEASED", "MOVED"]] = None + + message_id: Optional[str] = None + + metric: Optional[str] = None + + query: Optional[str] = None + + recipient: Optional[str] = None + + sender: Optional[str] = None + + start: Optional[datetime] = None + """Beginning of search date range""" + + subject: Optional[str] = None + + submissions: Optional[bool] = None + + +class CancelCreateResponse(BaseModel): + action_params: ActionParams + + action_type: Literal["MOVE", "RELEASE"] + + created_at: datetime + + job_id: str + + messages_failed: int + + messages_pending: int + + messages_successful: int + + search_params: SearchParams + + status: Literal["PENDING", "DISCOVERING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "SKIPPED"] + + total_messages_discovered: int + + comment: Optional[str] = None + + completed_at: Optional[datetime] = None + + started_at: Optional[datetime] = None + + status_message: Optional[str] = None diff --git a/src/cloudflare/types/email_security/investigate/bulk/message_list_params.py b/src/cloudflare/types/email_security/investigate/bulk/message_list_params.py new file mode 100644 index 00000000000..f077cccb2ea --- /dev/null +++ b/src/cloudflare/types/email_security/investigate/bulk/message_list_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 + +__all__ = ["MessageListParams"] + + +class MessageListParams(TypedDict, total=False): + account_id: Required[str] + """Identifier.""" + + page: int + """Current page within paginated list of results.""" + + per_page: int + """The number of results per page. Maximum value is 1000.""" + + status: Literal["PENDING", "DISCOVERING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "SKIPPED"] diff --git a/src/cloudflare/types/email_security/investigate/bulk/message_list_response.py b/src/cloudflare/types/email_security/investigate/bulk/message_list_response.py new file mode 100644 index 00000000000..03cd5c98f77 --- /dev/null +++ b/src/cloudflare/types/email_security/investigate/bulk/message_list_response.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from ....._utils import PropertyInfo +from ....._models import BaseModel + +__all__ = ["MessageListResponse", "ActionParams", "ActionParamsMove", "ActionParamsRelease"] + + +class ActionParamsMove(BaseModel): + client_recipient: str + + destination: Literal["Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"] + + type: Literal["MOVE"] + + expected_disposition: Optional[ + Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + ] = None + + +class ActionParamsRelease(BaseModel): + client_recipient: str + + type: Literal["RELEASE"] + + +ActionParams: TypeAlias = Annotated[Union[ActionParamsMove, ActionParamsRelease], PropertyInfo(discriminator="type")] + + +class MessageListResponse(BaseModel): + action_params: ActionParams + + action_type: Literal["MOVE", "RELEASE"] + + created_at: datetime + + message_id: str + + postfix_id: str + + retry_count: int + + status: Literal["PENDING", "DISCOVERING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "SKIPPED"] + + alert_id: Optional[str] = None + + email_message_id: Optional[str] = None + + processed_at: Optional[datetime] = None + + retry_after: Optional[datetime] = None + """When to retry the action if it failed""" + + status_message: Optional[str] = None diff --git a/src/cloudflare/types/email_security/investigate/bulk_create_params.py b/src/cloudflare/types/email_security/investigate/bulk_create_params.py new file mode 100644 index 00000000000..ee542cff107 --- /dev/null +++ b/src/cloudflare/types/email_security/investigate/bulk_create_params.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ...._utils import PropertyInfo + +__all__ = ["BulkCreateParams", "SearchParams"] + + +class BulkCreateParams(TypedDict, total=False): + account_id: Required[str] + """Identifier.""" + + action: Required[Literal["MOVE", "RELEASE"]] + + search_params: Required[SearchParams] + + comment: Optional[str] + + destination: Literal["Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"] + + expected_disposition: Literal[ + "MALICIOUS", "MALICIOUS-BEC", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "ENCRYPTED", "EXTERNAL", "UNKNOWN", "NONE" + ] + + +class SearchParams(TypedDict, total=False): + action_log: bool + """Deprecated, use `GET /investigate/{investigate_id}/action_log` instead. + + End of life: November 1, 2026. + """ + + alert_id: Optional[str] + + delivery_status: Literal["delivered", "moved", "quarantined", "rejected", "deferred", "bounced", "queued"] + """Delivery status of the message.""" + + detections_only: bool + + domain: Optional[str] + + end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """End of search date range""" + + exact_subject: Optional[str] + + final_disposition: Literal[ + "MALICIOUS", "MALICIOUS-BEC", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "ENCRYPTED", "EXTERNAL", "UNKNOWN", "NONE" + ] + + message_action: Optional[Literal["PREVIEW", "QUARANTINE_RELEASED", "MOVED"]] + + message_id: Optional[str] + + metric: Optional[str] + + query: Optional[str] + + recipient: Optional[str] + + sender: Optional[str] + + start: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Beginning of search date range""" + + subject: Optional[str] + + submissions: bool diff --git a/src/cloudflare/types/email_security/investigate/bulk_create_response.py b/src/cloudflare/types/email_security/investigate/bulk_create_response.py new file mode 100644 index 00000000000..88d1d25eb8a --- /dev/null +++ b/src/cloudflare/types/email_security/investigate/bulk_create_response.py @@ -0,0 +1,126 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = ["BulkCreateResponse", "ActionParams", "ActionParamsMove", "ActionParamsRelease", "SearchParams"] + + +class ActionParamsMove(BaseModel): + destination: Literal["Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"] + + type: Literal["MOVE"] + + expected_disposition: Optional[ + Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + ] = None + + +class ActionParamsRelease(BaseModel): + type: Literal["RELEASE"] + + +ActionParams: TypeAlias = Annotated[Union[ActionParamsMove, ActionParamsRelease], PropertyInfo(discriminator="type")] + + +class SearchParams(BaseModel): + action_log: Optional[bool] = None + """Deprecated, use `GET /investigate/{investigate_id}/action_log` instead. + + End of life: November 1, 2026. + """ + + alert_id: Optional[str] = None + + delivery_status: Optional[ + Literal["delivered", "moved", "quarantined", "rejected", "deferred", "bounced", "queued"] + ] = None + """Delivery status of the message.""" + + detections_only: Optional[bool] = None + + domain: Optional[str] = None + + end: Optional[datetime] = None + """End of search date range""" + + exact_subject: Optional[str] = None + + final_disposition: Optional[ + Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + ] = None + + message_action: Optional[Literal["PREVIEW", "QUARANTINE_RELEASED", "MOVED"]] = None + + message_id: Optional[str] = None + + metric: Optional[str] = None + + query: Optional[str] = None + + recipient: Optional[str] = None + + sender: Optional[str] = None + + start: Optional[datetime] = None + """Beginning of search date range""" + + subject: Optional[str] = None + + submissions: Optional[bool] = None + + +class BulkCreateResponse(BaseModel): + action_params: ActionParams + + action_type: Literal["MOVE", "RELEASE"] + + created_at: datetime + + job_id: str + + messages_failed: int + + messages_pending: int + + messages_successful: int + + search_params: SearchParams + + status: Literal["PENDING", "DISCOVERING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "SKIPPED"] + + total_messages_discovered: int + + comment: Optional[str] = None + + completed_at: Optional[datetime] = None + + started_at: Optional[datetime] = None + + status_message: Optional[str] = None diff --git a/src/cloudflare/types/email_security/investigate/bulk_delete_response.py b/src/cloudflare/types/email_security/investigate/bulk_delete_response.py new file mode 100644 index 00000000000..959e6690f5f --- /dev/null +++ b/src/cloudflare/types/email_security/investigate/bulk_delete_response.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ...._models import BaseModel + +__all__ = ["BulkDeleteResponse"] + + +class BulkDeleteResponse(BaseModel): + id: str diff --git a/src/cloudflare/types/email_security/investigate/bulk_get_response.py b/src/cloudflare/types/email_security/investigate/bulk_get_response.py new file mode 100644 index 00000000000..68c62074a78 --- /dev/null +++ b/src/cloudflare/types/email_security/investigate/bulk_get_response.py @@ -0,0 +1,126 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = ["BulkGetResponse", "ActionParams", "ActionParamsMove", "ActionParamsRelease", "SearchParams"] + + +class ActionParamsMove(BaseModel): + destination: Literal["Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"] + + type: Literal["MOVE"] + + expected_disposition: Optional[ + Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + ] = None + + +class ActionParamsRelease(BaseModel): + type: Literal["RELEASE"] + + +ActionParams: TypeAlias = Annotated[Union[ActionParamsMove, ActionParamsRelease], PropertyInfo(discriminator="type")] + + +class SearchParams(BaseModel): + action_log: Optional[bool] = None + """Deprecated, use `GET /investigate/{investigate_id}/action_log` instead. + + End of life: November 1, 2026. + """ + + alert_id: Optional[str] = None + + delivery_status: Optional[ + Literal["delivered", "moved", "quarantined", "rejected", "deferred", "bounced", "queued"] + ] = None + """Delivery status of the message.""" + + detections_only: Optional[bool] = None + + domain: Optional[str] = None + + end: Optional[datetime] = None + """End of search date range""" + + exact_subject: Optional[str] = None + + final_disposition: Optional[ + Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + ] = None + + message_action: Optional[Literal["PREVIEW", "QUARANTINE_RELEASED", "MOVED"]] = None + + message_id: Optional[str] = None + + metric: Optional[str] = None + + query: Optional[str] = None + + recipient: Optional[str] = None + + sender: Optional[str] = None + + start: Optional[datetime] = None + """Beginning of search date range""" + + subject: Optional[str] = None + + submissions: Optional[bool] = None + + +class BulkGetResponse(BaseModel): + action_params: ActionParams + + action_type: Literal["MOVE", "RELEASE"] + + created_at: datetime + + job_id: str + + messages_failed: int + + messages_pending: int + + messages_successful: int + + search_params: SearchParams + + status: Literal["PENDING", "DISCOVERING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "SKIPPED"] + + total_messages_discovered: int + + comment: Optional[str] = None + + completed_at: Optional[datetime] = None + + started_at: Optional[datetime] = None + + status_message: Optional[str] = None diff --git a/src/cloudflare/types/email_security/investigate/bulk_list_params.py b/src/cloudflare/types/email_security/investigate/bulk_list_params.py new file mode 100644 index 00000000000..d6f67091911 --- /dev/null +++ b/src/cloudflare/types/email_security/investigate/bulk_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BulkListParams"] + + +class BulkListParams(TypedDict, total=False): + account_id: Required[str] + """Identifier.""" + + action_type: Literal["MOVE", "RELEASE"] + + page: int + """Current page within paginated list of results.""" + + per_page: int + """The number of results per page. Maximum value is 1000.""" + + status: Literal["PENDING", "DISCOVERING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "SKIPPED"] diff --git a/src/cloudflare/types/email_security/investigate/bulk_list_response.py b/src/cloudflare/types/email_security/investigate/bulk_list_response.py new file mode 100644 index 00000000000..ebd22f34aa7 --- /dev/null +++ b/src/cloudflare/types/email_security/investigate/bulk_list_response.py @@ -0,0 +1,126 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = ["BulkListResponse", "ActionParams", "ActionParamsMove", "ActionParamsRelease", "SearchParams"] + + +class ActionParamsMove(BaseModel): + destination: Literal["Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"] + + type: Literal["MOVE"] + + expected_disposition: Optional[ + Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + ] = None + + +class ActionParamsRelease(BaseModel): + type: Literal["RELEASE"] + + +ActionParams: TypeAlias = Annotated[Union[ActionParamsMove, ActionParamsRelease], PropertyInfo(discriminator="type")] + + +class SearchParams(BaseModel): + action_log: Optional[bool] = None + """Deprecated, use `GET /investigate/{investigate_id}/action_log` instead. + + End of life: November 1, 2026. + """ + + alert_id: Optional[str] = None + + delivery_status: Optional[ + Literal["delivered", "moved", "quarantined", "rejected", "deferred", "bounced", "queued"] + ] = None + """Delivery status of the message.""" + + detections_only: Optional[bool] = None + + domain: Optional[str] = None + + end: Optional[datetime] = None + """End of search date range""" + + exact_subject: Optional[str] = None + + final_disposition: Optional[ + Literal[ + "MALICIOUS", + "MALICIOUS-BEC", + "SUSPICIOUS", + "SPOOF", + "SPAM", + "BULK", + "ENCRYPTED", + "EXTERNAL", + "UNKNOWN", + "NONE", + ] + ] = None + + message_action: Optional[Literal["PREVIEW", "QUARANTINE_RELEASED", "MOVED"]] = None + + message_id: Optional[str] = None + + metric: Optional[str] = None + + query: Optional[str] = None + + recipient: Optional[str] = None + + sender: Optional[str] = None + + start: Optional[datetime] = None + """Beginning of search date range""" + + subject: Optional[str] = None + + submissions: Optional[bool] = None + + +class BulkListResponse(BaseModel): + action_params: ActionParams + + action_type: Literal["MOVE", "RELEASE"] + + created_at: datetime + + job_id: str + + messages_failed: int + + messages_pending: int + + messages_successful: int + + search_params: SearchParams + + status: Literal["PENDING", "DISCOVERING", "PROCESSING", "COMPLETED", "FAILED", "CANCELLED", "SKIPPED"] + + total_messages_discovered: int + + comment: Optional[str] = None + + completed_at: Optional[datetime] = None + + started_at: Optional[datetime] = None + + status_message: Optional[str] = None diff --git a/tests/api_resources/email_security/investigate/bulk/__init__.py b/tests/api_resources/email_security/investigate/bulk/__init__.py new file mode 100644 index 00000000000..fd8019a9a1a --- /dev/null +++ b/tests/api_resources/email_security/investigate/bulk/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/email_security/investigate/bulk/test_cancel.py b/tests/api_resources/email_security/investigate/bulk/test_cancel.py new file mode 100644 index 00000000000..87d7ab3f64b --- /dev/null +++ b/tests/api_resources/email_security/investigate/bulk/test_cancel.py @@ -0,0 +1,120 @@ +# 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.email_security.investigate.bulk import CancelCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCancel: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Cloudflare) -> None: + cancel = client.email_security.investigate.bulk.cancel.create( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(CancelCreateResponse, cancel, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Cloudflare) -> None: + response = client.email_security.investigate.bulk.cancel.with_raw_response.create( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cancel = response.parse() + assert_matches_type(CancelCreateResponse, cancel, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Cloudflare) -> None: + with client.email_security.investigate.bulk.cancel.with_streaming_response.create( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cancel = response.parse() + assert_matches_type(CancelCreateResponse, cancel, 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 `account_id` but received ''"): + client.email_security.investigate.bulk.cancel.with_raw_response.create( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"): + client.email_security.investigate.bulk.cancel.with_raw_response.create( + job_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + +class TestAsyncCancel: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncCloudflare) -> None: + cancel = await async_client.email_security.investigate.bulk.cancel.create( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(CancelCreateResponse, cancel, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None: + response = await async_client.email_security.investigate.bulk.cancel.with_raw_response.create( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cancel = await response.parse() + assert_matches_type(CancelCreateResponse, cancel, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncCloudflare) -> None: + async with async_client.email_security.investigate.bulk.cancel.with_streaming_response.create( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cancel = await response.parse() + assert_matches_type(CancelCreateResponse, cancel, 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 `account_id` but received ''"): + await async_client.email_security.investigate.bulk.cancel.with_raw_response.create( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"): + await async_client.email_security.investigate.bulk.cancel.with_raw_response.create( + job_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) diff --git a/tests/api_resources/email_security/investigate/bulk/test_messages.py b/tests/api_resources/email_security/investigate/bulk/test_messages.py new file mode 100644 index 00000000000..fd92da58149 --- /dev/null +++ b/tests/api_resources/email_security/investigate/bulk/test_messages.py @@ -0,0 +1,143 @@ +# 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.pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray +from cloudflare.types.email_security.investigate.bulk import MessageListResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestMessages: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: Cloudflare) -> None: + message = client.email_security.investigate.bulk.messages.list( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(SyncV4PagePaginationArray[MessageListResponse], message, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Cloudflare) -> None: + message = client.email_security.investigate.bulk.messages.list( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + page=1, + per_page=20, + status="PENDING", + ) + assert_matches_type(SyncV4PagePaginationArray[MessageListResponse], message, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Cloudflare) -> None: + response = client.email_security.investigate.bulk.messages.with_raw_response.list( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = response.parse() + assert_matches_type(SyncV4PagePaginationArray[MessageListResponse], message, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Cloudflare) -> None: + with client.email_security.investigate.bulk.messages.with_streaming_response.list( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = response.parse() + assert_matches_type(SyncV4PagePaginationArray[MessageListResponse], message, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.email_security.investigate.bulk.messages.with_raw_response.list( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"): + client.email_security.investigate.bulk.messages.with_raw_response.list( + job_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + +class TestAsyncMessages: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list(self, async_client: AsyncCloudflare) -> None: + message = await async_client.email_security.investigate.bulk.messages.list( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(AsyncV4PagePaginationArray[MessageListResponse], message, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None: + message = await async_client.email_security.investigate.bulk.messages.list( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + page=1, + per_page=20, + status="PENDING", + ) + assert_matches_type(AsyncV4PagePaginationArray[MessageListResponse], message, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None: + response = await async_client.email_security.investigate.bulk.messages.with_raw_response.list( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + message = await response.parse() + assert_matches_type(AsyncV4PagePaginationArray[MessageListResponse], message, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncCloudflare) -> None: + async with async_client.email_security.investigate.bulk.messages.with_streaming_response.list( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + message = await response.parse() + assert_matches_type(AsyncV4PagePaginationArray[MessageListResponse], message, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.email_security.investigate.bulk.messages.with_raw_response.list( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"): + await async_client.email_security.investigate.bulk.messages.with_raw_response.list( + job_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) diff --git a/tests/api_resources/email_security/investigate/test_bulk.py b/tests/api_resources/email_security/investigate/test_bulk.py new file mode 100644 index 00000000000..308ae694465 --- /dev/null +++ b/tests/api_resources/email_security/investigate/test_bulk.py @@ -0,0 +1,473 @@ +# 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._utils import parse_datetime +from cloudflare.pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray +from cloudflare.types.email_security.investigate import ( + BulkGetResponse, + BulkListResponse, + BulkCreateResponse, + BulkDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestBulk: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Cloudflare) -> None: + bulk = client.email_security.investigate.bulk.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + action="MOVE", + search_params={}, + ) + assert_matches_type(BulkCreateResponse, bulk, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Cloudflare) -> None: + bulk = client.email_security.investigate.bulk.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + action="MOVE", + search_params={ + "action_log": True, + "alert_id": "alert_id", + "delivery_status": "delivered", + "detections_only": True, + "domain": "domain", + "end": parse_datetime("2022-07-25T14:30:00Z"), + "exact_subject": "exact_subject", + "final_disposition": "MALICIOUS", + "message_action": "PREVIEW", + "message_id": "message_id", + "metric": "metric", + "query": "query", + "recipient": "recipient", + "sender": "sender", + "start": parse_datetime("2022-06-25T14:30:00Z"), + "subject": "subject", + "submissions": True, + }, + comment="comment", + destination="Inbox", + expected_disposition="MALICIOUS", + ) + assert_matches_type(BulkCreateResponse, bulk, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Cloudflare) -> None: + response = client.email_security.investigate.bulk.with_raw_response.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + action="MOVE", + search_params={}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bulk = response.parse() + assert_matches_type(BulkCreateResponse, bulk, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Cloudflare) -> None: + with client.email_security.investigate.bulk.with_streaming_response.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + action="MOVE", + search_params={}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bulk = response.parse() + assert_matches_type(BulkCreateResponse, bulk, 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 `account_id` but received ''"): + client.email_security.investigate.bulk.with_raw_response.create( + account_id="", + action="MOVE", + search_params={}, + ) + + @parametrize + def test_method_list(self, client: Cloudflare) -> None: + bulk = client.email_security.investigate.bulk.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(SyncV4PagePaginationArray[BulkListResponse], bulk, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Cloudflare) -> None: + bulk = client.email_security.investigate.bulk.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + action_type="MOVE", + page=1, + per_page=20, + status="PENDING", + ) + assert_matches_type(SyncV4PagePaginationArray[BulkListResponse], bulk, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Cloudflare) -> None: + response = client.email_security.investigate.bulk.with_raw_response.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bulk = response.parse() + assert_matches_type(SyncV4PagePaginationArray[BulkListResponse], bulk, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Cloudflare) -> None: + with client.email_security.investigate.bulk.with_streaming_response.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bulk = response.parse() + assert_matches_type(SyncV4PagePaginationArray[BulkListResponse], bulk, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.email_security.investigate.bulk.with_raw_response.list( + account_id="", + ) + + @parametrize + def test_method_delete(self, client: Cloudflare) -> None: + bulk = client.email_security.investigate.bulk.delete( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(BulkDeleteResponse, bulk, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: Cloudflare) -> None: + response = client.email_security.investigate.bulk.with_raw_response.delete( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bulk = response.parse() + assert_matches_type(BulkDeleteResponse, bulk, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: Cloudflare) -> None: + with client.email_security.investigate.bulk.with_streaming_response.delete( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bulk = response.parse() + assert_matches_type(BulkDeleteResponse, bulk, 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.email_security.investigate.bulk.with_raw_response.delete( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"): + client.email_security.investigate.bulk.with_raw_response.delete( + job_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + @parametrize + def test_method_get(self, client: Cloudflare) -> None: + bulk = client.email_security.investigate.bulk.get( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(BulkGetResponse, bulk, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Cloudflare) -> None: + response = client.email_security.investigate.bulk.with_raw_response.get( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bulk = response.parse() + assert_matches_type(BulkGetResponse, bulk, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Cloudflare) -> None: + with client.email_security.investigate.bulk.with_streaming_response.get( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bulk = response.parse() + assert_matches_type(BulkGetResponse, bulk, 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.email_security.investigate.bulk.with_raw_response.get( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"): + client.email_security.investigate.bulk.with_raw_response.get( + job_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + +class TestAsyncBulk: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncCloudflare) -> None: + bulk = await async_client.email_security.investigate.bulk.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + action="MOVE", + search_params={}, + ) + assert_matches_type(BulkCreateResponse, bulk, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncCloudflare) -> None: + bulk = await async_client.email_security.investigate.bulk.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + action="MOVE", + search_params={ + "action_log": True, + "alert_id": "alert_id", + "delivery_status": "delivered", + "detections_only": True, + "domain": "domain", + "end": parse_datetime("2022-07-25T14:30:00Z"), + "exact_subject": "exact_subject", + "final_disposition": "MALICIOUS", + "message_action": "PREVIEW", + "message_id": "message_id", + "metric": "metric", + "query": "query", + "recipient": "recipient", + "sender": "sender", + "start": parse_datetime("2022-06-25T14:30:00Z"), + "subject": "subject", + "submissions": True, + }, + comment="comment", + destination="Inbox", + expected_disposition="MALICIOUS", + ) + assert_matches_type(BulkCreateResponse, bulk, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None: + response = await async_client.email_security.investigate.bulk.with_raw_response.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + action="MOVE", + search_params={}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bulk = await response.parse() + assert_matches_type(BulkCreateResponse, bulk, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncCloudflare) -> None: + async with async_client.email_security.investigate.bulk.with_streaming_response.create( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + action="MOVE", + search_params={}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bulk = await response.parse() + assert_matches_type(BulkCreateResponse, bulk, 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 `account_id` but received ''"): + await async_client.email_security.investigate.bulk.with_raw_response.create( + account_id="", + action="MOVE", + search_params={}, + ) + + @parametrize + async def test_method_list(self, async_client: AsyncCloudflare) -> None: + bulk = await async_client.email_security.investigate.bulk.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(AsyncV4PagePaginationArray[BulkListResponse], bulk, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None: + bulk = await async_client.email_security.investigate.bulk.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + action_type="MOVE", + page=1, + per_page=20, + status="PENDING", + ) + assert_matches_type(AsyncV4PagePaginationArray[BulkListResponse], bulk, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None: + response = await async_client.email_security.investigate.bulk.with_raw_response.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bulk = await response.parse() + assert_matches_type(AsyncV4PagePaginationArray[BulkListResponse], bulk, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncCloudflare) -> None: + async with async_client.email_security.investigate.bulk.with_streaming_response.list( + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bulk = await response.parse() + assert_matches_type(AsyncV4PagePaginationArray[BulkListResponse], bulk, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.email_security.investigate.bulk.with_raw_response.list( + account_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncCloudflare) -> None: + bulk = await async_client.email_security.investigate.bulk.delete( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(BulkDeleteResponse, bulk, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncCloudflare) -> None: + response = await async_client.email_security.investigate.bulk.with_raw_response.delete( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bulk = await response.parse() + assert_matches_type(BulkDeleteResponse, bulk, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncCloudflare) -> None: + async with async_client.email_security.investigate.bulk.with_streaming_response.delete( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bulk = await response.parse() + assert_matches_type(BulkDeleteResponse, bulk, 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.email_security.investigate.bulk.with_raw_response.delete( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"): + await async_client.email_security.investigate.bulk.with_raw_response.delete( + job_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + @parametrize + async def test_method_get(self, async_client: AsyncCloudflare) -> None: + bulk = await async_client.email_security.investigate.bulk.get( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(BulkGetResponse, bulk, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None: + response = await async_client.email_security.investigate.bulk.with_raw_response.get( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bulk = await response.parse() + assert_matches_type(BulkGetResponse, bulk, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None: + async with async_client.email_security.investigate.bulk.with_streaming_response.get( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bulk = await response.parse() + assert_matches_type(BulkGetResponse, bulk, 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.email_security.investigate.bulk.with_raw_response.get( + job_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"): + await async_client.email_security.investigate.bulk.with_raw_response.get( + job_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) From 05c5ac3bb50595ea85062ab5e464b5ab2d4638ae Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:29:06 +0000 Subject: [PATCH 26/29] chore(api): update composite API spec --- .stats.yml | 4 +- .../resources/cloudforce_one/api.md | 2 +- .../cloudforce_one/threat_events/datasets.py | 26 ++++++++-- .../cloudforce_one/threat_events/tags.py | 48 +++++++++++++++++++ .../cloudforce_one/threat_events/__init__.py | 1 + .../threat_events/dataset_create_response.py | 4 ++ .../threat_events/dataset_edit_response.py | 4 ++ .../threat_events/dataset_get_response.py | 4 ++ .../threat_events/dataset_list_params.py | 20 ++++++++ .../threat_events/dataset_list_response.py | 4 +- .../threat_events/tag_create_params.py | 48 ++++++++++++++++++- .../threat_events/tag_create_response.py | 35 +++++++++++++- .../threat_events/test_datasets.py | 18 +++++++ .../cloudforce_one/threat_events/test_tags.py | 40 ++++++++++++++-- 14 files changed, 243 insertions(+), 15 deletions(-) create mode 100644 src/cloudflare/types/cloudforce_one/threat_events/dataset_list_params.py diff --git a/.stats.yml b/.stats.yml index 206e0199837..25fc4e8cda7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2418 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-390a1858f30b8d927f55b608de32c0af9117819b1b17ef35376447cd537b91ae.yml -openapi_spec_hash: bacabcf09c5206d41639afc39af8f2ac +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-59f76d419fb40364c15f3c2b57f38cca5ea9e88d8eddbd45bb729f8480967c25.yml +openapi_spec_hash: 0c620418d87503a65cd244aa980fd774 config_hash: cd80863b2a094f3805409e4a5676b0b8 diff --git a/src/cloudflare/resources/cloudforce_one/api.md b/src/cloudflare/resources/cloudforce_one/api.md index a53cc4086b3..3ce6bf2fcae 100644 --- a/src/cloudflare/resources/cloudforce_one/api.md +++ b/src/cloudflare/resources/cloudforce_one/api.md @@ -214,7 +214,7 @@ from cloudflare.types.cloudforce_one.threat_events import ( Methods: - client.cloudforce_one.threat_events.datasets.create(\*, account_id, \*\*params) -> DatasetCreateResponse -- client.cloudforce_one.threat_events.datasets.list(\*, account_id) -> DatasetListResponse +- client.cloudforce_one.threat_events.datasets.list(\*, account_id, \*\*params) -> DatasetListResponse - client.cloudforce_one.threat_events.datasets.edit(dataset_id, \*, account_id, \*\*params) -> DatasetEditResponse - client.cloudforce_one.threat_events.datasets.get(dataset_id, \*, account_id) -> DatasetGetResponse - client.cloudforce_one.threat_events.datasets.raw(event_id, \*, account_id, dataset_id) -> DatasetRawResponse diff --git a/src/cloudflare/resources/cloudforce_one/threat_events/datasets.py b/src/cloudflare/resources/cloudforce_one/threat_events/datasets.py index aa061a94e4a..7cacd119b6a 100644 --- a/src/cloudflare/resources/cloudforce_one/threat_events/datasets.py +++ b/src/cloudflare/resources/cloudforce_one/threat_events/datasets.py @@ -4,7 +4,7 @@ import httpx -from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import path_template, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -15,7 +15,7 @@ async_to_streamed_response_wrapper, ) from ...._base_client import make_request_options -from ....types.cloudforce_one.threat_events import dataset_edit_params, dataset_create_params +from ....types.cloudforce_one.threat_events import dataset_edit_params, dataset_list_params, dataset_create_params from ....types.cloudforce_one.threat_events.dataset_get_response import DatasetGetResponse from ....types.cloudforce_one.threat_events.dataset_raw_response import DatasetRawResponse from ....types.cloudforce_one.threat_events.dataset_edit_response import DatasetEditResponse @@ -98,6 +98,7 @@ def list( self, *, account_id: str, + include_deleted: bool | Omit = omit, # 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, @@ -111,6 +112,9 @@ def list( Args: account_id: Account ID. + include_deleted: When true, include soft-deleted datasets in the response. Each item includes a + `deletedAt` field (ISO 8601 or null). Default: false. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -124,7 +128,11 @@ def list( return self._get( path_template("/accounts/{account_id}/cloudforce-one/events/dataset", account_id=account_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include_deleted": include_deleted}, dataset_list_params.DatasetListParams), ), cast_to=DatasetListResponse, ) @@ -357,6 +365,7 @@ async def list( self, *, account_id: str, + include_deleted: bool | Omit = omit, # 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, @@ -370,6 +379,9 @@ async def list( Args: account_id: Account ID. + include_deleted: When true, include soft-deleted datasets in the response. Each item includes a + `deletedAt` field (ISO 8601 or null). Default: false. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -383,7 +395,13 @@ async def list( return await self._get( path_template("/accounts/{account_id}/cloudforce-one/events/dataset", account_id=account_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"include_deleted": include_deleted}, dataset_list_params.DatasetListParams + ), ), cast_to=DatasetListResponse, ) diff --git a/src/cloudflare/resources/cloudforce_one/threat_events/tags.py b/src/cloudflare/resources/cloudforce_one/threat_events/tags.py index e658f166eef..9df687c4786 100644 --- a/src/cloudflare/resources/cloudforce_one/threat_events/tags.py +++ b/src/cloudflare/resources/cloudforce_one/threat_events/tags.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing import Iterable + import httpx from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given @@ -48,13 +50,17 @@ def create( value: str, active_duration: str | Omit = omit, actor_category: str | Omit = omit, + aliases: Iterable[tag_create_params.Alias] | Omit = omit, alias_group_names: SequenceNotStr[str] | Omit = omit, alias_group_names_internal: SequenceNotStr[str] | Omit = omit, analytic_priority: float | Omit = omit, attribution_confidence: str | Omit = omit, + attribution_confidence_score: int | Omit = omit, attribution_organization: str | Omit = omit, category_uuid: str | Omit = omit, + date_of_discovery: str | Omit = omit, external_reference_links: SequenceNotStr[str] | Omit = omit, + internal_aliases: Iterable[tag_create_params.InternalAlias] | Omit = omit, internal_description: str | Omit = omit, motive: str | Omit = omit, opsec_level: str | Omit = omit, @@ -74,6 +80,21 @@ def create( Args: account_id: Account ID. + actor_category: Actor variety. Allowed values: Activist, Competitor, Customer, Crime Syndicate, + Former Employee, Nation State, Organized Crime, Nation State Affiliated, + Terrorist, Unaffiliated. + + aliases: Structured aliases ({ value, confidence 1-10, tlp }). CFONE-only: stripped from + responses to non-CFONE accounts. + + date_of_discovery: Date the actor was discovered (ISO YYYY-MM-DD). + + internal_aliases: Internal structured aliases ({ value, confidence 1-10, tlp }). CFONE-only: never + returned to non-CFONE accounts. + + motive: Actor motive. Allowed values: Convenience, Fear, Fun, Financial, Grudge, + Ideology, Espionage. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -91,13 +112,17 @@ def create( "value": value, "active_duration": active_duration, "actor_category": actor_category, + "aliases": aliases, "alias_group_names": alias_group_names, "alias_group_names_internal": alias_group_names_internal, "analytic_priority": analytic_priority, "attribution_confidence": attribution_confidence, + "attribution_confidence_score": attribution_confidence_score, "attribution_organization": attribution_organization, "category_uuid": category_uuid, + "date_of_discovery": date_of_discovery, "external_reference_links": external_reference_links, + "internal_aliases": internal_aliases, "internal_description": internal_description, "motive": motive, "opsec_level": opsec_level, @@ -141,13 +166,17 @@ async def create( value: str, active_duration: str | Omit = omit, actor_category: str | Omit = omit, + aliases: Iterable[tag_create_params.Alias] | Omit = omit, alias_group_names: SequenceNotStr[str] | Omit = omit, alias_group_names_internal: SequenceNotStr[str] | Omit = omit, analytic_priority: float | Omit = omit, attribution_confidence: str | Omit = omit, + attribution_confidence_score: int | Omit = omit, attribution_organization: str | Omit = omit, category_uuid: str | Omit = omit, + date_of_discovery: str | Omit = omit, external_reference_links: SequenceNotStr[str] | Omit = omit, + internal_aliases: Iterable[tag_create_params.InternalAlias] | Omit = omit, internal_description: str | Omit = omit, motive: str | Omit = omit, opsec_level: str | Omit = omit, @@ -167,6 +196,21 @@ async def create( Args: account_id: Account ID. + actor_category: Actor variety. Allowed values: Activist, Competitor, Customer, Crime Syndicate, + Former Employee, Nation State, Organized Crime, Nation State Affiliated, + Terrorist, Unaffiliated. + + aliases: Structured aliases ({ value, confidence 1-10, tlp }). CFONE-only: stripped from + responses to non-CFONE accounts. + + date_of_discovery: Date the actor was discovered (ISO YYYY-MM-DD). + + internal_aliases: Internal structured aliases ({ value, confidence 1-10, tlp }). CFONE-only: never + returned to non-CFONE accounts. + + motive: Actor motive. Allowed values: Convenience, Fear, Fun, Financial, Grudge, + Ideology, Espionage. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -184,13 +228,17 @@ async def create( "value": value, "active_duration": active_duration, "actor_category": actor_category, + "aliases": aliases, "alias_group_names": alias_group_names, "alias_group_names_internal": alias_group_names_internal, "analytic_priority": analytic_priority, "attribution_confidence": attribution_confidence, + "attribution_confidence_score": attribution_confidence_score, "attribution_organization": attribution_organization, "category_uuid": category_uuid, + "date_of_discovery": date_of_discovery, "external_reference_links": external_reference_links, + "internal_aliases": internal_aliases, "internal_description": internal_description, "motive": motive, "opsec_level": opsec_level, diff --git a/src/cloudflare/types/cloudforce_one/threat_events/__init__.py b/src/cloudflare/types/cloudforce_one/threat_events/__init__.py index ca0dac14947..07dedb4c361 100644 --- a/src/cloudflare/types/cloudforce_one/threat_events/__init__.py +++ b/src/cloudflare/types/cloudforce_one/threat_events/__init__.py @@ -7,6 +7,7 @@ from .raw_edit_response import RawEditResponse as RawEditResponse from .tag_create_params import TagCreateParams as TagCreateParams from .dataset_edit_params import DatasetEditParams as DatasetEditParams +from .dataset_list_params import DatasetListParams as DatasetListParams from .tag_create_response import TagCreateResponse as TagCreateResponse from .attacker_list_params import AttackerListParams as AttackerListParams from .category_edit_params import CategoryEditParams as CategoryEditParams diff --git a/src/cloudflare/types/cloudforce_one/threat_events/dataset_create_response.py b/src/cloudflare/types/cloudforce_one/threat_events/dataset_create_response.py index d2a4ed0bce2..a001fff30cd 100644 --- a/src/cloudflare/types/cloudforce_one/threat_events/dataset_create_response.py +++ b/src/cloudflare/types/cloudforce_one/threat_events/dataset_create_response.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional + from pydantic import Field as FieldInfo from ...._models import BaseModel @@ -13,3 +15,5 @@ class DatasetCreateResponse(BaseModel): name: str uuid: str + + deleted_at: Optional[str] = FieldInfo(alias="deletedAt", default=None) diff --git a/src/cloudflare/types/cloudforce_one/threat_events/dataset_edit_response.py b/src/cloudflare/types/cloudforce_one/threat_events/dataset_edit_response.py index 791c041c355..35dae6b16c8 100644 --- a/src/cloudflare/types/cloudforce_one/threat_events/dataset_edit_response.py +++ b/src/cloudflare/types/cloudforce_one/threat_events/dataset_edit_response.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional + from pydantic import Field as FieldInfo from ...._models import BaseModel @@ -13,3 +15,5 @@ class DatasetEditResponse(BaseModel): name: str uuid: str + + deleted_at: Optional[str] = FieldInfo(alias="deletedAt", default=None) diff --git a/src/cloudflare/types/cloudforce_one/threat_events/dataset_get_response.py b/src/cloudflare/types/cloudforce_one/threat_events/dataset_get_response.py index 7e30033d8fe..466f46f3d5e 100644 --- a/src/cloudflare/types/cloudforce_one/threat_events/dataset_get_response.py +++ b/src/cloudflare/types/cloudforce_one/threat_events/dataset_get_response.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional + from pydantic import Field as FieldInfo from ...._models import BaseModel @@ -13,3 +15,5 @@ class DatasetGetResponse(BaseModel): name: str uuid: str + + deleted_at: Optional[str] = FieldInfo(alias="deletedAt", default=None) diff --git a/src/cloudflare/types/cloudforce_one/threat_events/dataset_list_params.py b/src/cloudflare/types/cloudforce_one/threat_events/dataset_list_params.py new file mode 100644 index 00000000000..5d7ec60d2e6 --- /dev/null +++ b/src/cloudflare/types/cloudforce_one/threat_events/dataset_list_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 Required, Annotated, TypedDict + +from ...._utils import PropertyInfo + +__all__ = ["DatasetListParams"] + + +class DatasetListParams(TypedDict, total=False): + account_id: Required[str] + """Account ID.""" + + include_deleted: Annotated[bool, PropertyInfo(alias="includeDeleted")] + """When true, include soft-deleted datasets in the response. + + Each item includes a `deletedAt` field (ISO 8601 or null). Default: false. + """ diff --git a/src/cloudflare/types/cloudforce_one/threat_events/dataset_list_response.py b/src/cloudflare/types/cloudforce_one/threat_events/dataset_list_response.py index 810843f27ef..266fa2a3c3f 100644 --- a/src/cloudflare/types/cloudforce_one/threat_events/dataset_list_response.py +++ b/src/cloudflare/types/cloudforce_one/threat_events/dataset_list_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List +from typing import List, Optional from typing_extensions import TypeAlias from pydantic import Field as FieldInfo @@ -17,5 +17,7 @@ class DatasetListResponseItem(BaseModel): uuid: str + deleted_at: Optional[str] = FieldInfo(alias="deletedAt", default=None) + DatasetListResponse: TypeAlias = List[DatasetListResponseItem] diff --git a/src/cloudflare/types/cloudforce_one/threat_events/tag_create_params.py b/src/cloudflare/types/cloudforce_one/threat_events/tag_create_params.py index 85452744e19..8b7128838d5 100644 --- a/src/cloudflare/types/cloudforce_one/threat_events/tag_create_params.py +++ b/src/cloudflare/types/cloudforce_one/threat_events/tag_create_params.py @@ -2,12 +2,13 @@ from __future__ import annotations -from typing_extensions import Required, Annotated, TypedDict +from typing import Iterable, Optional +from typing_extensions import Literal, Required, Annotated, TypedDict from ...._types import SequenceNotStr from ...._utils import PropertyInfo -__all__ = ["TagCreateParams"] +__all__ = ["TagCreateParams", "Alias", "InternalAlias"] class TagCreateParams(TypedDict, total=False): @@ -19,6 +20,18 @@ class TagCreateParams(TypedDict, total=False): active_duration: Annotated[str, PropertyInfo(alias="activeDuration")] actor_category: Annotated[str, PropertyInfo(alias="actorCategory")] + """Actor variety. + + Allowed values: Activist, Competitor, Customer, Crime Syndicate, Former + Employee, Nation State, Organized Crime, Nation State Affiliated, Terrorist, + Unaffiliated. + """ + + aliases: Iterable[Alias] + """Structured aliases ({ value, confidence 1-10, tlp }). + + CFONE-only: stripped from responses to non-CFONE accounts. + """ alias_group_names: Annotated[SequenceNotStr[str], PropertyInfo(alias="aliasGroupNames")] @@ -28,15 +41,30 @@ class TagCreateParams(TypedDict, total=False): attribution_confidence: Annotated[str, PropertyInfo(alias="attributionConfidence")] + attribution_confidence_score: Annotated[int, PropertyInfo(alias="attributionConfidenceScore")] + attribution_organization: Annotated[str, PropertyInfo(alias="attributionOrganization")] category_uuid: Annotated[str, PropertyInfo(alias="categoryUuid")] + date_of_discovery: Annotated[str, PropertyInfo(alias="dateOfDiscovery")] + """Date the actor was discovered (ISO YYYY-MM-DD).""" + external_reference_links: Annotated[SequenceNotStr[str], PropertyInfo(alias="externalReferenceLinks")] + internal_aliases: Annotated[Iterable[InternalAlias], PropertyInfo(alias="internalAliases")] + """Internal structured aliases ({ value, confidence 1-10, tlp }). + + CFONE-only: never returned to non-CFONE accounts. + """ + internal_description: Annotated[str, PropertyInfo(alias="internalDescription")] motive: str + """Actor motive. + + Allowed values: Convenience, Fear, Fun, Financial, Grudge, Ideology, Espionage. + """ opsec_level: Annotated[str, PropertyInfo(alias="opsecLevel")] @@ -45,3 +73,19 @@ class TagCreateParams(TypedDict, total=False): priority: float sophistication_level: Annotated[str, PropertyInfo(alias="sophisticationLevel")] + + +class Alias(TypedDict, total=False): + value: Required[str] + + confidence: Optional[int] + + tlp: Optional[Literal["red", "amber", "green", "white"]] + + +class InternalAlias(TypedDict, total=False): + value: Required[str] + + confidence: Optional[int] + + tlp: Optional[Literal["red", "amber", "green", "white"]] diff --git a/src/cloudflare/types/cloudforce_one/threat_events/tag_create_response.py b/src/cloudflare/types/cloudforce_one/threat_events/tag_create_response.py index e1c09229a54..bd7b94e70b1 100644 --- a/src/cloudflare/types/cloudforce_one/threat_events/tag_create_response.py +++ b/src/cloudflare/types/cloudforce_one/threat_events/tag_create_response.py @@ -1,12 +1,29 @@ # 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__ = ["TagCreateResponse"] +__all__ = ["TagCreateResponse", "Alias", "InternalAlias"] + + +class Alias(BaseModel): + value: str + + confidence: Optional[int] = None + + tlp: Optional[Literal["red", "amber", "green", "white"]] = None + + +class InternalAlias(BaseModel): + value: str + + confidence: Optional[int] = None + + tlp: Optional[Literal["red", "amber", "green", "white"]] = None class TagCreateResponse(BaseModel): @@ -18,6 +35,12 @@ class TagCreateResponse(BaseModel): actor_category: Optional[str] = FieldInfo(alias="actorCategory", default=None) + aliases: Optional[List[Alias]] = None + """Structured aliases ({ value, confidence 1-10, tlp }). + + CFONE-only: stripped from responses to non-CFONE accounts. + """ + alias_group_names: Optional[List[str]] = FieldInfo(alias="aliasGroupNames", default=None) alias_group_names_internal: Optional[List[str]] = FieldInfo(alias="aliasGroupNamesInternal", default=None) @@ -26,14 +49,24 @@ class TagCreateResponse(BaseModel): attribution_confidence: Optional[str] = FieldInfo(alias="attributionConfidence", default=None) + attribution_confidence_score: Optional[int] = FieldInfo(alias="attributionConfidenceScore", default=None) + attribution_organization: Optional[str] = FieldInfo(alias="attributionOrganization", default=None) category_name: Optional[str] = FieldInfo(alias="categoryName", default=None) category_uuid: Optional[str] = FieldInfo(alias="categoryUuid", default=None) + date_of_discovery: Optional[str] = FieldInfo(alias="dateOfDiscovery", default=None) + external_reference_links: Optional[List[str]] = FieldInfo(alias="externalReferenceLinks", default=None) + internal_aliases: Optional[List[InternalAlias]] = FieldInfo(alias="internalAliases", default=None) + """Internal structured aliases ({ value, confidence 1-10, tlp }). + + CFONE-only: never returned to non-CFONE accounts. + """ + internal_description: Optional[str] = FieldInfo(alias="internalDescription", default=None) motive: Optional[str] = None diff --git a/tests/api_resources/cloudforce_one/threat_events/test_datasets.py b/tests/api_resources/cloudforce_one/threat_events/test_datasets.py index f1ae4d436cd..d71515b0dd3 100644 --- a/tests/api_resources/cloudforce_one/threat_events/test_datasets.py +++ b/tests/api_resources/cloudforce_one/threat_events/test_datasets.py @@ -81,6 +81,15 @@ def test_method_list(self, client: Cloudflare) -> None: ) assert_matches_type(DatasetListResponse, dataset, path=["response"]) + @pytest.mark.skip(reason="TODO: HTTP 401 from prism") + @parametrize + def test_method_list_with_all_params(self, client: Cloudflare) -> None: + dataset = client.cloudforce_one.threat_events.datasets.list( + account_id="account_id", + include_deleted=True, + ) + assert_matches_type(DatasetListResponse, dataset, path=["response"]) + @pytest.mark.skip(reason="TODO: HTTP 401 from prism") @parametrize def test_raw_response_list(self, client: Cloudflare) -> None: @@ -357,6 +366,15 @@ async def test_method_list(self, async_client: AsyncCloudflare) -> None: ) assert_matches_type(DatasetListResponse, dataset, path=["response"]) + @pytest.mark.skip(reason="TODO: HTTP 401 from prism") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None: + dataset = await async_client.cloudforce_one.threat_events.datasets.list( + account_id="account_id", + include_deleted=True, + ) + assert_matches_type(DatasetListResponse, dataset, path=["response"]) + @pytest.mark.skip(reason="TODO: HTTP 401 from prism") @parametrize async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None: diff --git a/tests/api_resources/cloudforce_one/threat_events/test_tags.py b/tests/api_resources/cloudforce_one/threat_events/test_tags.py index 4187ab44687..ae938923fd1 100644 --- a/tests/api_resources/cloudforce_one/threat_events/test_tags.py +++ b/tests/api_resources/cloudforce_one/threat_events/test_tags.py @@ -33,16 +33,32 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None: account_id="account_id", value="APT28", active_duration="activeDuration", - actor_category="actorCategory", + actor_category="Nation State", + aliases=[ + { + "value": "Fancy Bear", + "confidence": 8, + "tlp": "amber", + } + ], alias_group_names=["string"], alias_group_names_internal=["string"], analytic_priority=0, attribution_confidence="attributionConfidence", + attribution_confidence_score=7, attribution_organization="attributionOrganization", category_uuid="12345678-1234-1234-1234-1234567890ab", + date_of_discovery="2024-01-15", external_reference_links=["string"], + internal_aliases=[ + { + "value": "Fancy Bear", + "confidence": 8, + "tlp": "amber", + } + ], internal_description="internalDescription", - motive="motive", + motive="Espionage", opsec_level="opsecLevel", origin_country_iso="originCountryISO", priority=0, @@ -109,16 +125,32 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare account_id="account_id", value="APT28", active_duration="activeDuration", - actor_category="actorCategory", + actor_category="Nation State", + aliases=[ + { + "value": "Fancy Bear", + "confidence": 8, + "tlp": "amber", + } + ], alias_group_names=["string"], alias_group_names_internal=["string"], analytic_priority=0, attribution_confidence="attributionConfidence", + attribution_confidence_score=7, attribution_organization="attributionOrganization", category_uuid="12345678-1234-1234-1234-1234567890ab", + date_of_discovery="2024-01-15", external_reference_links=["string"], + internal_aliases=[ + { + "value": "Fancy Bear", + "confidence": 8, + "tlp": "amber", + } + ], internal_description="internalDescription", - motive="motive", + motive="Espionage", opsec_level="opsecLevel", origin_country_iso="originCountryISO", priority=0, From 409955fd1c1d5d385514c952e9e57786f3eb4b75 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 17:39:54 +0000 Subject: [PATCH 27/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 25fc4e8cda7..212da94fde9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2418 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-59f76d419fb40364c15f3c2b57f38cca5ea9e88d8eddbd45bb729f8480967c25.yml -openapi_spec_hash: 0c620418d87503a65cd244aa980fd774 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-d243a405486a776fde567c6cd063083f78866d97bafc66c621e311c4ec37faa8.yml +openapi_spec_hash: 5bdca7468c199405ddfa3209f852762b config_hash: cd80863b2a094f3805409e4a5676b0b8 From 56b8ae2b60883ed08fbdf85467f074e1e40232a7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 20:17:56 +0000 Subject: [PATCH 28/29] chore(api): update composite API spec --- .stats.yml | 4 +-- src/cloudflare/resources/iam/sso.py | 28 +++++++++++-------- src/cloudflare/resources/user/user.py | 10 +++++-- .../tokens/permission_group_get_response.py | 19 +++++++++++++ .../tokens/permission_group_list_response.py | 19 +++++++++++++ .../tokens/permission_group_list_response.py | 19 +++++++++++++ 6 files changed, 83 insertions(+), 16 deletions(-) diff --git a/.stats.yml b/.stats.yml index 212da94fde9..c42f30a5ba4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 2418 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-d243a405486a776fde567c6cd063083f78866d97bafc66c621e311c4ec37faa8.yml -openapi_spec_hash: 5bdca7468c199405ddfa3209f852762b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-bd56ab70f18eeddb377203f0766f19e3c4d91162e1782a20848e579729778608.yml +openapi_spec_hash: 75ded8f4018def0946f2b3e23ca4b929 config_hash: cd80863b2a094f3805409e4a5676b0b8 diff --git a/src/cloudflare/resources/iam/sso.py b/src/cloudflare/resources/iam/sso.py index b759052b831..80229a73142 100644 --- a/src/cloudflare/resources/iam/sso.py +++ b/src/cloudflare/resources/iam/sso.py @@ -65,7 +65,8 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[SSOCreateResponse]: """ - Initialize new SSO connector + Creates a new SSO connector for logging into Cloudflare through an identity + provider. Args: account_id: Account identifier tag. @@ -121,7 +122,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[SSOUpdateResponse]: """ - Update SSO connector state + Updates the state or configuration of an SSO connector. Args: account_id: Account identifier tag. @@ -179,7 +180,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSinglePage[SSOListResponse]: """ - Get all SSO connectors + Lists all SSO connectors configured for the account. Args: account_id: Account identifier tag. @@ -216,7 +217,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[SSODeleteResponse]: """ - Delete SSO connector + Deletes an SSO connector from the account. Args: account_id: Account identifier tag. @@ -264,7 +265,8 @@ def begin_verification( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SSOBeginVerificationResponse: """ - Begin SSO connector verification + Validates the user has added the DNS TXT record required for validating + ownership of the domain they are trying to set up a connector for. Args: account_id: Account identifier tag. @@ -308,7 +310,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[SSOGetResponse]: """ - Get single SSO connector + Retrieves details for a specific SSO connector. Args: account_id: Account identifier tag. @@ -379,7 +381,8 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[SSOCreateResponse]: """ - Initialize new SSO connector + Creates a new SSO connector for logging into Cloudflare through an identity + provider. Args: account_id: Account identifier tag. @@ -435,7 +438,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[SSOUpdateResponse]: """ - Update SSO connector state + Updates the state or configuration of an SSO connector. Args: account_id: Account identifier tag. @@ -493,7 +496,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[SSOListResponse, AsyncSinglePage[SSOListResponse]]: """ - Get all SSO connectors + Lists all SSO connectors configured for the account. Args: account_id: Account identifier tag. @@ -530,7 +533,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[SSODeleteResponse]: """ - Delete SSO connector + Deletes an SSO connector from the account. Args: account_id: Account identifier tag. @@ -578,7 +581,8 @@ async def begin_verification( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SSOBeginVerificationResponse: """ - Begin SSO connector verification + Validates the user has added the DNS TXT record required for validating + ownership of the domain they are trying to set up a connector for. Args: account_id: Account identifier tag. @@ -622,7 +626,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[SSOGetResponse]: """ - Get single SSO connector + Retrieves details for a specific SSO connector. Args: account_id: Account identifier tag. diff --git a/src/cloudflare/resources/user/user.py b/src/cloudflare/resources/user/user.py index 60793fda83b..53273ad0162 100644 --- a/src/cloudflare/resources/user/user.py +++ b/src/cloudflare/resources/user/user.py @@ -198,7 +198,10 @@ def get( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[UserGetResponse]: - """User Details""" + """ + Retrieves detailed information about the currently authenticated user, including + email, name, and account memberships. + """ return self._get( "/user", options=make_request_options( @@ -329,7 +332,10 @@ async def get( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[UserGetResponse]: - """User Details""" + """ + Retrieves detailed information about the currently authenticated user, including + email, name, and account memberships. + """ return await self._get( "/user", options=make_request_options( diff --git a/src/cloudflare/types/accounts/tokens/permission_group_get_response.py b/src/cloudflare/types/accounts/tokens/permission_group_get_response.py index 81d6a1d88ba..3034896f531 100644 --- a/src/cloudflare/types/accounts/tokens/permission_group_get_response.py +++ b/src/cloudflare/types/accounts/tokens/permission_group_get_response.py @@ -12,6 +12,25 @@ class PermissionGroupGetResponseItem(BaseModel): id: Optional[str] = None """Public ID.""" + category: Optional[ + Literal[ + "developer_platform", + "ai_and_machine_learning", + "dns_and_zones", + "app_security", + "rules_and_configuration", + "cloudflare_one_and_zero_trust", + "analytics_and_logs", + "network_services", + "media", + "email_and_messaging", + "cache_and_performance", + "account_and_billing", + "other", + ] + ] = None + """Product category that this permission group belongs to.""" + name: Optional[str] = None """Permission Group Name""" diff --git a/src/cloudflare/types/accounts/tokens/permission_group_list_response.py b/src/cloudflare/types/accounts/tokens/permission_group_list_response.py index b34b7ff89c2..a251784a991 100644 --- a/src/cloudflare/types/accounts/tokens/permission_group_list_response.py +++ b/src/cloudflare/types/accounts/tokens/permission_group_list_response.py @@ -12,6 +12,25 @@ class PermissionGroupListResponse(BaseModel): id: Optional[str] = None """Public ID.""" + category: Optional[ + Literal[ + "developer_platform", + "ai_and_machine_learning", + "dns_and_zones", + "app_security", + "rules_and_configuration", + "cloudflare_one_and_zero_trust", + "analytics_and_logs", + "network_services", + "media", + "email_and_messaging", + "cache_and_performance", + "account_and_billing", + "other", + ] + ] = None + """Product category that this permission group belongs to.""" + name: Optional[str] = None """Permission Group Name""" diff --git a/src/cloudflare/types/user/tokens/permission_group_list_response.py b/src/cloudflare/types/user/tokens/permission_group_list_response.py index b34b7ff89c2..a251784a991 100644 --- a/src/cloudflare/types/user/tokens/permission_group_list_response.py +++ b/src/cloudflare/types/user/tokens/permission_group_list_response.py @@ -12,6 +12,25 @@ class PermissionGroupListResponse(BaseModel): id: Optional[str] = None """Public ID.""" + category: Optional[ + Literal[ + "developer_platform", + "ai_and_machine_learning", + "dns_and_zones", + "app_security", + "rules_and_configuration", + "cloudflare_one_and_zero_trust", + "analytics_and_logs", + "network_services", + "media", + "email_and_messaging", + "cache_and_performance", + "account_and_billing", + "other", + ] + ] = None + """Product category that this permission group belongs to.""" + name: Optional[str] = None """Permission Group Name""" From 7888a658a88df0395f40194fd9ba47cd5efe5aaa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 21:46:24 +0000 Subject: [PATCH 29/29] chore(api): update composite API spec --- .stats.yml | 6 +- src/cloudflare/resources/ssl/__init__.py | 14 -- src/cloudflare/resources/ssl/api.md | 12 -- .../resources/ssl/recommendations.py | 186 ------------------ src/cloudflare/resources/ssl/ssl.py | 32 --- src/cloudflare/types/ssl/__init__.py | 1 - .../types/ssl/recommendation_get_response.py | 25 --- .../api_resources/ssl/test_recommendations.py | 112 ----------- 8 files changed, 3 insertions(+), 385 deletions(-) delete mode 100644 src/cloudflare/resources/ssl/recommendations.py delete mode 100644 src/cloudflare/types/ssl/recommendation_get_response.py delete mode 100644 tests/api_resources/ssl/test_recommendations.py diff --git a/.stats.yml b/.stats.yml index c42f30a5ba4..edc506ab4e9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 2418 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-bd56ab70f18eeddb377203f0766f19e3c4d91162e1782a20848e579729778608.yml -openapi_spec_hash: 75ded8f4018def0946f2b3e23ca4b929 +configured_endpoints: 2417 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-08f6951dff1022387f05582fe54cd8eff8e606ad0809b0e3efb5874ebb11cf01.yml +openapi_spec_hash: 6b8ffc5d6d40adc6dc83fbb44811ef18 config_hash: cd80863b2a094f3805409e4a5676b0b8 diff --git a/src/cloudflare/resources/ssl/__init__.py b/src/cloudflare/resources/ssl/__init__.py index 993c9403c31..e65d8d199b2 100644 --- a/src/cloudflare/resources/ssl/__init__.py +++ b/src/cloudflare/resources/ssl/__init__.py @@ -32,14 +32,6 @@ VerificationResourceWithStreamingResponse, AsyncVerificationResourceWithStreamingResponse, ) -from .recommendations import ( - RecommendationsResource, - AsyncRecommendationsResource, - RecommendationsResourceWithRawResponse, - AsyncRecommendationsResourceWithRawResponse, - RecommendationsResourceWithStreamingResponse, - AsyncRecommendationsResourceWithStreamingResponse, -) from .certificate_packs import ( CertificatePacksResource, AsyncCertificatePacksResource, @@ -70,12 +62,6 @@ "AsyncCertificatePacksResourceWithRawResponse", "CertificatePacksResourceWithStreamingResponse", "AsyncCertificatePacksResourceWithStreamingResponse", - "RecommendationsResource", - "AsyncRecommendationsResource", - "RecommendationsResourceWithRawResponse", - "AsyncRecommendationsResourceWithRawResponse", - "RecommendationsResourceWithStreamingResponse", - "AsyncRecommendationsResourceWithStreamingResponse", "AutoOriginTLSKexResource", "AsyncAutoOriginTLSKexResource", "AutoOriginTLSKexResourceWithRawResponse", diff --git a/src/cloudflare/resources/ssl/api.md b/src/cloudflare/resources/ssl/api.md index bd6b4cb1352..bbed721e7d2 100644 --- a/src/cloudflare/resources/ssl/api.md +++ b/src/cloudflare/resources/ssl/api.md @@ -44,18 +44,6 @@ Methods: - client.ssl.certificate_packs.quota.get(\*, zone_id) -> Optional[QuotaGetResponse] -## Recommendations - -Types: - -```python -from cloudflare.types.ssl import RecommendationGetResponse -``` - -Methods: - -- client.ssl.recommendations.get(\*, zone_id) -> RecommendationGetResponse - ## AutoOriginTLSKex Types: diff --git a/src/cloudflare/resources/ssl/recommendations.py b/src/cloudflare/resources/ssl/recommendations.py deleted file mode 100644 index 3d3f2366583..00000000000 --- a/src/cloudflare/resources/ssl/recommendations.py +++ /dev/null @@ -1,186 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import typing_extensions -from typing import Type, cast - -import httpx - -from ..._types import Body, Query, Headers, NotGiven, not_given -from ..._utils import path_template -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._wrappers import ResultWrapper -from ..._base_client import make_request_options -from ...types.ssl.recommendation_get_response import RecommendationGetResponse - -__all__ = ["RecommendationsResource", "AsyncRecommendationsResource"] - - -class RecommendationsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> RecommendationsResourceWithRawResponse: - """ - 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 RecommendationsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> RecommendationsResourceWithStreamingResponse: - """ - 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 RecommendationsResourceWithStreamingResponse(self) - - @typing_extensions.deprecated("SSL/TLS Recommender has been decommissioned in favor of Automatic SSL/TLS") - 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, - ) -> RecommendationGetResponse: - """ - Retrieve the SSL/TLS Recommender's recommendation for a zone. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not zone_id: - raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}") - return self._get( - path_template("/zones/{zone_id}/ssl/recommendation", zone_id=zone_id), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - post_parser=ResultWrapper[RecommendationGetResponse]._unwrapper, - ), - cast_to=cast(Type[RecommendationGetResponse], ResultWrapper[RecommendationGetResponse]), - ) - - -class AsyncRecommendationsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncRecommendationsResourceWithRawResponse: - """ - 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 AsyncRecommendationsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncRecommendationsResourceWithStreamingResponse: - """ - 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 AsyncRecommendationsResourceWithStreamingResponse(self) - - @typing_extensions.deprecated("SSL/TLS Recommender has been decommissioned in favor of Automatic SSL/TLS") - 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, - ) -> RecommendationGetResponse: - """ - Retrieve the SSL/TLS Recommender's recommendation for a zone. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not zone_id: - raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}") - return await self._get( - path_template("/zones/{zone_id}/ssl/recommendation", zone_id=zone_id), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - post_parser=ResultWrapper[RecommendationGetResponse]._unwrapper, - ), - cast_to=cast(Type[RecommendationGetResponse], ResultWrapper[RecommendationGetResponse]), - ) - - -class RecommendationsResourceWithRawResponse: - def __init__(self, recommendations: RecommendationsResource) -> None: - self._recommendations = recommendations - - self.get = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - recommendations.get, # pyright: ignore[reportDeprecated], - ) - ) - - -class AsyncRecommendationsResourceWithRawResponse: - def __init__(self, recommendations: AsyncRecommendationsResource) -> None: - self._recommendations = recommendations - - self.get = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - recommendations.get, # pyright: ignore[reportDeprecated], - ) - ) - - -class RecommendationsResourceWithStreamingResponse: - def __init__(self, recommendations: RecommendationsResource) -> None: - self._recommendations = recommendations - - self.get = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - recommendations.get, # pyright: ignore[reportDeprecated], - ) - ) - - -class AsyncRecommendationsResourceWithStreamingResponse: - def __init__(self, recommendations: AsyncRecommendationsResource) -> None: - self._recommendations = recommendations - - self.get = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - recommendations.get, # pyright: ignore[reportDeprecated], - ) - ) diff --git a/src/cloudflare/resources/ssl/ssl.py b/src/cloudflare/resources/ssl/ssl.py index dffad263b4f..7ba9eaeabab 100644 --- a/src/cloudflare/resources/ssl/ssl.py +++ b/src/cloudflare/resources/ssl/ssl.py @@ -20,14 +20,6 @@ VerificationResourceWithStreamingResponse, AsyncVerificationResourceWithStreamingResponse, ) -from .recommendations import ( - RecommendationsResource, - AsyncRecommendationsResource, - RecommendationsResourceWithRawResponse, - AsyncRecommendationsResourceWithRawResponse, - RecommendationsResourceWithStreamingResponse, - AsyncRecommendationsResourceWithStreamingResponse, -) from .auto_origin_tls_kex import ( AutoOriginTLSKexResource, AsyncAutoOriginTLSKexResource, @@ -65,10 +57,6 @@ def analyze(self) -> AnalyzeResource: def certificate_packs(self) -> CertificatePacksResource: return CertificatePacksResource(self._client) - @cached_property - def recommendations(self) -> RecommendationsResource: - return RecommendationsResource(self._client) - @cached_property def auto_origin_tls_kex(self) -> AutoOriginTLSKexResource: return AutoOriginTLSKexResource(self._client) @@ -110,10 +98,6 @@ def analyze(self) -> AsyncAnalyzeResource: def certificate_packs(self) -> AsyncCertificatePacksResource: return AsyncCertificatePacksResource(self._client) - @cached_property - def recommendations(self) -> AsyncRecommendationsResource: - return AsyncRecommendationsResource(self._client) - @cached_property def auto_origin_tls_kex(self) -> AsyncAutoOriginTLSKexResource: return AsyncAutoOriginTLSKexResource(self._client) @@ -158,10 +142,6 @@ def analyze(self) -> AnalyzeResourceWithRawResponse: def certificate_packs(self) -> CertificatePacksResourceWithRawResponse: return CertificatePacksResourceWithRawResponse(self._ssl.certificate_packs) - @cached_property - def recommendations(self) -> RecommendationsResourceWithRawResponse: - return RecommendationsResourceWithRawResponse(self._ssl.recommendations) - @cached_property def auto_origin_tls_kex(self) -> AutoOriginTLSKexResourceWithRawResponse: return AutoOriginTLSKexResourceWithRawResponse(self._ssl.auto_origin_tls_kex) @@ -187,10 +167,6 @@ def analyze(self) -> AsyncAnalyzeResourceWithRawResponse: def certificate_packs(self) -> AsyncCertificatePacksResourceWithRawResponse: return AsyncCertificatePacksResourceWithRawResponse(self._ssl.certificate_packs) - @cached_property - def recommendations(self) -> AsyncRecommendationsResourceWithRawResponse: - return AsyncRecommendationsResourceWithRawResponse(self._ssl.recommendations) - @cached_property def auto_origin_tls_kex(self) -> AsyncAutoOriginTLSKexResourceWithRawResponse: return AsyncAutoOriginTLSKexResourceWithRawResponse(self._ssl.auto_origin_tls_kex) @@ -216,10 +192,6 @@ def analyze(self) -> AnalyzeResourceWithStreamingResponse: def certificate_packs(self) -> CertificatePacksResourceWithStreamingResponse: return CertificatePacksResourceWithStreamingResponse(self._ssl.certificate_packs) - @cached_property - def recommendations(self) -> RecommendationsResourceWithStreamingResponse: - return RecommendationsResourceWithStreamingResponse(self._ssl.recommendations) - @cached_property def auto_origin_tls_kex(self) -> AutoOriginTLSKexResourceWithStreamingResponse: return AutoOriginTLSKexResourceWithStreamingResponse(self._ssl.auto_origin_tls_kex) @@ -245,10 +217,6 @@ def analyze(self) -> AsyncAnalyzeResourceWithStreamingResponse: def certificate_packs(self) -> AsyncCertificatePacksResourceWithStreamingResponse: return AsyncCertificatePacksResourceWithStreamingResponse(self._ssl.certificate_packs) - @cached_property - def recommendations(self) -> AsyncRecommendationsResourceWithStreamingResponse: - return AsyncRecommendationsResourceWithStreamingResponse(self._ssl.recommendations) - @cached_property def auto_origin_tls_kex(self) -> AsyncAutoOriginTLSKexResourceWithStreamingResponse: return AsyncAutoOriginTLSKexResourceWithStreamingResponse(self._ssl.auto_origin_tls_kex) diff --git a/src/cloudflare/types/ssl/__init__.py b/src/cloudflare/types/ssl/__init__.py index aeffbe95988..084e8c1e34d 100644 --- a/src/cloudflare/types/ssl/__init__.py +++ b/src/cloudflare/types/ssl/__init__.py @@ -12,7 +12,6 @@ from .verification_edit_params import VerificationEditParams as VerificationEditParams from .verification_get_response import VerificationGetResponse as VerificationGetResponse from .verification_edit_response import VerificationEditResponse as VerificationEditResponse -from .recommendation_get_response import RecommendationGetResponse as RecommendationGetResponse from .certificate_pack_edit_params import CertificatePackEditParams as CertificatePackEditParams from .certificate_pack_list_params import CertificatePackListParams as CertificatePackListParams from .certificate_pack_get_response import CertificatePackGetResponse as CertificatePackGetResponse diff --git a/src/cloudflare/types/ssl/recommendation_get_response.py b/src/cloudflare/types/ssl/recommendation_get_response.py deleted file mode 100644 index 9a9d74b7e27..00000000000 --- a/src/cloudflare/types/ssl/recommendation_get_response.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["RecommendationGetResponse"] - - -class RecommendationGetResponse(BaseModel): - id: str - - editable: bool - """Whether this setting can be updated or not.""" - - modified_on: datetime - """Last time this setting was modified.""" - - value: Literal["auto", "custom"] - """Current setting of the automatic SSL/TLS.""" - - next_scheduled_scan: Optional[datetime] = None - """Next time this zone will be scanned by the Automatic SSL/TLS.""" diff --git a/tests/api_resources/ssl/test_recommendations.py b/tests/api_resources/ssl/test_recommendations.py deleted file mode 100644 index 972bf1c1ffc..00000000000 --- a/tests/api_resources/ssl/test_recommendations.py +++ /dev/null @@ -1,112 +0,0 @@ -# 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.ssl import RecommendationGetResponse - -# pyright: reportDeprecated=false - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestRecommendations: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_get(self, client: Cloudflare) -> None: - with pytest.warns(DeprecationWarning): - recommendation = client.ssl.recommendations.get( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) - - assert_matches_type(RecommendationGetResponse, recommendation, path=["response"]) - - @parametrize - def test_raw_response_get(self, client: Cloudflare) -> None: - with pytest.warns(DeprecationWarning): - response = client.ssl.recommendations.with_raw_response.get( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - recommendation = response.parse() - assert_matches_type(RecommendationGetResponse, recommendation, path=["response"]) - - @parametrize - def test_streaming_response_get(self, client: Cloudflare) -> None: - with pytest.warns(DeprecationWarning): - with client.ssl.recommendations.with_streaming_response.get( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - recommendation = response.parse() - assert_matches_type(RecommendationGetResponse, recommendation, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_get(self, client: Cloudflare) -> None: - with pytest.warns(DeprecationWarning): - with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"): - client.ssl.recommendations.with_raw_response.get( - zone_id="", - ) - - -class TestAsyncRecommendations: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_get(self, async_client: AsyncCloudflare) -> None: - with pytest.warns(DeprecationWarning): - recommendation = await async_client.ssl.recommendations.get( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) - - assert_matches_type(RecommendationGetResponse, recommendation, path=["response"]) - - @parametrize - async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.ssl.recommendations.with_raw_response.get( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - recommendation = await response.parse() - assert_matches_type(RecommendationGetResponse, recommendation, path=["response"]) - - @parametrize - async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.ssl.recommendations.with_streaming_response.get( - zone_id="023e105f4ecef8ad9ca31a8372d0c353", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - recommendation = await response.parse() - assert_matches_type(RecommendationGetResponse, recommendation, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_get(self, async_client: AsyncCloudflare) -> None: - with pytest.warns(DeprecationWarning): - with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"): - await async_client.ssl.recommendations.with_raw_response.get( - zone_id="", - )