diff --git a/mongodbatlas/backup_compliance_policy.go b/mongodbatlas/backup_compliance_policy.go new file mode 100644 index 000000000..2cde32a31 --- /dev/null +++ b/mongodbatlas/backup_compliance_policy.go @@ -0,0 +1,114 @@ +// Copyright 2023 MongoDB Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mongodbatlas + +import ( + "context" + "fmt" + "net/http" +) + +const ( + BackupCompliancePolicyBasePath = "api/atlas/v1.0/groups/%s/backupCompliancePolicy" +) + +// BackupCompliancePolicyService is an interface for interfacing with the Backup Compliance Policy +// endpoints of the MongoDB Atlas API. +// +// See more: https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#tag/Cloud-Backups/operation/updateDataProtectionSettings +type BackupCompliancePolicyService interface { + Get(context.Context, string) (*BackupCompliancePolicy, *Response, error) + Update(context.Context, string, *BackupCompliancePolicy) (*BackupCompliancePolicy, *Response, error) +} + +// CloudProviderSnapshotBackupPolicyServiceOp handles communication with the BackupCompliancePolicyService related methods of the +// MongoDB Atlas API. +type BackupCompliancePolicyServiceOp service + +var _ BackupCompliancePolicyService = &BackupCompliancePolicyServiceOp{} + +// BackupCompliancePolicy represents a backup compiance policy. +type BackupCompliancePolicy struct { + AuthorizedEmail string `json:"authorizedEmail,omitempty"` + CopyProtectionEnabled *bool `json:"copyProtectionEnabled,omitempty"` + EncryptionAtRestEnabled *bool `json:"encryptionAtRestEnabled,omitempty"` + OnDemandPolicyItem PolicyItem `json:"onDemandPolicyItem,omitempty"` + PitEnabled *bool `json:"pitEnabled,omitempty"` + ProjectID string `json:"projectId,omitempty"` + RestoreWindowDays *int64 `json:"restoreWindowDays,omitempty"` + ScheduledPolicyItems []ScheduledPolicyItem `json:"scheduledPolicyItems,omitempty"` + State string `json:"state,omitempty"` + UpdatedDate string `json:"updatedDate,omitempty"` + UpdatedUser string `json:"updatedUser,omitempty"` +} + +// PolicyItem represents a specifications for a scheduled backup policy and on demand policy. +type ScheduledPolicyItem struct { + ID string `json:"id,omitempty"` // Unique identifier of the backup policy item. + FrequencyInterval int `json:"frequencyInterval,omitempty"` // Desired frequency of the new backup policy item specified by frequencyType. + FrequencyType string `json:"frequencyType,omitempty"` // Frequency associated with the backup policy item. One of the following values: hourly, daily, weekly or monthly. + RetentionUnit string `json:"retentionUnit,omitempty"` // Metric of duration of the backup policy item: days, weeks, or months. + RetentionValue int `json:"retentionValue,omitempty"` // Duration for which the backup is kept. Associated with retentionUnit. +} + +// Get gets the current snapshot schedule and retention settings for the cluster with {CLUSTER-NAME}. +// See more: https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#tag/Cloud-Backups/operation/getDataProtectionSettings +func (s *BackupCompliancePolicyServiceOp) Get(ctx context.Context, groupID string) (*BackupCompliancePolicy, *Response, error) { + if groupID == "" { + return nil, nil, NewArgError("groupId", "must be set") + } + + path := fmt.Sprintf(BackupCompliancePolicyBasePath, groupID) + + req, err := s.Client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + root := new(BackupCompliancePolicy) + resp, err := s.Client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + return root, resp, err +} + +// Update updates the snapshot schedule or retention settings for the cluster with {CLUSTER-NAME}. +// See more: https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#tag/Cloud-Backups/operation/updateDataProtectionSettings +func (s *BackupCompliancePolicyServiceOp) Update(ctx context.Context, groupID string, createRequest *BackupCompliancePolicy) (*BackupCompliancePolicy, *Response, error) { + if groupID == "" { + return nil, nil, NewArgError("groupId", "must be set") + } + + if createRequest == nil { + return nil, nil, NewArgError("createRequest", "cannot be nil") + } + + path := fmt.Sprintf(BackupCompliancePolicyBasePath, groupID) + + req, err := s.Client.NewRequest(ctx, http.MethodPut, path, createRequest) + if err != nil { + return nil, nil, err + } + + root := new(BackupCompliancePolicy) + resp, err := s.Client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + return root, resp, err +} diff --git a/mongodbatlas/backup_compliance_policy_test.go b/mongodbatlas/backup_compliance_policy_test.go new file mode 100644 index 000000000..60893bbf9 --- /dev/null +++ b/mongodbatlas/backup_compliance_policy_test.go @@ -0,0 +1,287 @@ +// Copyright 2023 MongoDB Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mongodbatlas + +import ( + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/go-test/deep" +) + +func TestBackupCompliancePolicy_Get(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + path := fmt.Sprintf("/api/atlas/v1.0/groups/%s/backupCompliancePolicy", groupID) + + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, `{ + "authorizedEmail": "user@example.com", + "copyProtectionEnabled": false, + "encryptionAtRestEnabled": false, + "onDemandPolicyItem": + { + "frequencyInterval": 1, + "frequencyType": "daily", + "id": "32b6e34b3d91647abb20e7b9", + "retentionUnit": "days", + "retentionValue": 0 + }, + "pitEnabled": false, + "projectId": "32b6e34b3d91647abb20e7b8", + "restoreWindowDays": 7, + "scheduledPolicyItems": + [ + { + "frequencyInterval": 1, + "frequencyType": "daily", + "id": "32b6e34b3d91647abb20e7b9", + "retentionUnit": "days", + "retentionValue": 0 + }, + { + "frequencyInterval": 1, + "frequencyType": "hourly", + "id": "5c95242c87d9d636e70c28f2", + "retentionUnit": "days", + "retentionValue": 0 + } + ], + "state": "ACTIVE", + "updatedDate": "2019-08-24T14:15:22Z", + "updatedUser": "user@example.com" + }`) + }) + + backupCompliancePolicy, _, err := client.BackupCompliancePolicy.Get(ctx, groupID) + if err != nil { + t.Fatalf("BackupCompliancePolicy.Get returned error: %v", err) + } + + expected := &BackupCompliancePolicy{ + AuthorizedEmail: "user@example.com", + CopyProtectionEnabled: pointer(false), + EncryptionAtRestEnabled: pointer(false), + ProjectID: "32b6e34b3d91647abb20e7b8", + RestoreWindowDays: pointer(int64(7)), + State: "ACTIVE", + UpdatedDate: "2019-08-24T14:15:22Z", + UpdatedUser: "user@example.com", + PitEnabled: pointer(false), + OnDemandPolicyItem: PolicyItem{ + ID: "32b6e34b3d91647abb20e7b9", + FrequencyInterval: 1, + FrequencyType: "daily", + RetentionUnit: "days", + RetentionValue: 0, + }, + ScheduledPolicyItems: []ScheduledPolicyItem{ + { + ID: "32b6e34b3d91647abb20e7b9", + FrequencyInterval: 1, + FrequencyType: "daily", + RetentionUnit: "days", + RetentionValue: 0, + }, + { + ID: "5c95242c87d9d636e70c28f2", + FrequencyInterval: 1, + FrequencyType: "hourly", + RetentionUnit: "days", + RetentionValue: 0, + }, + }, + } + + if diff := deep.Equal(backupCompliancePolicy, expected); diff != nil { + t.Error(diff) + } +} + +func TestBackupCompliancePolicy_Update(t *testing.T) { + client, mux, teardown := setup() + defer teardown() + + groupID := "5b6212af90dc76637950a2c6" + + path := fmt.Sprintf("/api/atlas/v1.0/groups/%s/backupCompliancePolicy", groupID) + + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + expected := map[string]interface{}{ + "authorizedEmail": "user@example.com", + "copyProtectionEnabled": false, + "encryptionAtRestEnabled": false, + "updatedDate": "2019-08-24T14:15:22Z", + "updatedUser": "user@example.com", + "pitEnabled": false, + "projectId": "32b6e34b3d91647abb20e7b8", + "restoreWindowDays": float64(7), + "state": "ACTIVE", + "onDemandPolicyItem": map[string]interface{}{ + "id": "32b6e34b3d91647abb20e7b9", + "frequencyType": "daily", + "frequencyInterval": float64(1), + "retentionValue": float64(7), + "retentionUnit": "days", + }, + "scheduledPolicyItems": []interface{}{ + map[string]interface{}{ + "id": "5c95242c87d9d636e70c28f0", + "frequencyType": "hourly", + "frequencyInterval": float64(6), + "retentionValue": float64(2), + "retentionUnit": "days", + }, + map[string]interface{}{ + "id": "5c95242c87d9d636e70c28f2", + "frequencyType": "weekly", + "frequencyInterval": float64(1), + "retentionValue": float64(3), + "retentionUnit": "weeks", + }, + }, + } + + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("Decode json: %v", err) + } + + if diff := deep.Equal(v, expected); diff != nil { + t.Error(diff) + } + + fmt.Fprint(w, `{ + "authorizedEmail": "user@example.com", + "copyProtectionEnabled": false, + "encryptionAtRestEnabled": false, + "onDemandPolicyItem": + { + "id": "32b6e34b3d91647abb20e7b9", + "frequencyInterval": 1, + "frequencyType": "daily", + "retentionUnit": "days", + "retentionValue": 7 + }, + "pitEnabled": false, + "projectId": "32b6e34b3d91647abb20e7b8", + "restoreWindowDays": 7, + "scheduledPolicyItems": + [ + { + "id": "5c95242c87d9d636e70c28f0", + "frequencyInterval": 6, + "frequencyType": "hourly", + "retentionUnit": "days", + "retentionValue": 2 + }, + { + "id": "5c95242c87d9d636e70c28f2", + "frequencyInterval": 1, + "frequencyType": "weekly", + "retentionUnit": "weeks", + "retentionValue": 3 + } + ], + "state": "ACTIVE", + "updatedDate": "2019-08-24T14:15:22Z", + "updatedUser": "user@example.com" + }`) + }) + + updateRequest := &BackupCompliancePolicy{ + AuthorizedEmail: "user@example.com", + CopyProtectionEnabled: pointer(false), + EncryptionAtRestEnabled: pointer(false), + ProjectID: "32b6e34b3d91647abb20e7b8", + RestoreWindowDays: pointer(int64(7)), + State: "ACTIVE", + UpdatedDate: "2019-08-24T14:15:22Z", + UpdatedUser: "user@example.com", + PitEnabled: pointer(false), + OnDemandPolicyItem: PolicyItem{ + ID: "32b6e34b3d91647abb20e7b9", + FrequencyInterval: 1, + FrequencyType: "daily", + RetentionUnit: "days", + RetentionValue: 7, + }, + ScheduledPolicyItems: []ScheduledPolicyItem{ + { + ID: "5c95242c87d9d636e70c28f0", + FrequencyInterval: 6, + FrequencyType: "hourly", + RetentionUnit: "days", + RetentionValue: 2, + }, + { + ID: "5c95242c87d9d636e70c28f2", + FrequencyInterval: 1, + FrequencyType: "weekly", + RetentionUnit: "weeks", + RetentionValue: 3, + }, + }, + } + + backupCompliancePolicy, _, err := client.BackupCompliancePolicy.Update(ctx, groupID, updateRequest) + if err != nil { + t.Fatalf("BackupCompliancePolicy.Update returned error: %v", err) + } + + expected := &BackupCompliancePolicy{ + AuthorizedEmail: "user@example.com", + CopyProtectionEnabled: pointer(false), + EncryptionAtRestEnabled: pointer(false), + ProjectID: "32b6e34b3d91647abb20e7b8", + RestoreWindowDays: pointer(int64(7)), + State: "ACTIVE", + UpdatedDate: "2019-08-24T14:15:22Z", + UpdatedUser: "user@example.com", + PitEnabled: pointer(false), + OnDemandPolicyItem: PolicyItem{ + ID: "32b6e34b3d91647abb20e7b9", + FrequencyInterval: 1, + FrequencyType: "daily", + RetentionUnit: "days", + RetentionValue: 7, + }, + ScheduledPolicyItems: []ScheduledPolicyItem{ + { + ID: "5c95242c87d9d636e70c28f0", + FrequencyInterval: 6, + FrequencyType: "hourly", + RetentionUnit: "days", + RetentionValue: 2, + }, + { + ID: "5c95242c87d9d636e70c28f2", + FrequencyInterval: 1, + FrequencyType: "weekly", + RetentionUnit: "weeks", + RetentionValue: 3, + }, + }, + } + + if diff := deep.Equal(backupCompliancePolicy, expected); diff != nil { + t.Error(diff) + } +} diff --git a/mongodbatlas/mongodbatlas.go b/mongodbatlas/mongodbatlas.go index 79f1a32cc..61bbb6697 100644 --- a/mongodbatlas/mongodbatlas.go +++ b/mongodbatlas/mongodbatlas.go @@ -125,6 +125,7 @@ type Client struct { Checkpoints CheckpointsService Alerts AlertsService CloudProviderSnapshotBackupPolicies CloudProviderSnapshotBackupPoliciesService + BackupCompliancePolicy BackupCompliancePolicyService Events EventsService Processes ProcessesService ProcessMeasurements ProcessMeasurementsService @@ -274,6 +275,7 @@ func NewClient(httpClient *http.Client) *Client { c.Checkpoints = &CheckpointsServiceOp{Client: c} c.Alerts = &AlertsServiceOp{Client: c} c.CloudProviderSnapshotBackupPolicies = &CloudProviderSnapshotBackupPoliciesServiceOp{Client: c} + c.BackupCompliancePolicy = &BackupCompliancePolicyServiceOp{Client: c} c.Events = &EventsServiceOp{Client: c} c.Processes = &ProcessesServiceOp{Client: c} c.ProcessMeasurements = &ProcessMeasurementsServiceOp{Client: c}