Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/backup/backupCreateStatusGetter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Connection from '../connection/index.js';
import { WeaviateInvalidInputError } from '../errors.js';
import { BackupCreateStatusResponse } from '../openapi/types.js';
import { CommandBase } from '../validation/commandBase.js';
import { Backend } from './index.js';
Expand Down Expand Up @@ -29,7 +30,7 @@ export default class BackupCreateStatusGetter extends CommandBase {
do = (): Promise<BackupCreateStatusResponse> => {
this.validate();
if (this.errors.length > 0) {
return Promise.reject(new Error('invalid usage: ' + this.errors.join(', ')));
return Promise.reject(new WeaviateInvalidInputError('invalid usage: ' + this.errors.join(', ')));
}
return this.client.get(this._path()) as Promise<BackupCreateStatusResponse>;
};
Expand Down
3 changes: 2 additions & 1 deletion src/backup/backupCreator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Connection from '../connection/index.js';
import { WeaviateInvalidInputError } from '../errors.js';
import {
BackupConfig,
BackupCreateRequest,
Expand Down Expand Up @@ -81,7 +82,7 @@ export default class BackupCreator extends CommandBase {
do = (): Promise<BackupCreateResponse> => {
this.validate();
if (this.errors.length > 0) {
return Promise.reject(new Error('invalid usage: ' + this.errors.join(', ')));
return Promise.reject(new WeaviateInvalidInputError('invalid usage: ' + this.errors.join(', ')));
}

const payload = {
Expand Down
3 changes: 2 additions & 1 deletion src/backup/backupGetter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Connection from '../connection/index.js';
import { WeaviateInvalidInputError } from '../errors.js';
import { BackupCreateResponse } from '../openapi/types.js';
import { CommandBase } from '../validation/commandBase.js';
import { Backend } from './index.js';
Expand All @@ -23,7 +24,7 @@ export default class BackupGetter extends CommandBase {
do = (): Promise<BackupCreateResponse[]> => {
this.validate();
if (this.errors.length > 0) {
return Promise.reject(new Error('invalid usage: ' + this.errors.join(', ')));
return Promise.reject(new WeaviateInvalidInputError('invalid usage: ' + this.errors.join(', ')));
}

return this.client.get(this._path());
Expand Down
3 changes: 2 additions & 1 deletion src/backup/backupRestoreStatusGetter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Connection from '../connection/index.js';
import { WeaviateInvalidInputError } from '../errors.js';
import { BackupRestoreStatusResponse } from '../openapi/types.js';
import { CommandBase } from '../validation/commandBase.js';
import { Backend } from './index.js';
Expand Down Expand Up @@ -29,7 +30,7 @@ export default class BackupRestoreStatusGetter extends CommandBase {
do = (): Promise<BackupRestoreStatusResponse> => {
this.validate();
if (this.errors.length > 0) {
return Promise.reject(new Error('invalid usage: ' + this.errors.join(', ')));
return Promise.reject(new WeaviateInvalidInputError('invalid usage: ' + this.errors.join(', ')));
}

return this.client.get(this._path());
Expand Down
3 changes: 2 additions & 1 deletion src/backup/backupRestorer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Connection from '../connection/index.js';
import { WeaviateInvalidInputError } from '../errors.js';
import {
BackupRestoreRequest,
BackupRestoreResponse,
Expand Down Expand Up @@ -81,7 +82,7 @@ export default class BackupRestorer extends CommandBase {
do = (): Promise<BackupRestoreResponse> => {
this.validate();
if (this.errors.length > 0) {
return Promise.reject(new Error('invalid usage: ' + this.errors.join(', ')));
return Promise.reject(new WeaviateInvalidInputError('invalid usage: ' + this.errors.join(', ')));
}

const payload = {
Expand Down
211 changes: 135 additions & 76 deletions src/collections/backup/client.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,111 @@
import {
Backend,
BackupCompressionLevel,
BackupCreateStatusGetter,
BackupCreator,
BackupRestoreStatusGetter,
BackupRestorer,
} from '../../backup/index.js';
import { validateBackend, validateBackupId } from '../../backup/validation.js';
import Connection from '../../connection/index.js';
import { WeaviateBackupFailed } from '../../errors.js';
import {
WeaviateBackupCanceled,
WeaviateBackupCancellationError,
WeaviateBackupFailed,
WeaviateInvalidInputError,
WeaviateUnexpectedResponseError,
WeaviateUnexpectedStatusCodeError,
} from '../../errors.js';
import {
BackupCreateResponse,
BackupCreateStatusResponse,
BackupRestoreResponse,
BackupRestoreStatusResponse,
} from '../../openapi/types.js';

/** Configuration options available when creating a backup */
export type BackupConfigCreate = {
/** The size of the chunks to use for the backup. */
chunkSize?: number;
/** The standard of compression to use for the backup. */
compressionLevel?: BackupCompressionLevel;
/** The percentage of CPU to use for the backup creation job. */
cpuPercentage?: number;
};

/** Configuration options available when restoring a backup */
export type BackupConfigRestore = {
/** The percentage of CPU to use for the backuop restoration job. */
cpuPercentage?: number;
};

/** The arguments required to create and restore backups. */
export type BackupArgs<C extends BackupConfigCreate | BackupConfigRestore> = {
/** The ID of the backup. */
backupId: string;
/** The backend to use for the backup. */
backend: Backend;
/** The collections to include in the backup. */
includeCollections?: string[];
/** The collections to exclude from the backup. */
excludeCollections?: string[];
/** Whether to wait for the backup to complete. */
waitForCompletion?: boolean;
/** The configuration options for the backup. */
config?: C;
};

/** The arguments required to get the status of a backup. */
export type BackupStatusArgs = {
/** The ID of the backup. */
backupId: string;
/** The backend to use for the backup. */
backend: Backend;
};
import {
BackupArgs,
BackupCancelArgs,
BackupConfigCreate,
BackupConfigRestore,
BackupReturn,
BackupStatusArgs,
BackupStatusReturn,
} from './types.js';

export const backup = (connection: Connection) => {
const getCreateStatus = (args: BackupStatusArgs): Promise<BackupCreateStatusResponse> => {
const parseStatus = (res: BackupCreateStatusResponse | BackupRestoreResponse): BackupStatusReturn => {
if (res.id === undefined) {
throw new WeaviateUnexpectedResponseError('Backup ID is undefined in response');
}
if (res.path === undefined) {
throw new WeaviateUnexpectedResponseError('Backup path is undefined in response');
}
if (res.status === undefined) {
throw new WeaviateUnexpectedResponseError('Backup status is undefined in response');
}
return {
id: res.id,
error: res.error,
path: res.path,
status: res.status,
};
};
const parseResponse = (res: BackupCreateResponse | BackupRestoreResponse): BackupReturn => {
if (res.id === undefined) {
throw new WeaviateUnexpectedResponseError('Backup ID is undefined in response');
}
if (res.backend === undefined) {
throw new WeaviateUnexpectedResponseError('Backup backend is undefined in response');
}
if (res.path === undefined) {
throw new WeaviateUnexpectedResponseError('Backup path is undefined in response');
}
if (res.status === undefined) {
throw new WeaviateUnexpectedResponseError('Backup status is undefined in response');
}
return {
id: res.id,
backend: res.backend as Backend,
collections: res.classes ? res.classes : [],
error: res.error,
path: res.path,
status: res.status,
};
};
const getCreateStatus = (args: BackupStatusArgs): Promise<BackupStatusReturn> => {
return new BackupCreateStatusGetter(connection)
.withBackupId(args.backupId)
.withBackend(args.backend)
.do();
.do()
.then(parseStatus);
};
const getRestoreStatus = (args: BackupStatusArgs): Promise<BackupRestoreStatusResponse> => {
const getRestoreStatus = (args: BackupStatusArgs): Promise<BackupStatusReturn> => {
return new BackupRestoreStatusGetter(connection)
.withBackupId(args.backupId)
.withBackend(args.backend)
.do();
.do()
.then(parseStatus);
};
return {
create: async (args: BackupArgs<BackupConfigCreate>): Promise<BackupCreateResponse> => {
cancel: async (args: BackupCancelArgs): Promise<boolean> => {
let errors: string[] = [];
errors = errors.concat(validateBackupId(args.backupId)).concat(validateBackend(args.backend));
if (errors.length > 0) {
throw new WeaviateInvalidInputError(errors.join(', '));
}

try {
await connection.delete(`/backups/${args.backend}/${args.backupId}`, undefined, false);
} catch (err) {
if (err instanceof WeaviateUnexpectedStatusCodeError) {
if (err.code === 404) {
return false;
}
throw new WeaviateBackupCancellationError(err.message);
}
}

return true;
},
create: async (args: BackupArgs<BackupConfigCreate>): Promise<BackupReturn> => {
let builder = new BackupCreator(connection, new BackupCreateStatusGetter(connection))
.withBackupId(args.backupId)
.withBackend(args.backend);
Expand All @@ -90,31 +126,34 @@ export const backup = (connection: Connection) => {
try {
res = await builder.do();
} catch (err) {
throw new Error(`Backup creation failed: ${err}`);
throw new WeaviateBackupFailed(`Backup creation failed: ${err}`, 'creation');
}
if (res.status === 'FAILED') {
throw new Error(`Backup creation failed: ${res.error}`);
throw new WeaviateBackupFailed(`Backup creation failed: ${res.error}`, 'creation');
}
let status: BackupCreateStatusResponse | undefined;
let status: BackupStatusReturn | undefined;
if (args.waitForCompletion) {
let wait = true;
while (wait) {
const res = await getCreateStatus(args); // eslint-disable-line no-await-in-loop
if (res.status === 'SUCCESS') {
const ret = await getCreateStatus(args); // eslint-disable-line no-await-in-loop
if (ret.status === 'SUCCESS') {
wait = false;
status = res;
status = ret;
}
if (ret.status === 'FAILED') {
throw new WeaviateBackupFailed(ret.error ? ret.error : '<unknown>', 'creation');
}
if (res.status === 'FAILED') {
throw new WeaviateBackupFailed(res.error ? res.error : '<unknown>', 'creation');
if (ret.status === 'CANCELED') {
throw new WeaviateBackupCanceled('creation');
}
await new Promise((resolve) => setTimeout(resolve, 1000)); // eslint-disable-line no-await-in-loop
}
}
return status ? { ...status, classes: res.classes } : res;
return status ? { ...parseResponse(res), ...status } : parseResponse(res);
},
getCreateStatus: getCreateStatus,
getRestoreStatus: getRestoreStatus,
restore: async (args: BackupArgs<BackupConfigRestore>): Promise<BackupRestoreResponse> => {
restore: async (args: BackupArgs<BackupConfigRestore>): Promise<BackupReturn> => {
let builder = new BackupRestorer(connection, new BackupRestoreStatusGetter(connection))
.withBackupId(args.backupId)
.withBackend(args.backend);
Expand All @@ -133,63 +172,83 @@ export const backup = (connection: Connection) => {
try {
res = await builder.do();
} catch (err) {
throw new Error(`Backup restoration failed: ${err}`);
throw new WeaviateBackupFailed(`Backup restoration failed: ${err}`, 'restoration');
}
if (res.status === 'FAILED') {
throw new Error(`Backup restoration failed: ${res.error}`);
throw new WeaviateBackupFailed(`Backup restoration failed: ${res.error}`, 'restoration');
}
let status: BackupRestoreStatusResponse | undefined;
let status: BackupStatusReturn | undefined;
if (args.waitForCompletion) {
let wait = true;
while (wait) {
const res = await getRestoreStatus(args); // eslint-disable-line no-await-in-loop
if (res.status === 'SUCCESS') {
const ret = await getRestoreStatus(args); // eslint-disable-line no-await-in-loop
if (ret.status === 'SUCCESS') {
wait = false;
status = res;
status = ret;
}
if (ret.status === 'FAILED') {
throw new WeaviateBackupFailed(ret.error ? ret.error : '<unknown>', 'restoration');
}
if (res.status === 'FAILED') {
throw new WeaviateBackupFailed(res.error ? res.error : '<unknown>', 'restoration');
if (ret.status === 'CANCELED') {
throw new WeaviateBackupCanceled('restoration');
}
await new Promise((resolve) => setTimeout(resolve, 1000)); // eslint-disable-line no-await-in-loop
}
}
return status
? {
...parseResponse(res),
...status,
classes: res.classes,
}
: res;
: parseResponse(res);
},
};
};

export interface Backup {
/**
* Cancel a backup.
*
* @param {BackupCancelArgs} args The arguments for the request.
* @returns {Promise<boolean>} Whether the backup was canceled.
* @throws {WeaviateInvalidInputError} If the input is invalid.
* @throws {WeaviateBackupCancellationError} If the backup cancellation fails.
*/
cancel(args: BackupCancelArgs): Promise<boolean>;
/**
* Create a backup of the database.
*
* @param {BackupArgs} args The arguments for the request.
* @returns {Promise<BackupCreateResponse>} The response from Weaviate.
* @returns {Promise<BackupReturn>} The response from Weaviate.
* @throws {WeaviateInvalidInputError} If the input is invalid.
* @throws {WeaviateBackupFailed} If the backup creation fails.
* @throws {WeaviateBackupCanceled} If the backup creation is canceled.
*/
create(args: BackupArgs<BackupConfigCreate>): Promise<BackupCreateResponse>;
create(args: BackupArgs<BackupConfigCreate>): Promise<BackupReturn>;
/**
* Get the status of a backup creation.
*
* @param {BackupStatusArgs} args The arguments for the request.
* @returns {Promise<BackupCreateStatusResponse>} The status of the backup creation.
* @returns {Promise<BackupStatusReturn>} The status of the backup creation.
* @throws {WeaviateInvalidInputError} If the input is invalid.
*/
getCreateStatus(args: BackupStatusArgs): Promise<BackupCreateStatusResponse>;
getCreateStatus(args: BackupStatusArgs): Promise<BackupStatusReturn>;
/**
* Get the status of a backup restore.
*
* @param {BackupStatusArgs} args The arguments for the request.
* @returns {Promise<BackupRestoreStatusResponse>} The status of the backup restore.
* @returns {Promise<BackupStatusReturn>} The status of the backup restore.
* @throws {WeaviateInvalidInputError} If the input is invalid.
*/
getRestoreStatus(args: BackupStatusArgs): Promise<BackupRestoreStatusResponse>;
getRestoreStatus(args: BackupStatusArgs): Promise<BackupStatusReturn>;
/**
* Restore a backup of the database.
*
* @param {BackupArgs} args The arguments for the request.
* @returns {Promise<BackupRestoreResponse>} The response from Weaviate.
* @returns {Promise<BackupReturn>} The response from Weaviate.
* @throws {WeaviateInvalidInputError} If the input is invalid.
* @throws {WeaviateBackupFailed} If the backup restoration fails.
* @throws {WeaviateBackupCanceled} If the backup restoration is canceled.
*/
restore(args: BackupArgs<BackupConfigRestore>): Promise<BackupRestoreResponse>;
restore(args: BackupArgs<BackupConfigRestore>): Promise<BackupReturn>;
}
Loading
Loading