Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
761cac9
add samples and test cases for bucket soft delete policy
mahendra-google Apr 30, 2025
4920b0b
sample and tests changes
mahendra-google May 6, 2025
6339875
Tests changes
mahendra-google May 6, 2025
554be98
xml docs added for new helper methods
mahendra-google May 7, 2025
5e1d4b6
license header minor changes
mahendra-google May 7, 2025
7fc30d5
comments added
mahendra-google May 7, 2025
2cc3446
rewording of console output print messages
mahendra-google May 8, 2025
c4f2702
minor changes
mahendra-google May 8, 2025
ebcf624
removal of unused variable and rewording
mahendra-google May 8, 2025
71d7948
removal of unused code
mahendra-google May 8, 2025
d30994c
changes in parameter name description in bucketdisable soft delete po…
mahendra-google May 9, 2025
b0a7f9e
Code review changes
mahendra-google Jun 5, 2025
417fa41
Merge branch 'main' into bucket_soft_delete_policy
mahendra-google Jun 5, 2025
5499151
Update storage/api/Storage.Samples.Tests/BucketGetSoftDeletePolicyTes…
mahendra-google Jun 6, 2025
b0e0b69
Update storage/api/Storage.Samples/BucketGetSoftDeletePolicy.cs
mahendra-google Jun 6, 2025
09fe7d9
BucketGetSoftDeletePolicy sample and test changes
mahendra-google Jun 6, 2025
3773bac
BucketDisableSoftDeletePolicy smaple and test changes
mahendra-google Jun 6, 2025
2aa53a6
Update storage/api/Storage.Samples/BucketSetSoftDeletePolicy.cs
mahendra-google Jun 11, 2025
12fc516
Update storage/api/Storage.Samples/BucketDisableSoftDeletePolicy.cs
mahendra-google Jun 11, 2025
12067f3
disable soft delete policy test changes aligning with canonical samp…
mahendra-google Jun 24, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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
//
// 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.

using Google;
using System.Net;
using Xunit;

[Collection(nameof(StorageFixture))]
public class BucketDisableSoftDeletePolicyTest
{
private readonly StorageFixture _fixture;

public BucketDisableSoftDeletePolicyTest(StorageFixture fixture)
{
_fixture = fixture;
}

[Fact]
public void TestBucketDisableSoftDeletePolicy()
{
BucketDisableSoftDeletePolicySample disableSample = new BucketDisableSoftDeletePolicySample();
var bucketName = _fixture.GenerateBucketName();
var bucketWithDefaultSoftDeletePolicy = _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);

// Initializing zero with a value 0 indicates the retention duration for the bucket.
long zero = 0;
Assert.NotEqual(bucketWithDefaultSoftDeletePolicy.SoftDeletePolicy.RetentionDurationSeconds, zero);
// Disable soft-delete policy for the bucket.
var bucketPostDisableSoftDeletePolicy = disableSample.BucketDisableSoftDeletePolicy(bucketName);
Assert.Equal(bucketPostDisableSoftDeletePolicy.SoftDeletePolicy.RetentionDurationSeconds, zero);
Comment on lines +36 to +41
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The zero variable and its preceding comment are unnecessary. You can use the literal 0L directly in the assertions for better readability and conciseness. Additionally, it's a common convention in xUnit to have the expected value as the first argument in Assert.Equal and Assert.NotEqual.

        Assert.NotEqual(0L, bucketWithDefaultSoftDeletePolicy.SoftDeletePolicy.RetentionDurationSeconds);
        // Disable soft-delete policy for the bucket.
        var bucketPostDisableSoftDeletePolicy = disableSample.BucketDisableSoftDeletePolicy(bucketName);
        Assert.Equal(0L, bucketPostDisableSoftDeletePolicy.SoftDeletePolicy.RetentionDurationSeconds);

// After disabling soft-delete policy for the bucket, EffectiveTimeRaw property will be null.
Assert.Null(bucketPostDisableSoftDeletePolicy.SoftDeletePolicy.EffectiveTimeRaw);
}
}
39 changes: 39 additions & 0 deletions storage/api/Storage.Samples.Tests/BucketGetSoftDeletePolicyTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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
//
// 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.

