Skip to content

Commit 6c5c651

Browse files
samples(storage): add samples and test cases for storage to support bucket restore feature (#2963)
1 parent 2b75f32 commit 6c5c651

File tree

8 files changed

+282
-2
lines changed

8 files changed

+282
-2
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License").
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using Google.Cloud.Storage.V1;
16+
using System.Threading.Tasks;
17+
using Xunit;
18+
19+
[Collection(nameof(StorageFixture))]
20+
public class GetSoftDeleteBucketTest
21+
{
22+
private readonly StorageFixture _fixture;
23+
24+
public GetSoftDeleteBucketTest(StorageFixture fixture)
25+
{
26+
_fixture = fixture;
27+
}
28+
29+
[Fact]
30+
public async Task GetSoftDeleteBucket()
31+
{
32+
GetSoftDeletedBucketSample getSoftDeletedBucketSample = new GetSoftDeletedBucketSample();
33+
var bucketName = _fixture.GenerateBucketName();
34+
var softDeleteBucket = _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);
35+
await _fixture.Client.DeleteBucketAsync(softDeleteBucket.Name);
36+
37+
var softDeleted = getSoftDeletedBucketSample.GetSoftDeletedBucket(softDeleteBucket.Name, softDeleteBucket.Generation);
38+
Assert.Equal(softDeleteBucket.Name, softDeleted.Name);
39+
Assert.Equal(softDeleteBucket.Generation, softDeleted.Generation);
40+
Assert.NotNull(softDeleted.SoftDeleteTimeDateTimeOffset);
41+
Assert.NotNull(softDeleted.HardDeleteTimeDateTimeOffset);
42+
}
43+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License").
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using Google.Apis.Storage.v1.Data;
16+
using System.Threading.Tasks;
17+
using Xunit;
18+
19+
[Collection(nameof(StorageFixture))]
20+
public class ListSoftDeleteBucketsTest
21+
{
22+
private readonly StorageFixture _fixture;
23+
24+
public ListSoftDeleteBucketsTest(StorageFixture fixture)
25+
{
26+
_fixture = fixture;
27+
}
28+
29+
[Fact]
30+
public async Task ListSoftDeleteBuckets()
31+
{
32+
ListSoftDeletedBucketsSample listSoftDeletedBucketsSample = new ListSoftDeletedBucketsSample();
33+
var bucketName = _fixture.GenerateBucketName();
34+
var softDeleteBucket = _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);
35+
await _fixture.Client.DeleteBucketAsync(softDeleteBucket.Name);
36+
37+
var actualBuckets = listSoftDeletedBucketsSample.ListSoftDeletedBuckets(_fixture.ProjectId);
38+
// Check the list contains the bucket we just soft-deleted.
39+
Assert.Contains(actualBuckets, bucket => bucket.Name == softDeleteBucket.Name && bucket.Generation == softDeleteBucket.Generation);
40+
// Check all the buckets in the list are soft-deleted buckets.
41+
Assert.All(actualBuckets, AssertSoftDeletedBucket);
42+
}
43+
44+
// Validates that the given bucket is soft-deleted.
45+
private void AssertSoftDeletedBucket(Bucket b)
46+
{
47+
Assert.NotNull(b.Generation);
48+
Assert.NotNull(b.HardDeleteTimeDateTimeOffset);
49+
Assert.NotNull(b.SoftDeleteTimeDateTimeOffset);
50+
}
51+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License").
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using Google.Cloud.Storage.V1;
16+
using System.Threading.Tasks;
17+
using Xunit;
18+
19+
[Collection(nameof(StorageFixture))]
20+
public class RestoreSoftDeleteBucketTest
21+
{
22+
private readonly StorageFixture _fixture;
23+
24+
public RestoreSoftDeleteBucketTest(StorageFixture fixture)
25+
{
26+
_fixture = fixture;
27+
}
28+
29+
[Fact]
30+
public async Task RestoreSoftDeleteBucket()
31+
{
32+
RestoreSoftDeletedBucketSample restoreSoftDeletedBucketSample = new RestoreSoftDeletedBucketSample();
33+
var bucketName = _fixture.GenerateBucketName();
34+
var softDeleteBucket = _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);
35+
await _fixture.Client.DeleteBucketAsync(softDeleteBucket.Name);
36+
37+
var restoredBucket = restoreSoftDeletedBucketSample.RestoreSoftDeletedBucket(softDeleteBucket.Name, softDeleteBucket.Generation.Value);
38+
Assert.Equal(softDeleteBucket.Name, restoredBucket.Name);
39+
Assert.Equal(softDeleteBucket.Generation, restoredBucket.Generation);
40+
}
41+
}

storage/api/Storage.Samples.Tests/StorageFixture.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2020 Google Inc.
1+
// Copyright 2020 Google Inc.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -47,6 +47,7 @@ public class StorageFixture : IDisposable, ICollectionFixture<StorageFixture>
4747
public string KmsKeyLocation { get; } = "us-west1";
4848
public string ServiceAccountEmail { get; } = "gcs-iam-acl-test@dotnet-docs-samples-tests.iam.gserviceaccount.com";
4949
public List<TopicName> TempTopicNames { get; } = new List<TopicName>();
50+
public StorageClient Client { get; }
5051

