From 79cb6030c9315188f42f2af503b742b3a34bb949 Mon Sep 17 00:00:00 2001 From: "Joseph T. French" Date: Sat, 8 Aug 2026 13:35:49 -0500 Subject: [PATCH] feat(client): regenerate for the backup encryption field removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream retired the backup `encryption` flag. It never encrypted anything at the application layer — it only suppressed downloads, and since it defaulted to false the default backup was unrestorable. Restore is now gated on graph type instead, and every completed backup is downloadable. Contract changes: - BackupListResponse gains `restore_supported`, true unless the graph is an entity graph (materialized from the extensions database) or a shared repository (platform-managed, download-only) - BackupCreateRequest drops `encryption` - BackupResponse drops `encryption_enabled` and `allow_export` Objects remain encrypted at rest with S3 SSE-AES256 and are served over TLS through short-lived signed URLs. Removing response fields breaks a consumer that reads them, but nothing does: the flag has no effect on any operation and `allow_export` was always its inverse. Released as a minor by decision rather than carrying dead fields through a deprecation cycle. --- .../api/backup/get_backup_download_url.py | 40 +++++++++---------- .../api/graph_operations/restore_backup.py | 20 ++++++---- .../models/backup_create_request.py | 9 ----- .../models/backup_list_response.py | 11 +++++ robosystems_client/models/backup_response.py | 20 +--------- 5 files changed, 45 insertions(+), 55 deletions(-) diff --git a/robosystems_client/api/backup/get_backup_download_url.py b/robosystems_client/api/backup/get_backup_download_url.py index b678e1a..c9e1f92 100644 --- a/robosystems_client/api/backup/get_backup_download_url.py +++ b/robosystems_client/api/backup/get_backup_download_url.py @@ -87,11 +87,11 @@ def sync_detailed( ) -> Response[Any | BackupDownloadUrlResponse | HTTPValidationError]: """Get temporary download URL for backup - Generate a temporary download URL for a backup (unencrypted backups only). The filename carries the - extension listed as `download_extension` on the backup: `.lbug.zip` is a ZIP holding the LadybugDB - database file `{graph_id}.lbug`; `.lbug.zst` (shared repository snapshots) is a single zstd- - compressed database file. Decompress the latter with `zstd -d .lbug.zst` (install zstd first: - `brew install zstd`, `apt-get install zstd`, or `dnf install zstd`) — no `--long` flag is needed. + Generate a temporary download URL for a backup. The filename carries the extension listed as + `download_extension` on the backup: `.lbug.zip` is a ZIP holding the LadybugDB database file + `{graph_id}.lbug`; `.lbug.zst` (shared repository snapshots) is a single zstd-compressed database + file. Decompress the latter with `zstd -d .lbug.zst` (install zstd first: `brew install zstd`, + `apt-get install zstd`, or `dnf install zstd`) — no `--long` flag is needed. Args: graph_id (str): @@ -128,11 +128,11 @@ def sync( ) -> Any | BackupDownloadUrlResponse | HTTPValidationError | None: """Get temporary download URL for backup - Generate a temporary download URL for a backup (unencrypted backups only). The filename carries the - extension listed as `download_extension` on the backup: `.lbug.zip` is a ZIP holding the LadybugDB - database file `{graph_id}.lbug`; `.lbug.zst` (shared repository snapshots) is a single zstd- - compressed database file. Decompress the latter with `zstd -d .lbug.zst` (install zstd first: - `brew install zstd`, `apt-get install zstd`, or `dnf install zstd`) — no `--long` flag is needed. + Generate a temporary download URL for a backup. The filename carries the extension listed as + `download_extension` on the backup: `.lbug.zip` is a ZIP holding the LadybugDB database file + `{graph_id}.lbug`; `.lbug.zst` (shared repository snapshots) is a single zstd-compressed database + file. Decompress the latter with `zstd -d .lbug.zst` (install zstd first: `brew install zstd`, + `apt-get install zstd`, or `dnf install zstd`) — no `--long` flag is needed. Args: graph_id (str): @@ -164,11 +164,11 @@ async def asyncio_detailed( ) -> Response[Any | BackupDownloadUrlResponse | HTTPValidationError]: """Get temporary download URL for backup - Generate a temporary download URL for a backup (unencrypted backups only). The filename carries the - extension listed as `download_extension` on the backup: `.lbug.zip` is a ZIP holding the LadybugDB - database file `{graph_id}.lbug`; `.lbug.zst` (shared repository snapshots) is a single zstd- - compressed database file. Decompress the latter with `zstd -d .lbug.zst` (install zstd first: - `brew install zstd`, `apt-get install zstd`, or `dnf install zstd`) — no `--long` flag is needed. + Generate a temporary download URL for a backup. The filename carries the extension listed as + `download_extension` on the backup: `.lbug.zip` is a ZIP holding the LadybugDB database file + `{graph_id}.lbug`; `.lbug.zst` (shared repository snapshots) is a single zstd-compressed database + file. Decompress the latter with `zstd -d .lbug.zst` (install zstd first: `brew install zstd`, + `apt-get install zstd`, or `dnf install zstd`) — no `--long` flag is needed. Args: graph_id (str): @@ -203,11 +203,11 @@ async def asyncio( ) -> Any | BackupDownloadUrlResponse | HTTPValidationError | None: """Get temporary download URL for backup - Generate a temporary download URL for a backup (unencrypted backups only). The filename carries the - extension listed as `download_extension` on the backup: `.lbug.zip` is a ZIP holding the LadybugDB - database file `{graph_id}.lbug`; `.lbug.zst` (shared repository snapshots) is a single zstd- - compressed database file. Decompress the latter with `zstd -d .lbug.zst` (install zstd first: - `brew install zstd`, `apt-get install zstd`, or `dnf install zstd`) — no `--long` flag is needed. + Generate a temporary download URL for a backup. The filename carries the extension listed as + `download_extension` on the backup: `.lbug.zip` is a ZIP holding the LadybugDB database file + `{graph_id}.lbug`; `.lbug.zst` (shared repository snapshots) is a single zstd-compressed database + file. Decompress the latter with `zstd -d .lbug.zst` (install zstd first: `brew install zstd`, + `apt-get install zstd`, or `dnf install zstd`) — no `--long` flag is needed. Args: graph_id (str): diff --git a/robosystems_client/api/graph_operations/restore_backup.py b/robosystems_client/api/graph_operations/restore_backup.py index e2f9c84..00a99ea 100644 --- a/robosystems_client/api/graph_operations/restore_backup.py +++ b/robosystems_client/api/graph_operations/restore_backup.py @@ -111,8 +111,9 @@ def sync_detailed( ) -> Response[ErrorResponse | OperationEnvelope]: """Restore Backup - Only encrypted backups can be restored. Not allowed on entity graphs (use `materialize` instead) or - shared repositories. + Not allowed on entity graphs (use `materialize` instead) or shared repositories. Destructive: the + existing database is snapshotted, then overwritten. Monitor progress via SSE at + `/v1/operations/{operation_id}/stream`. **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. @@ -152,8 +153,9 @@ def sync( ) -> ErrorResponse | OperationEnvelope | None: """Restore Backup - Only encrypted backups can be restored. Not allowed on entity graphs (use `materialize` instead) or - shared repositories. + Not allowed on entity graphs (use `materialize` instead) or shared repositories. Destructive: the + existing database is snapshotted, then overwritten. Monitor progress via SSE at + `/v1/operations/{operation_id}/stream`. **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. @@ -188,8 +190,9 @@ async def asyncio_detailed( ) -> Response[ErrorResponse | OperationEnvelope]: """Restore Backup - Only encrypted backups can be restored. Not allowed on entity graphs (use `materialize` instead) or - shared repositories. + Not allowed on entity graphs (use `materialize` instead) or shared repositories. Destructive: the + existing database is snapshotted, then overwritten. Monitor progress via SSE at + `/v1/operations/{operation_id}/stream`. **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. @@ -227,8 +230,9 @@ async def asyncio( ) -> ErrorResponse | OperationEnvelope | None: """Restore Backup - Only encrypted backups can be restored. Not allowed on entity graphs (use `materialize` instead) or - shared repositories. + Not allowed on entity graphs (use `materialize` instead) or shared repositories. Destructive: the + existing database is snapshotted, then overwritten. Monitor progress via SSE at + `/v1/operations/{operation_id}/stream`. **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. diff --git a/robosystems_client/models/backup_create_request.py b/robosystems_client/models/backup_create_request.py index 73a21f8..9b6391b 100644 --- a/robosystems_client/models/backup_create_request.py +++ b/robosystems_client/models/backup_create_request.py @@ -23,7 +23,6 @@ class BackupCreateRequest: is the hard ceiling: the storage lifecycle expires backup objects then regardless of the requested value. Default: 30. compression (bool | Unset): Enable compression (always enabled for optimal storage) Default: True. - encryption (bool | Unset): Enable encryption (encrypted backups cannot be downloaded) Default: False. schedule (None | str | Unset): Optional cron schedule for automated backups """ @@ -31,7 +30,6 @@ class BackupCreateRequest: backup_type: str | Unset = "full" retention_days: int | Unset = 30 compression: bool | Unset = True - encryption: bool | Unset = False schedule: None | str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) @@ -44,8 +42,6 @@ def to_dict(self) -> dict[str, Any]: compression = self.compression - encryption = self.encryption - schedule: None | str | Unset if isinstance(self.schedule, Unset): schedule = UNSET @@ -63,8 +59,6 @@ def to_dict(self) -> dict[str, Any]: field_dict["retention_days"] = retention_days if compression is not UNSET: field_dict["compression"] = compression - if encryption is not UNSET: - field_dict["encryption"] = encryption if schedule is not UNSET: field_dict["schedule"] = schedule @@ -81,8 +75,6 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: compression = d.pop("compression", UNSET) - encryption = d.pop("encryption", UNSET) - def _parse_schedule(data: object) -> None | str | Unset: if data is None: return data @@ -97,7 +89,6 @@ def _parse_schedule(data: object) -> None | str | Unset: backup_type=backup_type, retention_days=retention_days, compression=compression, - encryption=encryption, schedule=schedule, ) diff --git a/robosystems_client/models/backup_list_response.py b/robosystems_client/models/backup_list_response.py index cd3acec..ef29494 100644 --- a/robosystems_client/models/backup_list_response.py +++ b/robosystems_client/models/backup_list_response.py @@ -25,6 +25,9 @@ class BackupListResponse: total_count (int): graph_id (str): is_shared_repository (bool | Unset): Whether this is a shared repository (limits apply) Default: False. + restore_supported (bool | Unset): Whether backups on this graph can be restored. False for entity graphs, which + are materialized from the extensions database (use the materialize operation instead), and for shared + repositories, which are platform-managed and download-only. Default: True. download_quota (DownloadQuota | None | Unset): Download quota for shared repositories """ @@ -32,6 +35,7 @@ class BackupListResponse: total_count: int graph_id: str is_shared_repository: bool | Unset = False + restore_supported: bool | Unset = True download_quota: DownloadQuota | None | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) @@ -49,6 +53,8 @@ def to_dict(self) -> dict[str, Any]: is_shared_repository = self.is_shared_repository + restore_supported = self.restore_supported + download_quota: dict[str, Any] | None | Unset if isinstance(self.download_quota, Unset): download_quota = UNSET @@ -68,6 +74,8 @@ def to_dict(self) -> dict[str, Any]: ) if is_shared_repository is not UNSET: field_dict["is_shared_repository"] = is_shared_repository + if restore_supported is not UNSET: + field_dict["restore_supported"] = restore_supported if download_quota is not UNSET: field_dict["download_quota"] = download_quota @@ -92,6 +100,8 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: is_shared_repository = d.pop("is_shared_repository", UNSET) + restore_supported = d.pop("restore_supported", UNSET) + def _parse_download_quota(data: object) -> DownloadQuota | None | Unset: if data is None: return data @@ -114,6 +124,7 @@ def _parse_download_quota(data: object) -> DownloadQuota | None | Unset: total_count=total_count, graph_id=graph_id, is_shared_repository=is_shared_repository, + restore_supported=restore_supported, download_quota=download_quota, ) diff --git a/robosystems_client/models/backup_response.py b/robosystems_client/models/backup_response.py index 9a8c43e..b479018 100644 --- a/robosystems_client/models/backup_response.py +++ b/robosystems_client/models/backup_response.py @@ -27,15 +27,13 @@ class BackupResponse: node_count (int): relationship_count (int): backup_duration_seconds (float): - encryption_enabled (bool): compression_enabled (bool): - allow_export (bool): created_at (str): completed_at (None | str): expires_at (None | str): download_extension (None | str | Unset): Extension the download will carry, and therefore how to unpack it: - '.lbug.zip' is a ZIP holding the LadybugDB database file, '.lbug.zst' is zstd-compressed (`zstd -d`). Null when - the backup is encrypted and so cannot be downloaded. + '.lbug.zip' is a ZIP holding the LadybugDB database file, '.lbug.zst' is zstd-compressed (`zstd -d`). Null only + when the backup has no stored object yet. """ backup_id: str @@ -49,9 +47,7 @@ class BackupResponse: node_count: int relationship_count: int backup_duration_seconds: float - encryption_enabled: bool compression_enabled: bool - allow_export: bool created_at: str completed_at: None | str expires_at: None | str @@ -81,12 +77,8 @@ def to_dict(self) -> dict[str, Any]: backup_duration_seconds = self.backup_duration_seconds - encryption_enabled = self.encryption_enabled - compression_enabled = self.compression_enabled - allow_export = self.allow_export - created_at = self.created_at completed_at: None | str @@ -116,9 +108,7 @@ def to_dict(self) -> dict[str, Any]: "node_count": node_count, "relationship_count": relationship_count, "backup_duration_seconds": backup_duration_seconds, - "encryption_enabled": encryption_enabled, "compression_enabled": compression_enabled, - "allow_export": allow_export, "created_at": created_at, "completed_at": completed_at, "expires_at": expires_at, @@ -154,12 +144,8 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: backup_duration_seconds = d.pop("backup_duration_seconds") - encryption_enabled = d.pop("encryption_enabled") - compression_enabled = d.pop("compression_enabled") - allow_export = d.pop("allow_export") - created_at = d.pop("created_at") def _parse_completed_at(data: object) -> None | str: @@ -197,9 +183,7 @@ def _parse_download_extension(data: object) -> None | str | Unset: node_count=node_count, relationship_count=relationship_count, backup_duration_seconds=backup_duration_seconds, - encryption_enabled=encryption_enabled, compression_enabled=compression_enabled, - allow_export=allow_export, created_at=created_at, completed_at=completed_at, expires_at=expires_at,