From 0866cf95a75c90dc2c321b17dc160577e3b281fc Mon Sep 17 00:00:00 2001 From: "Bob \"Wombat\" Hogg" Date: Tue, 10 Oct 2023 12:39:48 -0400 Subject: [PATCH] Allow enabling and disabling PITR on Firestore databases (#6427) Also show Point In Time Recovery setting, earliest version time, and version retention period on get --- CHANGELOG.md | 1 + src/commands/firestore-databases-create.ts | 22 +++++++++++++++++++- src/commands/firestore-databases-update.ts | 24 ++++++++++++++++++++-- src/firestore/api-types.ts | 14 +++++++++++++ src/firestore/api.ts | 15 +++++++++++--- src/firestore/options.ts | 1 + 6 files changed, 71 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb2d..564b82f756c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Added support for enabling, disabling, and displaying Point In Time Recovery enablement state on Firestore databases (#6388) diff --git a/src/commands/firestore-databases-create.ts b/src/commands/firestore-databases-create.ts index 271fba07dcd..95547a3aa3b 100644 --- a/src/commands/firestore-databases-create.ts +++ b/src/commands/firestore-databases-create.ts @@ -18,6 +18,10 @@ export const command = new Command("firestore:databases:create ") "--delete-protection ", "Whether or not to prevent deletion of database, for example 'ENABLED' or 'DISABLED'. Default is 'DISABLED'" ) + .option( + "--point-in-time-recovery ", + "Whether to enable the PITR feature on this database, for example 'ENABLED' or 'DISABLED'. Default is 'DISABLED'" + ) .before(requirePermissions, ["datastore.databases.create"]) .before(warnEmulatorNotSupported, Emulators.FIRESTORE) .action(async (database: string, options: FirestoreOptions) => { @@ -45,12 +49,28 @@ export const command = new Command("firestore:databases:create ") ? types.DatabaseDeleteProtectionState.ENABLED : types.DatabaseDeleteProtectionState.DISABLED; + if ( + options.pointInTimeRecovery && + options.pointInTimeRecovery !== types.PointInTimeRecoveryEnablementOption.ENABLED && + options.pointInTimeRecovery !== types.PointInTimeRecoveryEnablementOption.DISABLED + ) { + logger.error( + "Invalid value for flag --point-in-time-recovery. See firebase firestore:databases:create --help for more info." + ); + return; + } + const pointInTimeRecoveryEnablement: types.PointInTimeRecoveryEnablement = + options.pointInTimeRecovery === types.PointInTimeRecoveryEnablementOption.ENABLED + ? types.PointInTimeRecoveryEnablement.ENABLED + : types.PointInTimeRecoveryEnablement.DISABLED; + const databaseResp: types.DatabaseResp = await api.createDatabase( options.project, database, options.location, type, - deleteProtectionState + deleteProtectionState, + pointInTimeRecoveryEnablement ); if (options.json) { diff --git a/src/commands/firestore-databases-update.ts b/src/commands/firestore-databases-update.ts index 88eea9b76af..c0a8ac305c8 100644 --- a/src/commands/firestore-databases-update.ts +++ b/src/commands/firestore-databases-update.ts @@ -17,12 +17,16 @@ export const command = new Command("firestore:databases:update ") "--delete-protection ", "Whether or not to prevent deletion of database, for example 'ENABLED' or 'DISABLED'. Default is 'DISABLED'" ) + .option( + "--point-in-time-recovery ", + "Whether to enable the PITR feature on this database, for example 'ENABLED' or 'DISABLED'. Default is 'DISABLED'" + ) .before(requirePermissions, ["datastore.databases.update"]) .before(warnEmulatorNotSupported, Emulators.FIRESTORE) .action(async (database: string, options: FirestoreOptions) => { const api = new fsi.FirestoreApi(); - if (!options.type && !options.deleteProtection) { + if (!options.type && !options.deleteProtection && !options.pointInTimeRecovery) { logger.error( "Missing properties to update. See firebase firestore:databases:update --help for more info." ); @@ -44,11 +48,27 @@ export const command = new Command("firestore:databases:update ") ? types.DatabaseDeleteProtectionState.ENABLED : types.DatabaseDeleteProtectionState.DISABLED; + if ( + options.pointInTimeRecovery && + options.pointInTimeRecovery !== types.PointInTimeRecoveryEnablementOption.ENABLED && + options.pointInTimeRecovery !== types.PointInTimeRecoveryEnablementOption.DISABLED + ) { + logger.error( + "Invalid value for flag --point-in-time-recovery. See firebase firestore:databases:update --help for more info." + ); + return; + } + const pointInTimeRecoveryEnablement: types.PointInTimeRecoveryEnablement = + options.pointInTimeRecovery === types.PointInTimeRecoveryEnablementOption.ENABLED + ? types.PointInTimeRecoveryEnablement.ENABLED + : types.PointInTimeRecoveryEnablement.DISABLED; + const databaseResp: types.DatabaseResp = await api.updateDatabase( options.project, database, type, - deleteProtectionState + deleteProtectionState, + pointInTimeRecoveryEnablement ); if (options.json) { diff --git a/src/firestore/api-types.ts b/src/firestore/api-types.ts index 9ceec083c89..13c91580e65 100644 --- a/src/firestore/api-types.ts +++ b/src/firestore/api-types.ts @@ -105,10 +105,21 @@ export enum DatabaseDeleteProtectionState { DISABLED = "DELETE_PROTECTION_DISABLED", } +export enum PointInTimeRecoveryEnablementOption { + ENABLED = "ENABLED", + DISABLED = "DISABLED", +} + +export enum PointInTimeRecoveryEnablement { + ENABLED = "POINT_IN_TIME_RECOVERY_ENABLED", + DISABLED = "POINT_IN_TIME_RECOVERY_DISABLED", +} + export interface DatabaseReq { locationId?: string; type?: DatabaseType; deleteProtectionState?: DatabaseDeleteProtectionState; + pointInTimeRecoveryEnablement?: PointInTimeRecoveryEnablement; } export interface DatabaseResp { @@ -122,5 +133,8 @@ export interface DatabaseResp { appEngineIntegrationMode: string; keyPrefix: string; deleteProtectionState: DatabaseDeleteProtectionState; + pointInTimeRecoveryEnablement: PointInTimeRecoveryEnablement; etag: string; + versionRetentionPeriod: string; + earliestVersionTime: string; } diff --git a/src/firestore/api.ts b/src/firestore/api.ts index a00edab8bb6..47a186bd103 100644 --- a/src/firestore/api.ts +++ b/src/firestore/api.ts @@ -321,7 +321,10 @@ export class FirestoreApi { ["Last Update Time", clc.yellow(database.updateTime)], ["Type", clc.yellow(database.type)], ["Location", clc.yellow(database.locationId)], - ["Delete Protection State", clc.yellow(database.deleteProtectionState)] + ["Delete Protection State", clc.yellow(database.deleteProtectionState)], + ["Point In Time Recovery", clc.yellow(database.pointInTimeRecoveryEnablement)], + ["Earliest Version Time", clc.yellow(database.earliestVersionTime)], + ["Version Retention Period", clc.yellow(database.versionRetentionPeriod)] ); logger.info(table.toString()); } @@ -716,19 +719,22 @@ export class FirestoreApi { * @param locationId the id of the region the database will be created in * @param type FIRESTORE_NATIVE or DATASTORE_MODE * @param deleteProtectionState DELETE_PROTECTION_ENABLED or DELETE_PROTECTION_DISABLED + * @param pointInTimeRecoveryEnablement POINT_IN_TIME_RECOVERY_ENABLED or POINT_IN_TIME_RECOVERY_DISABLED */ async createDatabase( project: string, databaseId: string, locationId: string, type: types.DatabaseType, - deleteProtectionState: types.DatabaseDeleteProtectionState + deleteProtectionState: types.DatabaseDeleteProtectionState, + pointInTimeRecoveryEnablement: types.PointInTimeRecoveryEnablement ): Promise { const url = `/projects/${project}/databases`; const payload: types.DatabaseReq = { type, locationId, deleteProtectionState, + pointInTimeRecoveryEnablement, }; const options = { queryParams: { databaseId: databaseId } }; const res = await this.apiClient.post( @@ -750,17 +756,20 @@ export class FirestoreApi { * @param databaseId the name of the Firestore Database * @param type FIRESTORE_NATIVE or DATASTORE_MODE * @param deleteProtectionState DELETE_PROTECTION_ENABLED or DELETE_PROTECTION_DISABLED + * @param pointInTimeRecoveryEnablement POINT_IN_TIME_RECOVERY_ENABLED or POINT_IN_TIME_RECOVERY_DISABLED */ async updateDatabase( project: string, databaseId: string, type?: types.DatabaseType, - deleteProtectionState?: types.DatabaseDeleteProtectionState + deleteProtectionState?: types.DatabaseDeleteProtectionState, + pointInTimeRecoveryEnablement?: types.PointInTimeRecoveryEnablement ): Promise { const url = `/projects/${project}/databases/${databaseId}`; const payload: types.DatabaseReq = { type, deleteProtectionState, + pointInTimeRecoveryEnablement, }; const res = await this.apiClient.patch( url, diff --git a/src/firestore/options.ts b/src/firestore/options.ts index e27d83896f6..5576f14f3a0 100644 --- a/src/firestore/options.ts +++ b/src/firestore/options.ts @@ -16,4 +16,5 @@ export interface FirestoreOptions extends Options { location?: string; type?: types.DatabaseType; deleteProtection?: types.DatabaseDeleteProtectionStateOption; + pointInTimeRecoveryEnablement?: types.PointInTimeRecoveryEnablementOption; }