Repository: GoogleCloudPlatform/golang-samples

Issue:
storage: samples for Soft Delete (objects)
Please add samples for object soft delete:

- storage_disable_soft_delete
- storage_get_soft_delete_policy
- storage_set_soft_delete_policy
- storage_list_soft_deleted_objects
- storage_list_soft_deleted_object_versions
- storage_restore_object

Reference Java Implementation: https://github.com/googleapis/java-storage/pull/2754

Discussion:

Solution patch:
diff --git a/storage/buckets/disable_soft_delete_policy.go b/storage/buckets/disable_soft_delete_policy.go
new file mode 100644
index 00000000..facb190b
--- /dev/null
+++ b/storage/buckets/disable_soft_delete_policy.go
@@ -0,0 +1,54 @@
+// Copyright 2025 Google LLC
+//
+// 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
+//
+//     https://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 buckets
+
+// [START storage_disable_soft_delete_policy]
+import (
+	"context"
+	"fmt"
+	"io"
+	"time"
+
+	"cloud.google.com/go/storage"
+)
+
+// disableSoftDeletePolicy disables the soft delete policy for a bucket by setting retention duration to 0.
+func disableSoftDeletePolicy(w io.Writer, bucketName string) error {
+	// bucketName := "bucket-name"
+	ctx := context.Background()
+	client, err := storage.NewClient(ctx)
+	if err != nil {
+		return fmt.Errorf("storage.NewClient: %w", err)
+	}
+	defer client.Close()
+
+	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
+	defer cancel()
+
+	bucket := client.Bucket(bucketName)
+
+	// Update the bucket with zero retention duration to disable soft delete.
+	_, err = bucket.Update(ctx, storage.BucketAttrsToUpdate{
+		SoftDeletePolicy: &storage.SoftDeletePolicy{},
+	})
+	if err != nil {
+		return fmt.Errorf("bucket.Update: %w", err)
+	}
+
+	fmt.Fprintf(w, "Soft delete policy for bucket %s was disabled.\n", bucketName)
+	return nil
+}
+
+// [END storage_disable_soft_delete_policy]
diff --git a/storage/buckets/get_soft_delete_policy.go b/storage/buckets/get_soft_delete_policy.go
new file mode 100644
index 00000000..d8fb07e6
--- /dev/null
+++ b/storage/buckets/get_soft_delete_policy.go
@@ -0,0 +1,52 @@
+// Copyright 2025 Google LLC
+//
+// 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
+//
+//     https://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 buckets
+
+// [START storage_get_soft_delete_policy]
+import (
+	"context"
+	"fmt"
+	"io"
+	"time"
+
+	"cloud.google.com/go/storage"
+)
+
+// getSoftDeletePolicy retrieves and outputs the soft delete policy for a bucket.
+func getSoftDeletePolicy(w io.Writer, bucketName string) error {
+	// bucketName := "bucket-name"
+	ctx := context.Background()
+	client, err := storage.NewClient(ctx)
+	if err != nil {
+		return fmt.Errorf("storage.NewClient: %w", err)
+	}
+	defer client.Close()
+
+	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
+	defer cancel()
+
+	bucket := client.Bucket(bucketName)
+
+	// Get the current bucket attributes.
+	attrs, err := bucket.Attrs(ctx)
+	if err != nil {
+		return fmt.Errorf("bucket.Attrs: %w", err)
+	}
+
+	fmt.Fprintf(w, "Soft delete policy for bucket %s is:\n %+v\n", bucketName, attrs.SoftDeletePolicy)
+	return nil
+}
+
+// [END storage_get_soft_delete_policy]
diff --git a/storage/buckets/set_soft_delete_policy.go b/storage/buckets/set_soft_delete_policy.go
new file mode 100644
index 00000000..867f3b40
--- /dev/null
+++ b/storage/buckets/set_soft_delete_policy.go
@@ -0,0 +1,56 @@
+// Copyright 2025 Google LLC
+//
+// 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
+//
+//     https://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 buckets
+
+// [START storage_set_soft_delete_policy]
+import (
+	"context"
+	"fmt"
+	"io"
+	"time"
+
+	"cloud.google.com/go/storage"
+)
+
+// setSoftDeletePolicy sets a bucket's soft delete policy with a 10-day retention period.
+func setSoftDeletePolicy(w io.Writer, bucketName string) error {
+	// bucketName := "bucket-name"
+	ctx := context.Background()
+	client, err := storage.NewClient(ctx)
+	if err != nil {
+		return fmt.Errorf("storage.NewClient: %w", err)
+	}
+	defer client.Close()
+
+	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
+	defer cancel()
+
+	bucket := client.Bucket(bucketName)
+
+	// Set soft delete policy with 10-day retention period.
+	_, err = bucket.Update(ctx, storage.BucketAttrsToUpdate{
+		SoftDeletePolicy: &storage.SoftDeletePolicy{
+			RetentionDuration: 10 * 24 * time.Hour, // 10 days in hours
+		},
+	})
+	if err != nil {
+		return fmt.Errorf("bucket.Update: %w", err)
+	}
+
+	fmt.Fprintf(w, "Soft delete policy for %s was set to a 10-day retention period\n", bucketName)
+	return nil
+}
+
+// [END storage_set_soft_delete_policy]



