diff --git a/src/collections/backup/client.ts b/src/collections/backup/client.ts index 4a655f10..d7b1952a 100644 --- a/src/collections/backup/client.ts +++ b/src/collections/backup/client.ts @@ -18,6 +18,7 @@ import { import { BackupCreateResponse, BackupCreateStatusResponse, + BackupListResponse, BackupRestoreResponse, } from '../../openapi/types.js'; import { @@ -47,6 +48,8 @@ export const backup = (connection: Connection): Backup => { error: res.error, path: res.path, status: res.status, + incrementalBaseBackupId: + 'incremental_base_backup_id' in res ? res.incremental_base_backup_id : undefined, }; }; const parseResponse = (res: BackupCreateResponse | BackupRestoreResponse): BackupReturn => { @@ -213,7 +216,12 @@ export const backup = (connection: Connection): Backup => { if (opts?.startedAtAsc) { url += '?order=asc'; } - return connection.get(url); + return connection.get(url).then((backups) => + (backups ?? []).map((backup) => ({ + ...(backup as unknown as BackupReturn), + incrementalBaseBackupId: backup.incremental_base_backup_id, + })) + ); }, }; }; diff --git a/src/collections/backup/types.ts b/src/collections/backup/types.ts index c76e5a25..3d773538 100644 --- a/src/collections/backup/types.ts +++ b/src/collections/backup/types.ts @@ -16,6 +16,8 @@ export type BackupStatusReturn = { status: BackupStatus; /** Size of the backup in Gibs */ size?: number; + /** The ID of the base backup this incremental backup was built on; absent when the backup is not incremental. */ + incrementalBaseBackupId?: string; }; /** The return type of a backup creation or restoration operation */ diff --git a/src/openapi/types.ts b/src/openapi/types.ts index 4cecdcfc..dad985cb 100644 --- a/src/openapi/types.ts +++ b/src/openapi/types.ts @@ -18,6 +18,7 @@ export type BackupCreateStatusResponse = definitions['BackupCreateStatusResponse export type BackupRestoreRequest = definitions['BackupRestoreRequest']; export type BackupRestoreResponse = definitions['BackupRestoreResponse']; export type BackupRestoreStatusResponse = definitions['BackupRestoreStatusResponse']; +export type BackupListResponse = definitions['BackupListResponse']; export type BackupConfig = definitions['BackupConfig']; export type RestoreConfig = definitions['RestoreConfig']; export type WeaviateBackupStatus = diff --git a/test/collections/backup/mock.test.ts b/test/collections/backup/mock.test.ts index ab4ca53a..e02bfc27 100644 --- a/test/collections/backup/mock.test.ts +++ b/test/collections/backup/mock.test.ts @@ -66,9 +66,23 @@ class CancelMock { backend: BACKEND, path: 'path/to/backup', status: CancelMock.status, + incremental_base_backup_id: 'base-backup-001', }) ); + // Backup list endpoint + httpApp.get(`/v1/backups/${BACKEND}`, (req, res) => + res.send([ + { id: 'full-backup', classes: ['A'], status: 'SUCCESS' }, + { + id: 'incremental-backup', + classes: ['A'], + status: 'SUCCESS', + incremental_base_backup_id: 'full-backup', + }, + ]) + ); + // Backup restoration endpoint httpApp.post( `/v1/backups/${BACKEND}/${BACKUP_ID}/restore`, @@ -184,5 +198,16 @@ describe('Mock testing of backup cancellation', () => { expect(success).toBe(false); }); + it('should surface incrementalBaseBackupId from the creation status', async () => { + const status = await client.backup.getCreateStatus({ backupId: BACKUP_ID, backend: BACKEND }); + expect(status.incrementalBaseBackupId).toBe('base-backup-001'); + }); + + it('should surface incrementalBaseBackupId when listing backups', async () => { + const backups = await client.backup.list(BACKEND); + expect(backups.find((b) => b.id === 'full-backup')?.incrementalBaseBackupId).toBeUndefined(); + expect(backups.find((b) => b.id === 'incremental-backup')?.incrementalBaseBackupId).toBe('full-backup'); + }); + afterAll(() => mock.close()); });