From f2464a663b082db061da32a6a50126095099e0bb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:06:57 +0000 Subject: [PATCH] feat: feat(d1): Add time travel `get_bookmark` and `restore` endpoints --- .stats.yml | 4 +- api.md | 33 +- src/cloudflare/resources/d1/d1.py | 6 +- .../resources/d1/database/__init__.py | 33 ++ .../resources/d1/{ => database}/database.py | 62 +++- .../resources/d1/database/time_travel.py | 340 ++++++++++++++++++ src/cloudflare/types/d1/database/__init__.py | 8 + .../time_travel_get_bookmark_params.py | 23 ++ .../time_travel_get_bookmark_response.py | 15 + .../d1/database/time_travel_restore_params.py | 25 ++ .../database/time_travel_restore_response.py | 27 ++ tests/api_resources/d1/database/__init__.py | 1 + .../d1/database/test_time_travel.py | 258 +++++++++++++ 13 files changed, 805 insertions(+), 30 deletions(-) create mode 100644 src/cloudflare/resources/d1/database/__init__.py rename src/cloudflare/resources/d1/{ => database}/database.py (96%) create mode 100644 src/cloudflare/resources/d1/database/time_travel.py create mode 100644 src/cloudflare/types/d1/database/__init__.py create mode 100644 src/cloudflare/types/d1/database/time_travel_get_bookmark_params.py create mode 100644 src/cloudflare/types/d1/database/time_travel_get_bookmark_response.py create mode 100644 src/cloudflare/types/d1/database/time_travel_restore_params.py create mode 100644 src/cloudflare/types/d1/database/time_travel_restore_response.py create mode 100644 tests/api_resources/d1/database/__init__.py create mode 100644 tests/api_resources/d1/database/test_time_travel.py diff --git a/.stats.yml b/.stats.yml index d2bb80362..dde18a709 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 1993 +configured_endpoints: 1995 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-28b84a5db03b38290dfe7ef1de2c68feb68762d7a8f207bdbea4d39a7aeef1ea.yml openapi_spec_hash: ba6bd61621e4be20b581f4f3bf0978d3 -config_hash: 316e765ff117ffcf8ecf5a3187c184b1 +config_hash: 605c16e61b71b1e41175315ae4fcc6c0 diff --git a/api.md b/api.md index 0c76d5b8d..77df8ee7a 100644 --- a/api.md +++ b/api.md @@ -5064,16 +5064,29 @@ from cloudflare.types.d1 import ( Methods: -- client.d1.database.create(\*, account_id, \*\*params) -> D1 -- client.d1.database.update(database_id, \*, account_id, \*\*params) -> D1 -- client.d1.database.list(\*, account_id, \*\*params) -> SyncV4PagePaginationArray[DatabaseListResponse] -- client.d1.database.delete(database_id, \*, account_id) -> object -- client.d1.database.edit(database_id, \*, account_id, \*\*params) -> D1 -- client.d1.database.export(database_id, \*, account_id, \*\*params) -> DatabaseExportResponse -- client.d1.database.get(database_id, \*, account_id) -> D1 -- client.d1.database.import\_(database_id, \*, account_id, \*\*params) -> DatabaseImportResponse -- client.d1.database.query(database_id, \*, account_id, \*\*params) -> SyncSinglePage[QueryResult] -- client.d1.database.raw(database_id, \*, account_id, \*\*params) -> SyncSinglePage[DatabaseRawResponse] +- client.d1.database.create(\*, account_id, \*\*params) -> D1 +- client.d1.database.update(database_id, \*, account_id, \*\*params) -> D1 +- client.d1.database.list(\*, account_id, \*\*params) -> SyncV4PagePaginationArray[DatabaseListResponse] +- client.d1.database.delete(database_id, \*, account_id) -> object +- client.d1.database.edit(database_id, \*, account_id, \*\*params) -> D1 +- client.d1.database.export(database_id, \*, account_id, \*\*params) -> DatabaseExportResponse +- client.d1.database.get(database_id, \*, account_id) -> D1 +- client.d1.database.import\_(database_id, \*, account_id, \*\*params) -> DatabaseImportResponse +- client.d1.database.query(database_id, \*, account_id, \*\*params) -> SyncSinglePage[QueryResult] +- client.d1.database.raw(database_id, \*, account_id, \*\*params) -> SyncSinglePage[DatabaseRawResponse] + +### TimeTravel + +Types: + +```python +from cloudflare.types.d1.database import TimeTravelGetBookmarkResponse, TimeTravelRestoreResponse +``` + +Methods: + +- client.d1.database.time_travel.get_bookmark(database_id, \*, account_id, \*\*params) -> TimeTravelGetBookmarkResponse +- client.d1.database.time_travel.restore(database_id, \*, account_id, \*\*params) -> TimeTravelRestoreResponse # R2 diff --git a/src/cloudflare/resources/d1/d1.py b/src/cloudflare/resources/d1/d1.py index 514b18eff..5ced774c4 100644 --- a/src/cloudflare/resources/d1/d1.py +++ b/src/cloudflare/resources/d1/d1.py @@ -2,7 +2,9 @@ from __future__ import annotations -from .database import ( +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .database.database import ( DatabaseResource, AsyncDatabaseResource, DatabaseResourceWithRawResponse, @@ -10,8 +12,6 @@ from .database import ( DatabaseResourceWithStreamingResponse, AsyncDatabaseResourceWithStreamingResponse, ) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource __all__ = ["D1Resource", "AsyncD1Resource"] diff --git a/src/cloudflare/resources/d1/database/__init__.py b/src/cloudflare/resources/d1/database/__init__.py new file mode 100644 index 000000000..5834ebb6a --- /dev/null +++ b/src/cloudflare/resources/d1/database/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .database import ( + DatabaseResource, + AsyncDatabaseResource, + DatabaseResourceWithRawResponse, + AsyncDatabaseResourceWithRawResponse, + DatabaseResourceWithStreamingResponse, + AsyncDatabaseResourceWithStreamingResponse, +) +from .time_travel import ( + TimeTravelResource, + AsyncTimeTravelResource, + TimeTravelResourceWithRawResponse, + AsyncTimeTravelResourceWithRawResponse, + TimeTravelResourceWithStreamingResponse, + AsyncTimeTravelResourceWithStreamingResponse, +) + +__all__ = [ + "TimeTravelResource", + "AsyncTimeTravelResource", + "TimeTravelResourceWithRawResponse", + "AsyncTimeTravelResourceWithRawResponse", + "TimeTravelResourceWithStreamingResponse", + "AsyncTimeTravelResourceWithStreamingResponse", + "DatabaseResource", + "AsyncDatabaseResource", + "DatabaseResourceWithRawResponse", + "AsyncDatabaseResourceWithRawResponse", + "DatabaseResourceWithStreamingResponse", + "AsyncDatabaseResourceWithStreamingResponse", +] diff --git a/src/cloudflare/resources/d1/database.py b/src/cloudflare/resources/d1/database/database.py similarity index 96% rename from src/cloudflare/resources/d1/database.py rename to src/cloudflare/resources/d1/database/database.py index c844fa90a..200815a56 100644 --- a/src/cloudflare/resources/d1/database.py +++ b/src/cloudflare/resources/d1/database/database.py @@ -7,10 +7,10 @@ from typing_extensions import Literal, overload import httpx -from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import required_args, maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ...types.d1 import ( +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import required_args, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ....types.d1 import ( database_raw_params, database_edit_params, database_list_params, @@ -20,27 +20,39 @@ from ...types.d1 import ( database_import_params, database_update_params, ) -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( +from .time_travel import ( + TimeTravelResource, + AsyncTimeTravelResource, + TimeTravelResourceWithRawResponse, + AsyncTimeTravelResourceWithRawResponse, + TimeTravelResourceWithStreamingResponse, + AsyncTimeTravelResourceWithStreamingResponse, +) +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 SyncSinglePage, AsyncSinglePage, SyncV4PagePaginationArray, AsyncV4PagePaginationArray -from ...types.d1.d1 import D1 -from ..._base_client import AsyncPaginator, make_request_options -from ...types.d1.query_result import QueryResult -from ...types.d1.database_raw_response import DatabaseRawResponse -from ...types.d1.database_list_response import DatabaseListResponse -from ...types.d1.database_export_response import DatabaseExportResponse -from ...types.d1.database_import_response import DatabaseImportResponse +from ...._wrappers import ResultWrapper +from ....pagination import SyncSinglePage, AsyncSinglePage, SyncV4PagePaginationArray, AsyncV4PagePaginationArray +from ....types.d1.d1 import D1 +from ...._base_client import AsyncPaginator, make_request_options +from ....types.d1.query_result import QueryResult +from ....types.d1.database_raw_response import DatabaseRawResponse +from ....types.d1.database_list_response import DatabaseListResponse +from ....types.d1.database_export_response import DatabaseExportResponse +from ....types.d1.database_import_response import DatabaseImportResponse __all__ = ["DatabaseResource", "AsyncDatabaseResource"] class DatabaseResource(SyncAPIResource): + @cached_property + def time_travel(self) -> TimeTravelResource: + return TimeTravelResource(self._client) + @cached_property def with_raw_response(self) -> DatabaseResourceWithRawResponse: """ @@ -809,6 +821,10 @@ class DatabaseResource(SyncAPIResource): class AsyncDatabaseResource(AsyncAPIResource): + @cached_property + def time_travel(self) -> AsyncTimeTravelResource: + return AsyncTimeTravelResource(self._client) + @cached_property def with_raw_response(self) -> AsyncDatabaseResourceWithRawResponse: """ @@ -1615,6 +1631,10 @@ class DatabaseResourceWithRawResponse: database.raw, ) + @cached_property + def time_travel(self) -> TimeTravelResourceWithRawResponse: + return TimeTravelResourceWithRawResponse(self._database.time_travel) + class AsyncDatabaseResourceWithRawResponse: def __init__(self, database: AsyncDatabaseResource) -> None: @@ -1651,6 +1671,10 @@ class AsyncDatabaseResourceWithRawResponse: database.raw, ) + @cached_property + def time_travel(self) -> AsyncTimeTravelResourceWithRawResponse: + return AsyncTimeTravelResourceWithRawResponse(self._database.time_travel) + class DatabaseResourceWithStreamingResponse: def __init__(self, database: DatabaseResource) -> None: @@ -1687,6 +1711,10 @@ class DatabaseResourceWithStreamingResponse: database.raw, ) + @cached_property + def time_travel(self) -> TimeTravelResourceWithStreamingResponse: + return TimeTravelResourceWithStreamingResponse(self._database.time_travel) + class AsyncDatabaseResourceWithStreamingResponse: def __init__(self, database: AsyncDatabaseResource) -> None: @@ -1722,3 +1750,7 @@ class AsyncDatabaseResourceWithStreamingResponse: self.raw = async_to_streamed_response_wrapper( database.raw, ) + + @cached_property + def time_travel(self) -> AsyncTimeTravelResourceWithStreamingResponse: + return AsyncTimeTravelResourceWithStreamingResponse(self._database.time_travel) diff --git a/src/cloudflare/resources/d1/database/time_travel.py b/src/cloudflare/resources/d1/database/time_travel.py new file mode 100644 index 000000000..a3f6c6844 --- /dev/null +++ b/src/cloudflare/resources/d1/database/time_travel.py @@ -0,0 +1,340 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Type, Union, cast +from datetime import datetime + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._wrappers import ResultWrapper +from ...._base_client import make_request_options +from ....types.d1.database import time_travel_restore_params, time_travel_get_bookmark_params +from ....types.d1.database.time_travel_restore_response import TimeTravelRestoreResponse +from ....types.d1.database.time_travel_get_bookmark_response import TimeTravelGetBookmarkResponse + +__all__ = ["TimeTravelResource", "AsyncTimeTravelResource"] + + +class TimeTravelResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> TimeTravelResourceWithRawResponse: + """ + 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 TimeTravelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TimeTravelResourceWithStreamingResponse: + """ + 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 TimeTravelResourceWithStreamingResponse(self) + + def get_bookmark( + self, + database_id: str, + *, + account_id: str, + timestamp: Union[str, datetime] | 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, + ) -> TimeTravelGetBookmarkResponse: + """ + Retrieves the current bookmark, or the nearest bookmark at or before a provided + timestamp. Bookmarks can be used with the restore endpoint to revert the + database to a previous point in time. + + Args: + account_id: Account identifier tag. + + database_id: D1 database identifier (UUID). + + timestamp: An optional ISO 8601 timestamp. If provided, returns the nearest available + bookmark at or before this timestamp. If omitted, returns the current bookmark. + + 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 database_id: + raise ValueError(f"Expected a non-empty value for `database_id` but received {database_id!r}") + return self._get( + f"/accounts/{account_id}/d1/database/{database_id}/time_travel/bookmark", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"timestamp": timestamp}, time_travel_get_bookmark_params.TimeTravelGetBookmarkParams + ), + post_parser=ResultWrapper[TimeTravelGetBookmarkResponse]._unwrapper, + ), + cast_to=cast(Type[TimeTravelGetBookmarkResponse], ResultWrapper[TimeTravelGetBookmarkResponse]), + ) + + def restore( + self, + database_id: str, + *, + account_id: str, + bookmark: str | Omit = omit, + timestamp: Union[str, datetime] | 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, + ) -> TimeTravelRestoreResponse: + """ + Restores a D1 database to a previous point in time either via a bookmark or a + timestamp. + + Args: + account_id: Account identifier tag. + + database_id: D1 database identifier (UUID). + + bookmark: A bookmark to restore the database to. Required if `timestamp` is not provided. + + timestamp: An ISO 8601 timestamp to restore the database to. Required if `bookmark` is not + provided. + + 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 database_id: + raise ValueError(f"Expected a non-empty value for `database_id` but received {database_id!r}") + return self._post( + f"/accounts/{account_id}/d1/database/{database_id}/time_travel/restore", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "bookmark": bookmark, + "timestamp": timestamp, + }, + time_travel_restore_params.TimeTravelRestoreParams, + ), + post_parser=ResultWrapper[TimeTravelRestoreResponse]._unwrapper, + ), + cast_to=cast(Type[TimeTravelRestoreResponse], ResultWrapper[TimeTravelRestoreResponse]), + ) + + +class AsyncTimeTravelResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncTimeTravelResourceWithRawResponse: + """ + 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 AsyncTimeTravelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTimeTravelResourceWithStreamingResponse: + """ + 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 AsyncTimeTravelResourceWithStreamingResponse(self) + + async def get_bookmark( + self, + database_id: str, + *, + account_id: str, + timestamp: Union[str, datetime] | 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, + ) -> TimeTravelGetBookmarkResponse: + """ + Retrieves the current bookmark, or the nearest bookmark at or before a provided + timestamp. Bookmarks can be used with the restore endpoint to revert the + database to a previous point in time. + + Args: + account_id: Account identifier tag. + + database_id: D1 database identifier (UUID). + + timestamp: An optional ISO 8601 timestamp. If provided, returns the nearest available + bookmark at or before this timestamp. If omitted, returns the current bookmark. + + 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 database_id: + raise ValueError(f"Expected a non-empty value for `database_id` but received {database_id!r}") + return await self._get( + f"/accounts/{account_id}/d1/database/{database_id}/time_travel/bookmark", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"timestamp": timestamp}, time_travel_get_bookmark_params.TimeTravelGetBookmarkParams + ), + post_parser=ResultWrapper[TimeTravelGetBookmarkResponse]._unwrapper, + ), + cast_to=cast(Type[TimeTravelGetBookmarkResponse], ResultWrapper[TimeTravelGetBookmarkResponse]), + ) + + async def restore( + self, + database_id: str, + *, + account_id: str, + bookmark: str | Omit = omit, + timestamp: Union[str, datetime] | 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, + ) -> TimeTravelRestoreResponse: + """ + Restores a D1 database to a previous point in time either via a bookmark or a + timestamp. + + Args: + account_id: Account identifier tag. + + database_id: D1 database identifier (UUID). + + bookmark: A bookmark to restore the database to. Required if `timestamp` is not provided. + + timestamp: An ISO 8601 timestamp to restore the database to. Required if `bookmark` is not + provided. + + 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 database_id: + raise ValueError(f"Expected a non-empty value for `database_id` but received {database_id!r}") + return await self._post( + f"/accounts/{account_id}/d1/database/{database_id}/time_travel/restore", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "bookmark": bookmark, + "timestamp": timestamp, + }, + time_travel_restore_params.TimeTravelRestoreParams, + ), + post_parser=ResultWrapper[TimeTravelRestoreResponse]._unwrapper, + ), + cast_to=cast(Type[TimeTravelRestoreResponse], ResultWrapper[TimeTravelRestoreResponse]), + ) + + +class TimeTravelResourceWithRawResponse: + def __init__(self, time_travel: TimeTravelResource) -> None: + self._time_travel = time_travel + + self.get_bookmark = to_raw_response_wrapper( + time_travel.get_bookmark, + ) + self.restore = to_raw_response_wrapper( + time_travel.restore, + ) + + +class AsyncTimeTravelResourceWithRawResponse: + def __init__(self, time_travel: AsyncTimeTravelResource) -> None: + self._time_travel = time_travel + + self.get_bookmark = async_to_raw_response_wrapper( + time_travel.get_bookmark, + ) + self.restore = async_to_raw_response_wrapper( + time_travel.restore, + ) + + +class TimeTravelResourceWithStreamingResponse: + def __init__(self, time_travel: TimeTravelResource) -> None: + self._time_travel = time_travel + + self.get_bookmark = to_streamed_response_wrapper( + time_travel.get_bookmark, + ) + self.restore = to_streamed_response_wrapper( + time_travel.restore, + ) + + +class AsyncTimeTravelResourceWithStreamingResponse: + def __init__(self, time_travel: AsyncTimeTravelResource) -> None: + self._time_travel = time_travel + + self.get_bookmark = async_to_streamed_response_wrapper( + time_travel.get_bookmark, + ) + self.restore = async_to_streamed_response_wrapper( + time_travel.restore, + ) diff --git a/src/cloudflare/types/d1/database/__init__.py b/src/cloudflare/types/d1/database/__init__.py new file mode 100644 index 000000000..d01a1918c --- /dev/null +++ b/src/cloudflare/types/d1/database/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .time_travel_restore_params import TimeTravelRestoreParams as TimeTravelRestoreParams +from .time_travel_restore_response import TimeTravelRestoreResponse as TimeTravelRestoreResponse +from .time_travel_get_bookmark_params import TimeTravelGetBookmarkParams as TimeTravelGetBookmarkParams +from .time_travel_get_bookmark_response import TimeTravelGetBookmarkResponse as TimeTravelGetBookmarkResponse diff --git a/src/cloudflare/types/d1/database/time_travel_get_bookmark_params.py b/src/cloudflare/types/d1/database/time_travel_get_bookmark_params.py new file mode 100644 index 000000000..690290b66 --- /dev/null +++ b/src/cloudflare/types/d1/database/time_travel_get_bookmark_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 Union +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from ...._utils import PropertyInfo + +__all__ = ["TimeTravelGetBookmarkParams"] + + +class TimeTravelGetBookmarkParams(TypedDict, total=False): + account_id: Required[str] + """Account identifier tag.""" + + timestamp: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """An optional ISO 8601 timestamp. + + If provided, returns the nearest available bookmark at or before this timestamp. + If omitted, returns the current bookmark. + """ diff --git a/src/cloudflare/types/d1/database/time_travel_get_bookmark_response.py b/src/cloudflare/types/d1/database/time_travel_get_bookmark_response.py new file mode 100644 index 000000000..48ce81fdb --- /dev/null +++ b/src/cloudflare/types/d1/database/time_travel_get_bookmark_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["TimeTravelGetBookmarkResponse"] + + +class TimeTravelGetBookmarkResponse(BaseModel): + bookmark: Optional[str] = None + """ + A bookmark representing a specific state of the database at a specific point in + time. + """ diff --git a/src/cloudflare/types/d1/database/time_travel_restore_params.py b/src/cloudflare/types/d1/database/time_travel_restore_params.py new file mode 100644 index 000000000..ef86ba6d9 --- /dev/null +++ b/src/cloudflare/types/d1/database/time_travel_restore_params.py @@ -0,0 +1,25 @@ +# 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__ = ["TimeTravelRestoreParams"] + + +class TimeTravelRestoreParams(TypedDict, total=False): + account_id: Required[str] + """Account identifier tag.""" + + bookmark: str + """A bookmark to restore the database to. Required if `timestamp` is not provided.""" + + timestamp: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """An ISO 8601 timestamp to restore the database to. + + Required if `bookmark` is not provided. + """ diff --git a/src/cloudflare/types/d1/database/time_travel_restore_response.py b/src/cloudflare/types/d1/database/time_travel_restore_response.py new file mode 100644 index 000000000..671d5922a --- /dev/null +++ b/src/cloudflare/types/d1/database/time_travel_restore_response.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["TimeTravelRestoreResponse"] + + +class TimeTravelRestoreResponse(BaseModel): + """Response from a time travel restore operation.""" + + bookmark: Optional[str] = None + """ + The new bookmark representing the state of the database after the restore + operation. + """ + + message: Optional[str] = None + """A message describing the result of the restore operation.""" + + previous_bookmark: Optional[str] = None + """The bookmark representing the state of the database before the restore + operation. + + Can be used to undo the restore if needed. + """ diff --git a/tests/api_resources/d1/database/__init__.py b/tests/api_resources/d1/database/__init__.py new file mode 100644 index 000000000..fd8019a9a --- /dev/null +++ b/tests/api_resources/d1/database/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/d1/database/test_time_travel.py b/tests/api_resources/d1/database/test_time_travel.py new file mode 100644 index 000000000..7c3d59295 --- /dev/null +++ b/tests/api_resources/d1/database/test_time_travel.py @@ -0,0 +1,258 @@ +# 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.types.d1.database import ( + TimeTravelRestoreResponse, + TimeTravelGetBookmarkResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestTimeTravel: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_get_bookmark(self, client: Cloudflare) -> None: + time_travel = client.d1.database.time_travel.get_bookmark( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(TimeTravelGetBookmarkResponse, time_travel, path=["response"]) + + @parametrize + def test_method_get_bookmark_with_all_params(self, client: Cloudflare) -> None: + time_travel = client.d1.database.time_travel.get_bookmark( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + timestamp=parse_datetime("2024-01-15T12:00:00Z"), + ) + assert_matches_type(TimeTravelGetBookmarkResponse, time_travel, path=["response"]) + + @parametrize + def test_raw_response_get_bookmark(self, client: Cloudflare) -> None: + response = client.d1.database.time_travel.with_raw_response.get_bookmark( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + time_travel = response.parse() + assert_matches_type(TimeTravelGetBookmarkResponse, time_travel, path=["response"]) + + @parametrize + def test_streaming_response_get_bookmark(self, client: Cloudflare) -> None: + with client.d1.database.time_travel.with_streaming_response.get_bookmark( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + time_travel = response.parse() + assert_matches_type(TimeTravelGetBookmarkResponse, time_travel, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_get_bookmark(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.d1.database.time_travel.with_raw_response.get_bookmark( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_id` but received ''"): + client.d1.database.time_travel.with_raw_response.get_bookmark( + database_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + @parametrize + def test_method_restore(self, client: Cloudflare) -> None: + time_travel = client.d1.database.time_travel.restore( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(TimeTravelRestoreResponse, time_travel, path=["response"]) + + @parametrize + def test_method_restore_with_all_params(self, client: Cloudflare) -> None: + time_travel = client.d1.database.time_travel.restore( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + bookmark="00000001-00000002-00004e2f-0a83ea2fceebc654de0640c422be4653", + timestamp=parse_datetime("2024-01-15T12:00:00Z"), + ) + assert_matches_type(TimeTravelRestoreResponse, time_travel, path=["response"]) + + @parametrize + def test_raw_response_restore(self, client: Cloudflare) -> None: + response = client.d1.database.time_travel.with_raw_response.restore( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + time_travel = response.parse() + assert_matches_type(TimeTravelRestoreResponse, time_travel, path=["response"]) + + @parametrize + def test_streaming_response_restore(self, client: Cloudflare) -> None: + with client.d1.database.time_travel.with_streaming_response.restore( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + time_travel = response.parse() + assert_matches_type(TimeTravelRestoreResponse, time_travel, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_restore(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.d1.database.time_travel.with_raw_response.restore( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_id` but received ''"): + client.d1.database.time_travel.with_raw_response.restore( + database_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + +class TestAsyncTimeTravel: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_get_bookmark(self, async_client: AsyncCloudflare) -> None: + time_travel = await async_client.d1.database.time_travel.get_bookmark( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(TimeTravelGetBookmarkResponse, time_travel, path=["response"]) + + @parametrize + async def test_method_get_bookmark_with_all_params(self, async_client: AsyncCloudflare) -> None: + time_travel = await async_client.d1.database.time_travel.get_bookmark( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + timestamp=parse_datetime("2024-01-15T12:00:00Z"), + ) + assert_matches_type(TimeTravelGetBookmarkResponse, time_travel, path=["response"]) + + @parametrize + async def test_raw_response_get_bookmark(self, async_client: AsyncCloudflare) -> None: + response = await async_client.d1.database.time_travel.with_raw_response.get_bookmark( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + time_travel = await response.parse() + assert_matches_type(TimeTravelGetBookmarkResponse, time_travel, path=["response"]) + + @parametrize + async def test_streaming_response_get_bookmark(self, async_client: AsyncCloudflare) -> None: + async with async_client.d1.database.time_travel.with_streaming_response.get_bookmark( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + time_travel = await response.parse() + assert_matches_type(TimeTravelGetBookmarkResponse, time_travel, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_get_bookmark(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.d1.database.time_travel.with_raw_response.get_bookmark( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_id` but received ''"): + await async_client.d1.database.time_travel.with_raw_response.get_bookmark( + database_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + @parametrize + async def test_method_restore(self, async_client: AsyncCloudflare) -> None: + time_travel = await async_client.d1.database.time_travel.restore( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + assert_matches_type(TimeTravelRestoreResponse, time_travel, path=["response"]) + + @parametrize + async def test_method_restore_with_all_params(self, async_client: AsyncCloudflare) -> None: + time_travel = await async_client.d1.database.time_travel.restore( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + bookmark="00000001-00000002-00004e2f-0a83ea2fceebc654de0640c422be4653", + timestamp=parse_datetime("2024-01-15T12:00:00Z"), + ) + assert_matches_type(TimeTravelRestoreResponse, time_travel, path=["response"]) + + @parametrize + async def test_raw_response_restore(self, async_client: AsyncCloudflare) -> None: + response = await async_client.d1.database.time_travel.with_raw_response.restore( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + time_travel = await response.parse() + assert_matches_type(TimeTravelRestoreResponse, time_travel, path=["response"]) + + @parametrize + async def test_streaming_response_restore(self, async_client: AsyncCloudflare) -> None: + async with async_client.d1.database.time_travel.with_streaming_response.restore( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + time_travel = await response.parse() + assert_matches_type(TimeTravelRestoreResponse, time_travel, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_restore(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.d1.database.time_travel.with_raw_response.restore( + database_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `database_id` but received ''"): + await async_client.d1.database.time_travel.with_raw_response.restore( + database_id="", + account_id="023e105f4ecef8ad9ca31a8372d0c353", + )