using Newtonsoft.Json;
using Xunit;

[Collection(nameof(StorageFixture))]
public class BucketGetSoftDeletePolicyTest
{
private readonly StorageFixture _fixture;

public BucketGetSoftDeletePolicyTest(StorageFixture fixture)
{
_fixture = fixture;
}

[Fact]
public void TestBucketGetSoftDeletePolicy()
{
BucketGetSoftDeletePolicySample getSample = new BucketGetSoftDeletePolicySample();
var bucketName = _fixture.GenerateBucketName();
var bucket = _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);
var softPolicyData = getSample.BucketGetSoftDeletePolicy(bucketName);
var bucketSoftDeletePolicy = JsonConvert.SerializeObject(bucket.SoftDeletePolicy);
var softDeletePolicyData = JsonConvert.SerializeObject(softPolicyData);
Assert.Equal(bucketSoftDeletePolicy, softDeletePolicyData);
Comment on lines +35 to +37
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Comparing objects by serializing them to JSON can make tests brittle. For example, a change in property order in the JSON output could break the test, even if the objects are semantically equivalent. It's better to assert the properties of the objects directly.

        Assert.Equal(bucket.SoftDeletePolicy.RetentionDurationSeconds, softPolicyData.RetentionDurationSeconds);
        Assert.Equal(bucket.SoftDeletePolicy.EffectiveTime, softPolicyData.EffectiveTime);

}
}
47 changes: 47 additions & 0 deletions storage/api/Storage.Samples.Tests/BucketSetSoftDeletePolicyTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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
//
// 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.

using System;
using Xunit;

[Collection(nameof(StorageFixture))]
public class BucketSetSoftDeletePolicyTest
{
private readonly StorageFixture _fixture;

public BucketSetSoftDeletePolicyTest(StorageFixture fixture)
{
_fixture = fixture;
}

[Fact]
public void TestBucketSetSoftDeletePolicy()
{
BucketSetSoftDeletePolicySample setSample = new BucketSetSoftDeletePolicySample();
var bucketName = _fixture.GenerateBucketName();
var bucketWithDefaultSoftDeletePolicy = _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);

int retentionDurationInDays = 10;
long retentionDurationInSeconds = (long) TimeSpan.FromDays(retentionDurationInDays).TotalSeconds;

Assert.NotEqual(bucketWithDefaultSoftDeletePolicy.SoftDeletePolicy.RetentionDurationSeconds, retentionDurationInSeconds);

// Set soft-delete policy for the bucket with a retention duration of 10 days.
var bucketPostSetSoftDeletePolicy = setSample.BucketSetSoftDeletePolicy(bucketName, retentionDurationInDays);

Assert.Equal(bucketPostSetSoftDeletePolicy.SoftDeletePolicy.RetentionDurationSeconds, retentionDurationInSeconds);
// After setting soft-delete policy for the bucket, EffectiveTimeRaw property will be not null.
Assert.NotNull(bucketPostSetSoftDeletePolicy.SoftDeletePolicy.EffectiveTimeRaw);
}
}
9 changes: 7 additions & 2 deletions storage/api/Storage.Samples.Tests/StorageFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ public StorageFixture()

