diff --git a/.release-please-manifest.json b/.release-please-manifest.json index db00cae..0b8a7c6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.67.0" + ".": "0.68.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index a164009..4127b5f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 119 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-51549f813f3002e18c6ca8d850cc0c7932828d511c151e0412c73b6798d19e30.yml -openapi_spec_hash: ee77b293c4bda91c1a32cfdd12b8739e -config_hash: 57567e00b41af47cef1b78e51b747aa0 +configured_endpoints: 120 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-e8afdbeac9332cf79200c2eb873e532104fd0a7472b08e63cde6c857a87cf0c3.yml +openapi_spec_hash: 2525caf30dffbdd83c83948201f11a52 +config_hash: 03c7e57f268c750e2415831662e95969 diff --git a/CHANGELOG.md b/CHANGELOG.md index 342bd47..746ed91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.68.0 (2026-06-14) + +Full Changelog: [v0.67.0...v0.68.0](https://github.com/kernel/kernel-python-sdk/compare/v0.67.0...v0.68.0) + +### Features + +* Add API key rotate endpoint ([5ad5ee5](https://github.com/kernel/kernel-python-sdk/commit/5ad5ee511057a6bcaa69fee40087e3cf490a4494)) +* **api:** surface deleted/expired API keys for audit trail (KERNEL-1350) ([6f16159](https://github.com/kernel/kernel-python-sdk/commit/6f16159d28de8129b12dfe2e144e9d1db39ad4ec)) + + +### Refactors + +* **api:** align API key audit surface with browser sibling (KERNEL-1350) ([bdf7a0d](https://github.com/kernel/kernel-python-sdk/commit/bdf7a0dec6ed448f4846186fba05e4cbb7eb0846)) + ## 0.67.0 (2026-06-11) Full Changelog: [v0.66.0...v0.67.0](https://github.com/kernel/kernel-python-sdk/compare/v0.66.0...v0.67.0) diff --git a/api.md b/api.md index 4b24f10..8ba0dd4 100644 --- a/api.md +++ b/api.md @@ -449,10 +449,11 @@ from kernel.types import APIKey, CreatedAPIKey Methods: - client.api_keys.create(\*\*params) -> CreatedAPIKey -- client.api_keys.retrieve(id) -> APIKey +- client.api_keys.retrieve(id, \*\*params) -> APIKey - client.api_keys.update(id, \*\*params) -> APIKey - client.api_keys.list(\*\*params) -> SyncOffsetPagination[APIKey] - client.api_keys.delete(id) -> None +- client.api_keys.rotate(id, \*\*params) -> CreatedAPIKey # CredentialProviders diff --git a/pyproject.toml b/pyproject.toml index 035e174..4020062 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.67.0" +version = "0.68.0" description = "The official Python library for the kernel API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/kernel/_version.py b/src/kernel/_version.py index 7c28de4..b0e3713 100644 --- a/src/kernel/_version.py +++ b/src/kernel/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "kernel" -__version__ = "0.67.0" # x-release-please-version +__version__ = "0.68.0" # x-release-please-version diff --git a/src/kernel/resources/api_keys.py b/src/kernel/resources/api_keys.py index 93e8015..da28774 100644 --- a/src/kernel/resources/api_keys.py +++ b/src/kernel/resources/api_keys.py @@ -7,7 +7,13 @@ import httpx -from ..types import api_key_list_params, api_key_create_params, api_key_update_params +from ..types import ( + api_key_list_params, + api_key_create_params, + api_key_rotate_params, + api_key_update_params, + api_key_retrieve_params, +) from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property @@ -99,6 +105,7 @@ def retrieve( self, 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, @@ -112,6 +119,9 @@ def retrieve( masked. Args: + include_deleted: When true, return the API key even if it has been deleted (soft-deleted), for + audit purposes. Defaults to false, which returns 404 for a deleted key. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -125,7 +135,13 @@ def retrieve( return self._get( path_template("/org/api_keys/{id}", id=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}, api_key_retrieve_params.APIKeyRetrieveParams + ), ), cast_to=APIKey, ) @@ -170,11 +186,13 @@ def update( def list( self, *, + include_deleted: bool | Omit = omit, limit: int | Omit = omit, offset: int | Omit = omit, query: str | Omit = omit, sort_by: Literal["created_at", "name", "expires_at"] | Omit = omit, sort_direction: Literal["asc", "desc"] | Omit = omit, + status: Literal["active", "deleted", "all"] | 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, @@ -187,6 +205,9 @@ def list( API keys are masked. Args: + include_deleted: Deprecated: use status=all instead. When true, include deleted (soft-deleted) + API keys in the results for audit purposes. + limit: Maximum number of results to return offset: Number of results to skip @@ -198,6 +219,10 @@ def list( sort_direction: Sort direction for API keys. + status: Filter API keys by status. "active" returns keys that are not deleted (default; + expired-but-not-deleted keys are still included), "deleted" returns only + soft-deleted keys, "all" returns both. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -216,11 +241,13 @@ def list( timeout=timeout, query=maybe_transform( { + "include_deleted": include_deleted, "limit": limit, "offset": offset, "query": query, "sort_by": sort_by, "sort_direction": sort_direction, + "status": status, }, api_key_list_params.APIKeyListParams, ), @@ -262,6 +289,57 @@ def delete( cast_to=NoneType, ) + def rotate( + self, + id: str, + *, + days_to_expire: Optional[int] | Omit = omit, + expire_in_days: Optional[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, + ) -> CreatedAPIKey: + """Rotate an API key. + + Issues a new key that copies the name and project of the + rotated key, and schedules the rotated key to expire after a grace period so + in-flight callers can swap over. The new plaintext key is returned once. + + Args: + days_to_expire: Lifetime in days for the new key, up to 3650. Omit to reuse the rotated key's + original lifetime, or never-expires if it had none. + + expire_in_days: Grace period in days before the rotated key expires. Use 0 to expire it + immediately. Omit for the default grace period of 7 days. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + path_template("/org/api_keys/{id}/rotate", id=id), + body=maybe_transform( + { + "days_to_expire": days_to_expire, + "expire_in_days": expire_in_days, + }, + api_key_rotate_params.APIKeyRotateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CreatedAPIKey, + ) + class AsyncAPIKeysResource(AsyncAPIResource): """Create and manage API keys for organization and project-scoped access.""" @@ -336,6 +414,7 @@ async def retrieve( self, 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, @@ -349,6 +428,9 @@ async def retrieve( masked. Args: + include_deleted: When true, return the API key even if it has been deleted (soft-deleted), for + audit purposes. Defaults to false, which returns 404 for a deleted key. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -362,7 +444,13 @@ async def retrieve( return await self._get( path_template("/org/api_keys/{id}", id=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}, api_key_retrieve_params.APIKeyRetrieveParams + ), ), cast_to=APIKey, ) @@ -407,11 +495,13 @@ async def update( def list( self, *, + include_deleted: bool | Omit = omit, limit: int | Omit = omit, offset: int | Omit = omit, query: str | Omit = omit, sort_by: Literal["created_at", "name", "expires_at"] | Omit = omit, sort_direction: Literal["asc", "desc"] | Omit = omit, + status: Literal["active", "deleted", "all"] | 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, @@ -424,6 +514,9 @@ def list( API keys are masked. Args: + include_deleted: Deprecated: use status=all instead. When true, include deleted (soft-deleted) + API keys in the results for audit purposes. + limit: Maximum number of results to return offset: Number of results to skip @@ -435,6 +528,10 @@ def list( sort_direction: Sort direction for API keys. + status: Filter API keys by status. "active" returns keys that are not deleted (default; + expired-but-not-deleted keys are still included), "deleted" returns only + soft-deleted keys, "all" returns both. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -453,11 +550,13 @@ def list( timeout=timeout, query=maybe_transform( { + "include_deleted": include_deleted, "limit": limit, "offset": offset, "query": query, "sort_by": sort_by, "sort_direction": sort_direction, + "status": status, }, api_key_list_params.APIKeyListParams, ), @@ -499,6 +598,57 @@ async def delete( cast_to=NoneType, ) + async def rotate( + self, + id: str, + *, + days_to_expire: Optional[int] | Omit = omit, + expire_in_days: Optional[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, + ) -> CreatedAPIKey: + """Rotate an API key. + + Issues a new key that copies the name and project of the + rotated key, and schedules the rotated key to expire after a grace period so + in-flight callers can swap over. The new plaintext key is returned once. + + Args: + days_to_expire: Lifetime in days for the new key, up to 3650. Omit to reuse the rotated key's + original lifetime, or never-expires if it had none. + + expire_in_days: Grace period in days before the rotated key expires. Use 0 to expire it + immediately. Omit for the default grace period of 7 days. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + path_template("/org/api_keys/{id}/rotate", id=id), + body=await async_maybe_transform( + { + "days_to_expire": days_to_expire, + "expire_in_days": expire_in_days, + }, + api_key_rotate_params.APIKeyRotateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CreatedAPIKey, + ) + class APIKeysResourceWithRawResponse: def __init__(self, api_keys: APIKeysResource) -> None: @@ -519,6 +669,9 @@ def __init__(self, api_keys: APIKeysResource) -> None: self.delete = to_raw_response_wrapper( api_keys.delete, ) + self.rotate = to_raw_response_wrapper( + api_keys.rotate, + ) class AsyncAPIKeysResourceWithRawResponse: @@ -540,6 +693,9 @@ def __init__(self, api_keys: AsyncAPIKeysResource) -> None: self.delete = async_to_raw_response_wrapper( api_keys.delete, ) + self.rotate = async_to_raw_response_wrapper( + api_keys.rotate, + ) class APIKeysResourceWithStreamingResponse: @@ -561,6 +717,9 @@ def __init__(self, api_keys: APIKeysResource) -> None: self.delete = to_streamed_response_wrapper( api_keys.delete, ) + self.rotate = to_streamed_response_wrapper( + api_keys.rotate, + ) class AsyncAPIKeysResourceWithStreamingResponse: @@ -582,3 +741,6 @@ def __init__(self, api_keys: AsyncAPIKeysResource) -> None: self.delete = async_to_streamed_response_wrapper( api_keys.delete, ) + self.rotate = async_to_streamed_response_wrapper( + api_keys.rotate, + ) diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py index 1e9b39a..e38ba40 100644 --- a/src/kernel/types/__init__.py +++ b/src/kernel/types/__init__.py @@ -39,6 +39,7 @@ from .proxy_list_response import ProxyListResponse as ProxyListResponse from .proxy_check_response import ProxyCheckResponse as ProxyCheckResponse from .api_key_create_params import APIKeyCreateParams as APIKeyCreateParams +from .api_key_rotate_params import APIKeyRotateParams as APIKeyRotateParams from .api_key_update_params import APIKeyUpdateParams as APIKeyUpdateParams from .browser_create_params import BrowserCreateParams as BrowserCreateParams from .browser_curl_response import BrowserCurlResponse as BrowserCurlResponse @@ -54,6 +55,7 @@ from .deployment_state_event import DeploymentStateEvent as DeploymentStateEvent from .invocation_list_params import InvocationListParams as InvocationListParams from .invocation_state_event import InvocationStateEvent as InvocationStateEvent +from .api_key_retrieve_params import APIKeyRetrieveParams as APIKeyRetrieveParams from .browser_create_response import BrowserCreateResponse as BrowserCreateResponse from .browser_retrieve_params import BrowserRetrieveParams as BrowserRetrieveParams from .browser_update_response import BrowserUpdateResponse as BrowserUpdateResponse diff --git a/src/kernel/types/api_key.py b/src/kernel/types/api_key.py index 6df577f..4451952 100644 --- a/src/kernel/types/api_key.py +++ b/src/kernel/types/api_key.py @@ -28,6 +28,12 @@ class APIKey(BaseModel): created_by: CreatedBy + deleted_at: Optional[datetime] = None + """When the API key was deleted (soft-deleted). + + Null for keys that have not been deleted. + """ + expires_at: Optional[datetime] = None """When the API key expires""" diff --git a/src/kernel/types/api_key_list_params.py b/src/kernel/types/api_key_list_params.py index 79a9c41..3ddc2a3 100644 --- a/src/kernel/types/api_key_list_params.py +++ b/src/kernel/types/api_key_list_params.py @@ -8,6 +8,13 @@ class APIKeyListParams(TypedDict, total=False): + include_deleted: bool + """Deprecated: use status=all instead. + + When true, include deleted (soft-deleted) API keys in the results for audit + purposes. + """ + limit: int """Maximum number of results to return""" @@ -25,3 +32,11 @@ class APIKeyListParams(TypedDict, total=False): sort_direction: Literal["asc", "desc"] """Sort direction for API keys.""" + + status: Literal["active", "deleted", "all"] + """Filter API keys by status. + + "active" returns keys that are not deleted (default; expired-but-not-deleted + keys are still included), "deleted" returns only soft-deleted keys, "all" + returns both. + """ diff --git a/src/kernel/types/api_key_retrieve_params.py b/src/kernel/types/api_key_retrieve_params.py new file mode 100644 index 0000000..e2b9ea3 --- /dev/null +++ b/src/kernel/types/api_key_retrieve_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 TypedDict + +__all__ = ["APIKeyRetrieveParams"] + + +class APIKeyRetrieveParams(TypedDict, total=False): + include_deleted: bool + """ + When true, return the API key even if it has been deleted (soft-deleted), for + audit purposes. Defaults to false, which returns 404 for a deleted key. + """ diff --git a/src/kernel/types/api_key_rotate_params.py b/src/kernel/types/api_key_rotate_params.py new file mode 100644 index 0000000..3810e58 --- /dev/null +++ b/src/kernel/types/api_key_rotate_params.py @@ -0,0 +1,23 @@ +# 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 TypedDict + +__all__ = ["APIKeyRotateParams"] + + +class APIKeyRotateParams(TypedDict, total=False): + days_to_expire: Optional[int] + """Lifetime in days for the new key, up to 3650. + + Omit to reuse the rotated key's original lifetime, or never-expires if it had + none. + """ + + expire_in_days: Optional[int] + """Grace period in days before the rotated key expires. + + Use 0 to expire it immediately. Omit for the default grace period of 7 days. + """ diff --git a/tests/api_resources/test_api_keys.py b/tests/api_resources/test_api_keys.py index 9c3abca..5fdb5dc 100644 --- a/tests/api_resources/test_api_keys.py +++ b/tests/api_resources/test_api_keys.py @@ -9,7 +9,10 @@ from kernel import Kernel, AsyncKernel from tests.utils import assert_matches_type -from kernel.types import APIKey, CreatedAPIKey +from kernel.types import ( + APIKey, + CreatedAPIKey, +) from kernel.pagination import SyncOffsetPagination, AsyncOffsetPagination base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -66,7 +69,16 @@ def test_streaming_response_create(self, client: Kernel) -> None: @parametrize def test_method_retrieve(self, client: Kernel) -> None: api_key = client.api_keys.retrieve( - "id", + id="id", + ) + assert_matches_type(APIKey, api_key, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_retrieve_with_all_params(self, client: Kernel) -> None: + api_key = client.api_keys.retrieve( + id="id", + include_deleted=True, ) assert_matches_type(APIKey, api_key, path=["response"]) @@ -74,7 +86,7 @@ def test_method_retrieve(self, client: Kernel) -> None: @parametrize def test_raw_response_retrieve(self, client: Kernel) -> None: response = client.api_keys.with_raw_response.retrieve( - "id", + id="id", ) assert response.is_closed is True @@ -86,7 +98,7 @@ def test_raw_response_retrieve(self, client: Kernel) -> None: @parametrize def test_streaming_response_retrieve(self, client: Kernel) -> None: with client.api_keys.with_streaming_response.retrieve( - "id", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -101,7 +113,7 @@ def test_streaming_response_retrieve(self, client: Kernel) -> None: def test_path_params_retrieve(self, client: Kernel) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): client.api_keys.with_raw_response.retrieve( - "", + id="", ) @pytest.mark.skip(reason="Mock server tests are disabled") @@ -160,11 +172,13 @@ def test_method_list(self, client: Kernel) -> None: @parametrize def test_method_list_with_all_params(self, client: Kernel) -> None: api_key = client.api_keys.list( + include_deleted=True, limit=100, offset=0, query="query", sort_by="created_at", sort_direction="asc", + status="active", ) assert_matches_type(SyncOffsetPagination[APIKey], api_key, path=["response"]) @@ -232,6 +246,58 @@ def test_path_params_delete(self, client: Kernel) -> None: "", ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_rotate(self, client: Kernel) -> None: + api_key = client.api_keys.rotate( + id="id", + ) + assert_matches_type(CreatedAPIKey, api_key, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_rotate_with_all_params(self, client: Kernel) -> None: + api_key = client.api_keys.rotate( + id="id", + days_to_expire=30, + expire_in_days=7, + ) + assert_matches_type(CreatedAPIKey, api_key, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_rotate(self, client: Kernel) -> None: + response = client.api_keys.with_raw_response.rotate( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(CreatedAPIKey, api_key, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_rotate(self, client: Kernel) -> None: + with client.api_keys.with_streaming_response.rotate( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(CreatedAPIKey, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_rotate(self, client: Kernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.api_keys.with_raw_response.rotate( + id="", + ) + class TestAsyncAPIKeys: parametrize = pytest.mark.parametrize( @@ -286,7 +352,16 @@ async def test_streaming_response_create(self, async_client: AsyncKernel) -> Non @parametrize async def test_method_retrieve(self, async_client: AsyncKernel) -> None: api_key = await async_client.api_keys.retrieve( - "id", + id="id", + ) + assert_matches_type(APIKey, api_key, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncKernel) -> None: + api_key = await async_client.api_keys.retrieve( + id="id", + include_deleted=True, ) assert_matches_type(APIKey, api_key, path=["response"]) @@ -294,7 +369,7 @@ async def test_method_retrieve(self, async_client: AsyncKernel) -> None: @parametrize async def test_raw_response_retrieve(self, async_client: AsyncKernel) -> None: response = await async_client.api_keys.with_raw_response.retrieve( - "id", + id="id", ) assert response.is_closed is True @@ -306,7 +381,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncKernel) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncKernel) -> None: async with async_client.api_keys.with_streaming_response.retrieve( - "id", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -321,7 +396,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncKernel) -> N async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): await async_client.api_keys.with_raw_response.retrieve( - "", + id="", ) @pytest.mark.skip(reason="Mock server tests are disabled") @@ -380,11 +455,13 @@ async def test_method_list(self, async_client: AsyncKernel) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncKernel) -> None: api_key = await async_client.api_keys.list( + include_deleted=True, limit=100, offset=0, query="query", sort_by="created_at", sort_direction="asc", + status="active", ) assert_matches_type(AsyncOffsetPagination[APIKey], api_key, path=["response"]) @@ -451,3 +528,55 @@ async def test_path_params_delete(self, async_client: AsyncKernel) -> None: await async_client.api_keys.with_raw_response.delete( "", ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_rotate(self, async_client: AsyncKernel) -> None: + api_key = await async_client.api_keys.rotate( + id="id", + ) + assert_matches_type(CreatedAPIKey, api_key, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_rotate_with_all_params(self, async_client: AsyncKernel) -> None: + api_key = await async_client.api_keys.rotate( + id="id", + days_to_expire=30, + expire_in_days=7, + ) + assert_matches_type(CreatedAPIKey, api_key, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_rotate(self, async_client: AsyncKernel) -> None: + response = await async_client.api_keys.with_raw_response.rotate( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = await response.parse() + assert_matches_type(CreatedAPIKey, api_key, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_rotate(self, async_client: AsyncKernel) -> None: + async with async_client.api_keys.with_streaming_response.rotate( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(CreatedAPIKey, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_rotate(self, async_client: AsyncKernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.api_keys.with_raw_response.rotate( + id="", + )