5152
public RetryRobot HmacChangesPropagated { get; } = new RetryRobot
5253
{
@@ -64,6 +65,7 @@ public StorageFixture()
6465
{
6566
throw new Exception("You need to set the Environment variable 'GOOGLE_PROJECT_ID' with your Google Cloud Project's project id.");
6667
}
68+
Client = StorageClient.Create();
6769
// create simple bucket
6870
CreateBucket(BucketNameGeneric);
6971

@@ -217,6 +219,26 @@ public void CreateBucket(string bucketName, string location = null, AutoclassDat
217219
TempBucketNames.Add(bucketName);
218220
}
219221

222+
internal Bucket CreateBucket(string name, bool multiVersion, bool softDelete = false, bool registerForDeletion = true)
223+
{
224+
var bucket = Client.CreateBucket(ProjectId,
225+
new Bucket
226+
{
227+
Name = name,
228+
Versioning = new Bucket.VersioningData { Enabled = multiVersion },
229+
// The minimum allowed for soft delete is 7 days.
230+
SoftDeletePolicy = softDelete ? new Bucket.SoftDeletePolicyData { RetentionDurationSeconds = (int) TimeSpan.FromDays(7).TotalSeconds } : null,
231+
});
232+
SleepAfterBucketCreateUpdateDelete();
233+
if (registerForDeletion)
234+
{
235+
TempBucketNames.Add(name);
236+
}
237+
return bucket;
238+
}
239+
240+
internal string GenerateBucketName() => Guid.NewGuid().ToString();
241+
220242
/// <summary>
221243
/// Bucket creation/update/deletion is rate-limited. To avoid making the tests flaky, we sleep after each operation.
222244
/// </summary>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2025 Google Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// [START storage_get_soft_deleted_bucket]
16+
17+
using Google.Apis.Storage.v1.Data;
18+
using Google.Cloud.Storage.V1;
19+
using System;
20+
21+
public class GetSoftDeletedBucketSample
22+
{
23+
/// <summary>
24+
/// Get a soft deleted bucket.
25+
/// </summary>
26+
/// <param name="bucketName">The name of the bucket.</param>
27+
/// <param name="generation">The generation of the bucket.</param>
28+
public Bucket GetSoftDeletedBucket(
29+
string bucketName = "your-unique-bucket-name",
30+
long? generation = 123456789)
31+
{
32+
var client = StorageClient.Create();
33+
var bucket = client.GetBucket(bucketName, new GetBucketOptions
34+
{
35+
SoftDeleted = true,
36+
Generation = generation
37+
});
38+
Console.WriteLine($"Bucket:\t{bucket.Name}");
39+
Console.WriteLine($"Bucket Generation:\t{bucket.Generation}");
40+
Console.WriteLine($"Bucket SoftDelete Time:\t{bucket.SoftDeleteTimeDateTimeOffset}");
41+
Console.WriteLine($"Bucket HardDelete Time:\t{bucket.HardDeleteTimeDateTimeOffset}");
42+
return bucket;
43+
}
44+
}
45+
// [END storage_get_soft_deleted_bucket]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2025 Google Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// [START storage_list_soft_deleted_buckets]
16+
17+
using Google.Apis.Storage.v1.Data;
18+
using Google.Cloud.Storage.V1;
19+
using System;
20+
using System.Collections.Generic;
21+
22+
public class ListSoftDeletedBucketsSample
23+
{
24+
/// <summary>
25+
/// List soft deleted buckets.
26+
/// </summary>
27+
/// <param name="projectId">The ID of the project to list soft deleted buckets.</param>
28+
public IEnumerable<Bucket> ListSoftDeletedBuckets(string projectId = "your-project-id")
29+
{
30+
var storage = StorageClient.Create();
31+
var buckets = storage.ListBuckets(projectId, new ListBucketsOptions { SoftDeletedOnly = true });
32+
Console.WriteLine("Soft Deleted Buckets are as follows:");
33+
foreach (var bucket in buckets)
34+
{
35+
Console.WriteLine(bucket.Name);
36+
}
37+
return buckets;
38+
}
39+
}
40+
// [END storage_list_soft_deleted_buckets]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License").
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// [START storage_restore_bucket]
16+
17+
using Google.Apis.Storage.v1.Data;
18+
using Google.Cloud.Storage.V1;
19+
using System;
20+
public class RestoreSoftDeletedBucketSample
21+
{
22+
/// <summary>
23+
/// Restores a soft deleted bucket.
24+
/// </summary>
25+
/// <param name="bucketName">The name of the bucket to restore.</param>
26+
/// <param name="generation">The generation of the bucket.</param>
27+
public Bucket RestoreSoftDeletedBucket(
28+
string bucketName = "your-unique-bucket-name",
29+
long generation = 123456789)
30+
{
31+
var client = StorageClient.Create();
32+
var restored = client.RestoreBucket(bucketName, generation);
33+
Console.WriteLine($"Bucket Name:\t {restored.Name}");
34+
Console.WriteLine($"Bucket Generation:\t {restored.Generation}");
35+
return restored;
36+
}
37+
}
38+
// [END storage_restore_bucket]

storage/api/Storage.Samples/Storage.Samples.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>netstandard2.1</TargetFramework>

0 commit comments

Comments
 (0)