From 7d3e0b562fa4d58439a3806df3969af96be842af Mon Sep 17 00:00:00 2001 From: Rafael Martins Date: Fri, 5 Apr 2024 15:55:39 +0200 Subject: [PATCH 1/5] adding cloud backup get/restore option --- src/cloudBackup.d.ts | 68 ++++++++++++++++++++++++++++++++++++++++++++ src/cloudBackup.js | 54 +++++++++++++++++++++++++++++++++++ src/index.d.ts | 3 ++ src/index.js | 3 ++ 4 files changed, 128 insertions(+) create mode 100644 src/cloudBackup.d.ts create mode 100644 src/cloudBackup.js diff --git a/src/cloudBackup.d.ts b/src/cloudBackup.d.ts new file mode 100644 index 0000000..a387a53 --- /dev/null +++ b/src/cloudBackup.d.ts @@ -0,0 +1,68 @@ +import {ClusterName, Links, AtlasResultsResponse, AtlasClientOptions, AtlasError} from "."; + +export interface GetReplicaSetCloudBackup { + cloudProvider: 'AWS' | 'GCP' | 'AZURE' | 'TENANT'; + copyRegions: string[]; + createdAt: string; + description?: string; + expiresAt: string; + frequencyType: 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly'; + id: string; + links: Links; + masterKeyUUID?: string; + mongodVersion: string; + policyItems: Array; + replicaSetName: string; + snapshotType: 'onDemand' | 'scheduled'; + status: 'queued' | 'inProgress' | 'completed' | 'failed'; + storageSizeBytes: number; + type: string +} + +export type GetAllReplicaSetCloudBackups = AtlasResultsResponse; + +export interface RestoreSnapshotJobComponent { + replicaSetName: string +} + +export interface GetRestoreSnapshotJob { + cancelled?: boolean; + components?: RestoreSnapshotJobComponent[]; + deliveryType: 'automated' | 'download' | 'pointInTime'; + deliveryUrl?: string[]; + desiredTimestamp?: { + date: string; + increment: number + }; + expired?: boolean; + expiresAt?: string; + failed?: boolean; + finishedAt?: string; + id?: string; + links?: Links; + oplogInc?: number; + oplogTs?: number; + pointInTimeUTCSeconds?: number; + snapshotId?: string; + targetClusterName?: string; + targetGroupId?: string; + timestamp?: string +} + +export interface RestoreSnapshotJobRequest { + deliveryType: 'automated' | 'download' | 'pointInTime'; + oplogInc?: number; + oplogTs?: number; + pointInTimeUTCSeconds?: number; + snapshotId?: string; + targetClusterName?: string; + targetGroupId?: string +} +export type CreateRestoreSnapshotJobResponse = GetClusterResponse; + +export interface Cluster { + getReplicaSetCloudBackup(clustername: ClusterName, snapshotId: string, options?: AtlasClientOptions): Promise; + getAllReplicaSetCloudBackups(clustername: ClusterName, options?: AtlasClientOptions): Promise; + getRestoreSnapshotJob(clustername: ClusterName, restoreJobId: string): Promise + createRestoreSnapshotJob(clustername: ClusterName, body: RestoreSnapshotJobRequest): Promise +} \ No newline at end of file diff --git a/src/cloudBackup.js b/src/cloudBackup.js new file mode 100644 index 0000000..061660c --- /dev/null +++ b/src/cloudBackup.js @@ -0,0 +1,54 @@ +const {getQueryStringFromOptions} = require("mongodb-atlas-api-client/src/helper"); + +class CloudBackup { + + constructor(client, baseUrl, projectId) { + this.client_ = client; + this.baseUrl_ = baseUrl; + this.projectId_ = projectId; + } + + async getReplicaSetCloudBackup(clustername, snapshotId, options = {}) { + const queryString = getQueryStringFromOptions(options); + const httpOptions = options.httpOptions; + const response = ( + await this.client_.fetch(`${this.baseUrl_}/groups/${this.projectId_}/clusters/${clustername}/backup/snapshots/${snapshotId}?${queryString}`, httpOptions) + ); + return response; + } + + async getAllReplicaSetCloudBackups(clustername, options = {}) { + const queryString = getQueryStringFromOptions(options); + const httpOptions = options.httpOptions; + const response = ( + await this.client_.fetch(`${this.baseUrl_}/groups/${this.projectId_}/clusters/${clustername}/backup/snapshots?${queryString}`, httpOptions) + ); + return response; + } + + async getRestoreSnapshotJob(clustername, restoreJobId, options = {}) { + const queryString = getQueryStringFromOptions(options); + const httpOptions = options.httpOptions; + const response = ( + await this.client_.fetch(`${this.baseUrl_}/groups/${this.projectId_}/clusters/${clustername}/backup/restoreJobs/${restoreJobId}?${queryString}`, httpOptions) + ); + return response; + } + + async createRestoreSnapshotJob(clustername, body, options = {}) { + const queryString = getQueryStringFromOptions(options); + const httpOptions = options.httpOptions; + const response = ( + await this.client_.fetch(`${this.baseUrl_}/groups/${this.projectId_}/clusters/${clustername}/backup/restoreJobs?${queryString}`, { + "method": "POST", + "data": body, + "headers": {"Content-Type": "application/json"}, + ...httpOptions + }) + ); + return response; + } +} + +module.exports = CloudBackup; + diff --git a/src/index.d.ts b/src/index.d.ts index c3803cd..3fc63ec 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1,6 +1,7 @@ import {Alert} from './alert' import {AtlasUser} from './atlasUser'; import {Cluster} from './cluster'; +import {CloudBackup} from './cloudBackup'; import {CustomDbRole} from './customDbRole'; import {Event} from './event'; import {Organization} from './organization'; @@ -15,6 +16,7 @@ import {AtlasSearch} from './atlasSearch'; export * from './alert' export * from './atlasUser'; export * from './cluster'; +export * from './cloudBackup'; export * from './customDbRole'; export * from './event'; export * from './organization'; @@ -66,6 +68,7 @@ export interface AtlasClient { projectAccesslist: ProjectAccesslist; customDbRole: CustomDbRole; cluster: Cluster; + cloudBackup: CloudBackup; event: Event; dataLake: DataLake; cloudProviderAccess: CloudProviderAccess; diff --git a/src/index.js b/src/index.js index d53dedb..dfed608 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ const User = require("./user"); +const CloudBackup = require("./cloudBackup"); const Cluster = require("./cluster"); const CustomDbRole = require("./customDbRole"); const ProjectWhitelist = require("./projectWhitelist"); @@ -30,6 +31,7 @@ function getMongodbAtlasApiClient(options) { const client = new HttpClient(urllibClient, options.publicKey, options.privateKey); const user = new User(client, options.baseUrl, options.projectId); const cluster = new Cluster(client, options.baseUrl, options.projectId); + const cloudBackup = new CloudBackup(client, options.baseUrl, options.projectId); const customDbRole = new CustomDbRole(client, options.baseUrl, options.projectId); const projectWhitelist = new ProjectWhitelist(client, options.baseUrl, options.projectId); const projectAccesslist = new ProjectAccesslist(client, options.baseUrl, options.projectId); @@ -45,6 +47,7 @@ function getMongodbAtlasApiClient(options) { const functions = {}; functions.user = getFunctions(user); functions.cluster = getFunctions(cluster); + functions.cloudBackup = getFunctions(cloudBackup); functions.customDbRole = getFunctions(customDbRole); functions.projectWhitelist = getFunctions(projectWhitelist); functions.projectAccesslist = getFunctions(projectAccesslist); From 932c1a2a00e756eab7523e5268c781cae5d11070 Mon Sep 17 00:00:00 2001 From: Rafael Martins Date: Fri, 5 Apr 2024 15:59:39 +0200 Subject: [PATCH 2/5] adding missing stuff --- src/cloudBackup.d.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cloudBackup.d.ts b/src/cloudBackup.d.ts index a387a53..cb310f6 100644 --- a/src/cloudBackup.d.ts +++ b/src/cloudBackup.d.ts @@ -25,9 +25,11 @@ export interface RestoreSnapshotJobComponent { replicaSetName: string } +export type RestoreSnapshotJobComponents = RestoreSnapshotJobComponent[] + export interface GetRestoreSnapshotJob { cancelled?: boolean; - components?: RestoreSnapshotJobComponent[]; + components?: RestoreSnapshotJobComponents; deliveryType: 'automated' | 'download' | 'pointInTime'; deliveryUrl?: string[]; desiredTimestamp?: { @@ -63,6 +65,6 @@ export type CreateRestoreSnapshotJobResponse = GetClusterResponse; export interface Cluster { getReplicaSetCloudBackup(clustername: ClusterName, snapshotId: string, options?: AtlasClientOptions): Promise; getAllReplicaSetCloudBackups(clustername: ClusterName, options?: AtlasClientOptions): Promise; - getRestoreSnapshotJob(clustername: ClusterName, restoreJobId: string): Promise - createRestoreSnapshotJob(clustername: ClusterName, body: RestoreSnapshotJobRequest): Promise + getRestoreSnapshotJob(clustername: ClusterName, restoreJobId: string, options?: AtlasClientOptions): Promise + createRestoreSnapshotJob(clustername: ClusterName, body: RestoreSnapshotJobRequest, options?: AtlasClientOptions): Promise } \ No newline at end of file From 6e07548ce62b00f77dbec842e32f5e3d5c7ee0a1 Mon Sep 17 00:00:00 2001 From: Rafael Martins Date: Mon, 8 Apr 2024 10:43:55 +0200 Subject: [PATCH 3/5] renaming some functions; adding docs --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++ src/cloudBackup.d.ts | 18 +++++++-------- src/cloudBackup.js | 7 +++--- 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 386fbb6..367fc36 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ const response = await user.update("someUserName", body, options); // update use Following entities are currently supported - [User](#user) +- [CloudBackup](#cloudbackup) - [Cluster](#cluster) - [CustomDbRole](#customdbrole) - [ProjectWhitelist](#projectwhitelist) @@ -128,6 +129,59 @@ Function - Deletes the user name passed. More details - https://docs.atlas.mongodb.com/reference/api/database-users-delete-a-user/ +### CloudBackup + +### cloudBackup.getReplicaSetCloudBackup(clustername, snapshotId, [options]) ⇒ Promise +Function - Returns the details of the specified snapshotId. + +**Returns**: Promise - - promise which resolves on success and rejects on error + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| clustername | String | | name of the cluster for which details needs to be retrieved | +| snapshotId | String | | Id of the snapshot for which details needs to be retrieved | +| [options] | Object | {} | Optional object containing extra query strings which will be passed to atlas api. It can also include httpOptions which will be sent to `urllib`. More info can be found here - https://github.com/node-modules/urllib | + +More details - https://www.mongodb.com/docs/atlas/reference/api-resources-spec/v1/#tag/Cloud-Backups/operation/getReplicaSetBackup + +### cloudBackup.getAllReplicaSetCloudBackups(clustername, [options]) ⇒ Promise +Function - Returns the details of all snapshots of an specified clustername. + +**Returns**: Promise - - promise which resolves on success and rejects on error + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| clustername | String | | name of the cluster for which details needs to be retrieved | +| [options] | Object | {} | Optional object containing extra query strings which will be passed to atlas api. It can also include httpOptions which will be sent to `urllib`. More info can be found here - https://github.com/node-modules/urllib | + +More details - https://www.mongodb.com/docs/atlas/reference/api-resources-spec/v1/#tag/Cloud-Backups/operation/listReplicaSetBackups + +### cloudBackup.getSnapshotRestoreJob(clustername, restoreJobId, [options]) ⇒ Promise +Function - Returns the details of all snapshots of an specified clustername. + +**Returns**: Promise - - promise which resolves on success and rejects on error + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| clustername | String | | name of the cluster for which details needs to be retrieved | +| restoreJobId | String | | snapshot restore job id for which details needs to be retrieved | +| [options] | Object | {} | Optional object containing extra query strings which will be passed to atlas api. It can also include httpOptions which will be sent to `urllib`. More info can be found here - https://github.com/node-modules/urllib | + +More details - https://www.mongodb.com/docs/atlas/reference/api-resources-spec/v1/#tag/Cloud-Backups/operation/getBackupRestoreJob + +### cloudBackup.createSnapshotRestoreJob(clustername, body, [options]) ⇒ Promise +Function - Returns the details of all snapshots of an specified clustername. + +**Returns**: Promise - - promise which resolves on success and rejects on error + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| clustername | String | | name of the cluster for which details needs to be retrieved | +| body | Object | | Body which has details for snapshot restore job which needs to be created | +| [options] | Object | {} | Optional object containing extra query strings which will be passed to atlas api. It can also include httpOptions which will be sent to `urllib`. More info can be found here - https://github.com/node-modules/urllib | + +More details - https://www.mongodb.com/docs/atlas/reference/api-resources-spec/v1/#tag/Cloud-Backups/operation/createBackupRestoreJob + ### Cluster ### cluster.get(clustername, [options]) ⇒ Promise diff --git a/src/cloudBackup.d.ts b/src/cloudBackup.d.ts index cb310f6..ea91e4c 100644 --- a/src/cloudBackup.d.ts +++ b/src/cloudBackup.d.ts @@ -21,15 +21,15 @@ export interface GetReplicaSetCloudBackup { export type GetAllReplicaSetCloudBackups = AtlasResultsResponse; -export interface RestoreSnapshotJobComponent { +export interface SnapshotRestoreJobComponent { replicaSetName: string } -export type RestoreSnapshotJobComponents = RestoreSnapshotJobComponent[] +export type SnapshotRestoreJobComponents = SnapshotRestoreJobComponent[] -export interface GetRestoreSnapshotJob { +export interface GetSnapshotRestoreJob { cancelled?: boolean; - components?: RestoreSnapshotJobComponents; + components?: SnapshotRestoreJobComponents; deliveryType: 'automated' | 'download' | 'pointInTime'; deliveryUrl?: string[]; desiredTimestamp?: { @@ -51,7 +51,7 @@ export interface GetRestoreSnapshotJob { timestamp?: string } -export interface RestoreSnapshotJobRequest { +export interface SnapshotRestoreJobRequest { deliveryType: 'automated' | 'download' | 'pointInTime'; oplogInc?: number; oplogTs?: number; @@ -60,11 +60,11 @@ export interface RestoreSnapshotJobRequest { targetClusterName?: string; targetGroupId?: string } -export type CreateRestoreSnapshotJobResponse = GetClusterResponse; +export type CreateSnapshotRestoreJobResponse = GetSnapshotRestoreJob; -export interface Cluster { +export interface CloudBackup { getReplicaSetCloudBackup(clustername: ClusterName, snapshotId: string, options?: AtlasClientOptions): Promise; getAllReplicaSetCloudBackups(clustername: ClusterName, options?: AtlasClientOptions): Promise; - getRestoreSnapshotJob(clustername: ClusterName, restoreJobId: string, options?: AtlasClientOptions): Promise - createRestoreSnapshotJob(clustername: ClusterName, body: RestoreSnapshotJobRequest, options?: AtlasClientOptions): Promise + getSnapshotRestoreJob(clustername: ClusterName, restoreJobId: string, options?: AtlasClientOptions): Promise + createSnapshotRestoreJob(clustername: ClusterName, body: SnapshotRestoreJobRequest, options?: AtlasClientOptions): Promise } \ No newline at end of file diff --git a/src/cloudBackup.js b/src/cloudBackup.js index 061660c..3d8af6d 100644 --- a/src/cloudBackup.js +++ b/src/cloudBackup.js @@ -1,4 +1,4 @@ -const {getQueryStringFromOptions} = require("mongodb-atlas-api-client/src/helper"); +const {getQueryStringFromOptions} = require("./helper"); class CloudBackup { @@ -26,7 +26,7 @@ class CloudBackup { return response; } - async getRestoreSnapshotJob(clustername, restoreJobId, options = {}) { + async getSnapshotRestoreJob(clustername, restoreJobId, options = {}) { const queryString = getQueryStringFromOptions(options); const httpOptions = options.httpOptions; const response = ( @@ -35,7 +35,7 @@ class CloudBackup { return response; } - async createRestoreSnapshotJob(clustername, body, options = {}) { + async createSnapshotRestoreJob(clustername, body, options = {}) { const queryString = getQueryStringFromOptions(options); const httpOptions = options.httpOptions; const response = ( @@ -51,4 +51,3 @@ class CloudBackup { } module.exports = CloudBackup; - From 4b40281662888a6bc69be4f125dfcefca59f386d Mon Sep 17 00:00:00 2001 From: Rafael Martins Date: Mon, 8 Apr 2024 11:15:46 +0200 Subject: [PATCH 4/5] adding tests --- test/cloudbackup.test.js | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 test/cloudbackup.test.js diff --git a/test/cloudbackup.test.js b/test/cloudbackup.test.js new file mode 100644 index 0000000..8a8ae72 --- /dev/null +++ b/test/cloudbackup.test.js @@ -0,0 +1,70 @@ +const {describe, it} = exports.lab = require("@hapi/lab").script(); +const {expect} = require("@hapi/code"); +const nock = require("nock"); +const getClient = require("../src"); + +const baseUrl = "http://dummyBaseUrl"; +const projectId = "dummyProjectId"; + +const client = getClient({ + "publicKey": "dummuyPublicKey", + "privateKey": "dummyPrivateKey", + "baseUrl": baseUrl, + "projectId": projectId +}); + +describe("Mongo Atlas Api Client - CloudBackup", () => { + + describe("When cluster is exported from index", () => { + it("should export cluster functions", async () => { + expect(client.cloudBackup.getReplicaSetCloudBackup).to.be.function(); + expect(client.cloudBackup.getAllReplicaSetCloudBackups).to.be.function(); + expect(client.cloudBackup.getSnapshotRestoreJob).to.be.function(); + expect(client.cloudBackup.createSnapshotRestoreJob).to.be.function(); + }); + }); + + describe("When getReplicaSetCloudBackup is called with querystring parameters", () => { + it("should return response", async () => { + const expectedRequest = nock(baseUrl) + .get(`/groups/${projectId}/clusters/mycluster/backup/snapshots/mysnapshot?key1=value1&key2=value2`) + .reply(200, {"replicaSetName": "mycluster"}); + const result = await client.cloudBackup.getReplicaSetCloudBackup("mycluster","mysnapshot",{"key1": "value1", "key2": "value2"}); + expect(result).to.equal({"replicaSetName": "mycluster"}); + expect(expectedRequest.isDone()).to.be.true(); + }); + }); + + describe("When getAllReplicaSetCloudBackups is called with querystring parameters", () => { + it("should return response", async () => { + const expectedRequest = nock(baseUrl) + .get(`/groups/${projectId}/clusters/mycluster/backup/snapshots?key1=value1&key2=value2`) + .reply(200, [{"replicaSetName": "mycluster"}]); + const result = await client.cloudBackup.getAllReplicaSetCloudBackups("mycluster",{"key1": "value1", "key2": "value2"}); + expect(result).to.equal([{"replicaSetName": "mycluster"}]); + expect(expectedRequest.isDone()).to.be.true(); + }); + }); + + describe("When getSnapshotRestoreJob is called with querystring parameters", () => { + it("should return response", async () => { + const expectedRequest = nock(baseUrl) + .get(`/groups/${projectId}/clusters/mycluster/backup/restoreJobs/myrestorejob?key1=value1&key2=value2`) + .reply(200, {"id": "myrestorejob"}); + const result = await client.cloudBackup.getSnapshotRestoreJob("mycluster","myrestorejob",{"key1": "value1", "key2": "value2"}); + expect(result).to.equal({"id": "myrestorejob"}); + expect(expectedRequest.isDone()).to.be.true(); + }); + }); + + describe("When createSnapshotRestoreJob is called with querystring parameters", () => { + it("should return response", async () => { + const expectedRequest = nock(baseUrl) + .post(`/groups/${projectId}/clusters/mycluster/backup/restoreJobs?key1=value1&key2=value2`) + .reply(200, {"id": "myrestorejob"}); + const result = await client.cloudBackup.createSnapshotRestoreJob("mycluster",{"body": "value"},{"key1": "value1", "key2": "value2"}); + expect(result).to.equal({"id": "myrestorejob"}); + expect(expectedRequest.isDone()).to.be.true(); + }); + }); +}); From 8e1afe4a4c565c606063e43e2cdc627041c39b7c Mon Sep 17 00:00:00 2001 From: Rafael Martins Date: Mon, 8 Apr 2024 11:17:52 +0200 Subject: [PATCH 5/5] fix spaces --- test/cloudbackup.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/cloudbackup.test.js b/test/cloudbackup.test.js index 8a8ae72..0e66216 100644 --- a/test/cloudbackup.test.js +++ b/test/cloudbackup.test.js @@ -29,7 +29,7 @@ describe("Mongo Atlas Api Client - CloudBackup", () => { const expectedRequest = nock(baseUrl) .get(`/groups/${projectId}/clusters/mycluster/backup/snapshots/mysnapshot?key1=value1&key2=value2`) .reply(200, {"replicaSetName": "mycluster"}); - const result = await client.cloudBackup.getReplicaSetCloudBackup("mycluster","mysnapshot",{"key1": "value1", "key2": "value2"}); + const result = await client.cloudBackup.getReplicaSetCloudBackup("mycluster", "mysnapshot", {"key1": "value1", "key2": "value2"}); expect(result).to.equal({"replicaSetName": "mycluster"}); expect(expectedRequest.isDone()).to.be.true(); }); @@ -40,7 +40,7 @@ describe("Mongo Atlas Api Client - CloudBackup", () => { const expectedRequest = nock(baseUrl) .get(`/groups/${projectId}/clusters/mycluster/backup/snapshots?key1=value1&key2=value2`) .reply(200, [{"replicaSetName": "mycluster"}]); - const result = await client.cloudBackup.getAllReplicaSetCloudBackups("mycluster",{"key1": "value1", "key2": "value2"}); + const result = await client.cloudBackup.getAllReplicaSetCloudBackups("mycluster", {"key1": "value1", "key2": "value2"}); expect(result).to.equal([{"replicaSetName": "mycluster"}]); expect(expectedRequest.isDone()).to.be.true(); }); @@ -51,7 +51,7 @@ describe("Mongo Atlas Api Client - CloudBackup", () => { const expectedRequest = nock(baseUrl) .get(`/groups/${projectId}/clusters/mycluster/backup/restoreJobs/myrestorejob?key1=value1&key2=value2`) .reply(200, {"id": "myrestorejob"}); - const result = await client.cloudBackup.getSnapshotRestoreJob("mycluster","myrestorejob",{"key1": "value1", "key2": "value2"}); + const result = await client.cloudBackup.getSnapshotRestoreJob("mycluster", "myrestorejob", {"key1": "value1", "key2": "value2"}); expect(result).to.equal({"id": "myrestorejob"}); expect(expectedRequest.isDone()).to.be.true(); }); @@ -62,7 +62,7 @@ describe("Mongo Atlas Api Client - CloudBackup", () => { const expectedRequest = nock(baseUrl) .post(`/groups/${projectId}/clusters/mycluster/backup/restoreJobs?key1=value1&key2=value2`) .reply(200, {"id": "myrestorejob"}); - const result = await client.cloudBackup.createSnapshotRestoreJob("mycluster",{"body": "value"},{"key1": "value1", "key2": "value2"}); + const result = await client.cloudBackup.createSnapshotRestoreJob("mycluster", {"body": "value"}, {"key1": "value1", "key2": "value2"}); expect(result).to.equal({"id": "myrestorejob"}); expect(expectedRequest.isDone()).to.be.true(); });