Test patch:
diff --git a/storage/buckets/buckets_test.go b/storage/buckets/buckets_test.go
index 18d18cd8..6ff6521a 100644
--- a/storage/buckets/buckets_test.go
+++ b/storage/buckets/buckets_test.go
@@ -783,3 +783,81 @@ func TestCreateBucketObjectRetention(t *testing.T) {
 		t.Errorf("got %q, want %q", got, want)
 	}
 }
+
+func TestSetSoftDeletePolicy(t *testing.T) {
+	tc := testutil.SystemTest(t)
+	ctx := context.Background()
+
+	client, err := storage.NewClient(ctx)
+	if err != nil {
+		t.Fatalf("storage.NewClient: %v", err)
+	}
+	defer client.Close()
+
+	bucketName := testutil.CreateTestBucket(ctx, t, client, tc.ProjectID, testPrefix)
+	defer testutil.DeleteBucketIfExists(ctx, client, bucketName)
+
+	var buf = bytes.Buffer{}
+	if err := setSoftDeletePolicy(&buf, bucketName); err != nil {
+		t.Fatalf("setSoftDeletePolicy: %v", err)
+	}
+
+	// Verify the output was printed as expected.
+	gotOutput := buf.String()
+	wantOutput := fmt.Sprintf("Soft delete policy for %s was set to a 10-day retention period\n", bucketName)
+	if gotOutput != wantOutput {
+		t.Errorf("Output mismatch: got %q, want %q", gotOutput, wantOutput)
+	}
+}
+
+func TestGetSoftDeletePolicy(t *testing.T) {
+	tc := testutil.SystemTest(t)
+	ctx := context.Background()
+
+	client, err := storage.NewClient(ctx)
+	if err != nil {
+		t.Fatalf("storage.NewClient: %v", err)
+	}
+	defer client.Close()
+
+	bucketName := testutil.CreateTestBucket(ctx, t, client, tc.ProjectID, testPrefix)
+	defer testutil.DeleteBucketIfExists(ctx, client, bucketName)
+
+	var buf = bytes.Buffer{}
+	if err := getSoftDeletePolicy(&buf, bucketName); err != nil {
+		t.Fatalf("getSoftDeletePolicy: %v", err)
+	}
+
+	// Verify the output was printed as expected.
+	got := buf.String()
+	want := fmt.Sprintf("Soft delete policy for bucket %s is:\n", bucketName)
+	if !strings.HasPrefix(got, want) {
+		t.Errorf("Output mismatch: got %q, want %q", got, want)
+	}
+}
+
+func TestDisableSoftDeletePolicy(t *testing.T) {
+	tc := testutil.SystemTest(t)
+	ctx := context.Background()
+
+	client, err := storage.NewClient(ctx)
+	if err != nil {
+		t.Fatalf("storage.NewClient: %v", err)
+	}
+	defer client.Close()
+
+	bucketName := testutil.CreateTestBucket(ctx, t, client, tc.ProjectID, testPrefix)
+	defer testutil.DeleteBucketIfExists(ctx, client, bucketName)
+
+	var buf = bytes.Buffer{}
+	if err := disableSoftDeletePolicy(&buf, bucketName); err != nil {
+		t.Fatalf("disableSoftDeletePolicy: %v", err)
+	}
+
+	// Verify the output was printed as expected.
+	got := buf.String()
+	want := fmt.Sprintf("Soft delete policy for bucket %s was disabled.\n", bucketName)
+	if !strings.HasPrefix(got, want) {
+		t.Errorf("Output mismatch: got %q, want %q", got, want)
+	}
+}