public void Dispose()
{
DeleteBucketSample deleteBucketSample = new DeleteBucketSample();
DeleteFileSample deleteFileSample = new DeleteFileSample();
DeleteFileArchivedGenerationSample deleteFileArchivedGenerationSample = new DeleteFileArchivedGenerationSample();
foreach (var bucket in TempBucketFiles)
Expand Down Expand Up @@ -132,7 +131,7 @@ public void Dispose()
{
try
{
deleteBucketSample.DeleteBucket(bucketName);
Client.DeleteBucket(bucketName, new DeleteBucketOptions { DeleteObjects = true });
SleepAfterBucketCreateUpdateDelete();
}
catch (Exception)
Expand Down Expand Up @@ -258,6 +257,12 @@ internal Bucket CreateBucket(string name, bool multiVersion, bool softDelete = f
/// <returns>The objectContent.</returns>
internal string GenerateContent() => Guid.NewGuid().ToString();

/// <summary>
/// Generates a new globally unique identifier (GUID).
/// </summary>
/// <returns>A new randomly generated GUID as string.</returns>
internal string GenerateGuid() => Guid.NewGuid().ToString();
Comment on lines +260 to +264
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The new method GenerateGuid is not used in the codebase. It also duplicates the functionality of existing methods like GenerateBucketName, GenerateName, and GenerateContent. To avoid dead code and duplication, this method should be removed. Alternatively, if the goal is to consolidate on a better name, the other methods should be updated to call this one and then be deprecated or removed.


/// <summary>
/// Bucket creation/update/deletion is rate-limited. To avoid making the tests flaky, we sleep after each operation.
/// </summary>
Expand Down
38 changes: 38 additions & 0 deletions storage/api/Storage.Samples/BucketDisableSoftDeletePolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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
//
// 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.

// [START storage_disable_soft_delete]

using Google.Apis.Storage.v1.Data;
using Google.Cloud.Storage.V1;
using System;

public class BucketDisableSoftDeletePolicySample
{
/// <summary>
/// Disable soft delete policy for the bucket.
/// </summary>
/// <param name="bucketName">The name of the bucket.</param>
public Bucket BucketDisableSoftDeletePolicy(string bucketName = "your-unique-bucket-name")
{
var storage = StorageClient.Create();
var bucket = storage.GetBucket(bucketName);
// To disable soft-delete policy for the bucket, set the soft delete retention duration to 0 seconds.
bucket.SoftDeletePolicy = new Bucket.SoftDeletePolicyData { RetentionDurationSeconds = 0L };
bucket = storage.UpdateBucket(bucket);
Console.WriteLine($"The Soft Delete Policy for the Bucket (Bucket Name: {bucketName}) is disabled");
return bucket;
Comment on lines +29 to +35
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation uses GetBucket followed by UpdateBucket. UpdateBucket performs a PUT operation, which can overwrite other bucket properties if they were changed between the GET and PUT calls. It's also less efficient due to the extra API call.

A better approach is to use PatchBucket, which performs a PATCH operation. This updates only the specified properties and avoids the need for an initial GetBucket call, making the code more efficient and safer against race conditions.

        var storage = StorageClient.Create();
        var bucket = new Bucket { Name = bucketName, SoftDeletePolicy = new Bucket.SoftDeletePolicyData { RetentionDurationSeconds = 0L } };
        bucket = storage.PatchBucket(bucket);
        Console.WriteLine($"The Soft Delete Policy for the Bucket (Bucket Name: {bucketName}) is disabled");
        return bucket;

}
}
// [END storage_disable_soft_delete]
43 changes: 43 additions & 0 deletions storage/api/Storage.Samples/BucketGetSoftDeletePolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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
//
// 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.

// [START storage_get_soft_delete_policy]

using Google.Apis.Storage.v1.Data;
using Google.Cloud.Storage.V1;
using System;

public class BucketGetSoftDeletePolicySample
{
/// <summary>
/// Get soft delete policy of the bucket.
/// </summary>
/// <param name="bucketName">The name of the bucket.</param>
public Bucket.SoftDeletePolicyData BucketGetSoftDeletePolicy(string bucketName = "your-unique-bucket-name")
{
var storage = StorageClient.Create();
var bucket = storage.GetBucket(bucketName);
if (bucket.SoftDeletePolicy.RetentionDurationSeconds == 0)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The bucket.SoftDeletePolicy property can be null if no soft delete policy has ever been set on the bucket. Accessing RetentionDurationSeconds on a null SoftDeletePolicy will cause a NullReferenceException. You should add a null check for SoftDeletePolicy.

        if (bucket.SoftDeletePolicy is null || bucket.SoftDeletePolicy.RetentionDurationSeconds == 0)

{
Console.WriteLine($"The Soft Delete Policy for the Bucket (Bucket Name: {bucketName}) is disabled");
}
else
{
int retentionDuration = (int) TimeSpan.FromSeconds((double) bucket.SoftDeletePolicy.RetentionDurationSeconds).TotalDays;
Console.WriteLine($"The Soft Delete Policy for the Bucket (Bucket Name: {bucketName}) is : {retentionDuration} days");
}
return bucket.SoftDeletePolicy;
}
}
// [END storage_get_soft_delete_policy]
47 changes: 47 additions & 0 deletions storage/api/Storage.Samples/BucketSetSoftDeletePolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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
//
// 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.

// [START storage_set_soft_delete_policy]

using Google.Apis.Storage.v1.Data;
using Google.Cloud.Storage.V1;
using System;

public class BucketSetSoftDeletePolicySample
{
/// <summary>
/// Set soft delete policy for the bucket.
/// </summary>
/// <param name="bucketName">The name of the bucket.</param>
/// <param name="retentionDurationInDays">The retention duration in days to set soft-delete policy for the bucket.</param>
public Bucket BucketSetSoftDeletePolicy(string bucketName = "your-unique-bucket-name",
int retentionDurationInDays = 10)
{
var storage = StorageClient.Create();
var bucket = storage.GetBucket(bucketName);
long retentionDurationInSeconds = (long) TimeSpan.FromDays(retentionDurationInDays).TotalSeconds;
if (retentionDurationInDays < 7 || retentionDurationInDays > 90)
{
// The maximum retention duration you can set is 90 days and the minimum retention duration you can set is 7 days.
// For more information, please refer to https://cloud.google.com/storage/docs/soft-delete#retention-duration
Console.WriteLine($"The Soft Delete Policy for the Bucket (Bucket Name: {bucketName}) must have a retention duration between 7 days and 90 days");
return bucket;
}
bucket.SoftDeletePolicy = new Bucket.SoftDeletePolicyData { RetentionDurationSeconds = retentionDurationInSeconds };
bucket = storage.UpdateBucket(bucket);
Console.WriteLine($"The Soft Delete Policy for the Bucket (Bucket Name: {bucketName}) is set to the {retentionDurationInDays} days retention duration");
return bucket;
Comment on lines +31 to +44
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This method uses GetBucket followed by UpdateBucket. UpdateBucket performs a PUT operation, which can overwrite other bucket properties if they were changed between the GET and PUT calls. It's also less efficient due to the extra API call. A better approach is to use PatchBucket, which performs a PATCH operation to update only the specified properties.

Additionally, for invalid input, returning the unmodified bucket object is inefficient as it requires a Get call. It's more idiomatic and clearer to throw an ArgumentOutOfRangeException to signal a programming error to the caller.

        if (retentionDurationInDays < 7 || retentionDurationInDays > 90)
        {
            // The maximum retention duration you can set is 90 days and the minimum retention duration you can set is 7 days.
            // For more information, please refer to https://cloud.google.com/storage/docs/soft-delete#retention-duration
            Console.WriteLine($"The Soft Delete Policy for the Bucket (Bucket Name: {bucketName}) must have a retention duration between 7 days and 90 days");
            throw new ArgumentOutOfRangeException(nameof(retentionDurationInDays), "Retention duration must be between 7 and 90 days.");
        }
        var storage = StorageClient.Create();
        long retentionDurationInSeconds = (long) TimeSpan.FromDays(retentionDurationInDays).TotalSeconds;
        var bucket = new Bucket { Name = bucketName, SoftDeletePolicy = new Bucket.SoftDeletePolicyData { RetentionDurationSeconds = retentionDurationInSeconds } };
        bucket = storage.PatchBucket(bucket);
        Console.WriteLine($"The Soft Delete Policy for the Bucket (Bucket Name: {bucketName}) is set to the {retentionDurationInDays} days retention duration");
        return bucket;

}
}
// [END storage_set_soft_delete_policy]