diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.IntegrationTests/GetBucketTest.cs b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.IntegrationTests/GetBucketTest.cs new file mode 100644 index 000000000000..f97b21c0b207 --- /dev/null +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.IntegrationTests/GetBucketTest.cs @@ -0,0 +1,43 @@ +// Copyright 2024 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. + +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Google.Cloud.Storage.V1.IntegrationTests; +[Collection(nameof(StorageFixture))] +public class GetBucketTest +{ + private readonly StorageFixture _fixture; + + public GetBucketTest(StorageFixture fixture) + { + _fixture = fixture; + } + + [Fact] + public async Task SoftDeleted() + { + var bucketName = _fixture.GenerateBucketName(); + var softDeleteBucket = _fixture.CreateBucket(bucketName, false, true); + await _fixture.Client.DeleteBucketAsync(softDeleteBucket.Name, new DeleteBucketOptions { DeleteObjects = true }); + + var softDeleted = await _fixture.Client.GetBucketAsync(softDeleteBucket.Name, new GetBucketOptions { SoftDeleted = true, Generation = softDeleteBucket.Generation }); + Assert.Equal(softDeleteBucket.Name, softDeleted.Name); + Assert.Equal(softDeleteBucket.Generation, softDeleted.Generation); + Assert.NotNull(softDeleted.SoftDeleteTimeDateTimeOffset); + Assert.NotNull(softDeleted.HardDeleteTimeDateTimeOffset); + } +} diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.IntegrationTests/ListBucketsTest.cs b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.IntegrationTests/ListBucketsTest.cs index c4f6f04799a1..563b3cabcdca 100644 --- a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.IntegrationTests/ListBucketsTest.cs +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.IntegrationTests/ListBucketsTest.cs @@ -75,6 +75,27 @@ public void PartialResponses() } } + [Fact] + public async Task SoftDeletedOnly() + { + var bucketName = _fixture.GenerateBucketName(); + var softDeleteBucket = _fixture.CreateBucket(bucketName, false, true); + await _fixture.Client.DeleteBucketAsync(softDeleteBucket.Name, new DeleteBucketOptions { DeleteObjects = true }); + var actualBuckets = await _fixture.Client.ListBucketsAsync(_fixture.ProjectId, new ListBucketsOptions { SoftDeletedOnly = true }).ToListAsync(); + + foreach (var bucket in actualBuckets) + { // Verify if the bucket is soft-deleted only + Assert.NotNull(bucket.Generation); + Assert.NotNull(bucket.SoftDeleteTimeDateTimeOffset); + Assert.NotNull(bucket.HardDeleteTimeDateTimeOffset); + + if (bucket.Name == softDeleteBucket.Name) + { // Compare the generation number + Assert.Equal(bucket.Generation, softDeleteBucket.Generation); + } + } + } + // Fetches buckets using the given options in each possible way, validating that the expected bucket names are returned. private async Task AssertBuckets(ListBucketsOptions options, params string[] expectedBucketNames) { diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.IntegrationTests/RestoreBucketTest.cs b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.IntegrationTests/RestoreBucketTest.cs new file mode 100644 index 000000000000..888f2afd29bf --- /dev/null +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.IntegrationTests/RestoreBucketTest.cs @@ -0,0 +1,42 @@ +// Copyright 2024 Google Inc. All Rights Reserved. +// +// 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 System.Threading.Tasks; +using Xunit; + +namespace Google.Cloud.Storage.V1.IntegrationTests; + +[Collection(nameof(StorageFixture))] +public class RestoreBucketTest +{ + private readonly StorageFixture _fixture; + + public RestoreBucketTest(StorageFixture fixture) + { + _fixture = fixture; + } + + [Fact] + public async Task RestoreSoftDeletedBucket() + { + var bucketName = _fixture.GenerateBucketName(); + var softDeleteBucket = _fixture.CreateBucket(bucketName, false, true); + await _fixture.Client.DeleteBucketAsync(softDeleteBucket.Name, new DeleteBucketOptions { DeleteObjects = true }); + + var restoredBucket = await _fixture.Client.RestoreBucketAsync(softDeleteBucket.Name, softDeleteBucket.Generation.Value); + Assert.Equal(softDeleteBucket.Name, restoredBucket.Name); + Assert.Equal(softDeleteBucket.Generation, restoredBucket.Generation); + } +} diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Tests/GetBucketOptionsTest.cs b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Tests/GetBucketOptionsTest.cs index abdb68f17624..73df3f10fda2 100644 --- a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Tests/GetBucketOptionsTest.cs +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Tests/GetBucketOptionsTest.cs @@ -31,6 +31,8 @@ public void ModifyRequest_DefaultOptions() Assert.Null(request.IfMetagenerationNotMatch); Assert.Null(request.Projection); Assert.Null(request.UserProject); + Assert.Null(request.SoftDeleted); + Assert.Null(request.Generation); } [Fact] @@ -41,13 +43,17 @@ public void ModifyRequest_PositiveMatchOptions() { IfMetagenerationMatch = 1L, Projection = Projection.Full, - UserProject = "proj" + UserProject = "proj", + SoftDeleted = true, + Generation = long.MaxValue }; options.ModifyRequest(request); Assert.Equal(1L, request.IfMetagenerationMatch); Assert.Null(request.IfMetagenerationNotMatch); Assert.Equal(ProjectionEnum.Full, request.Projection); Assert.Equal("proj", request.UserProject); + Assert.Equal(true, request.SoftDeleted); + Assert.Equal(long.MaxValue,request.Generation); } [Fact] @@ -61,6 +67,8 @@ public void ModifyRequest_NegativeMatchOptions() }; options.ModifyRequest(request); Assert.Null(request.IfMetagenerationMatch); + Assert.Null(request.SoftDeleted); + Assert.Null(request.Generation); Assert.Equal(1L, request.IfMetagenerationNotMatch); Assert.Equal(ProjectionEnum.Full, request.Projection); } diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Tests/ListBucketsOptionsTest.cs b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Tests/ListBucketsOptionsTest.cs index f4868acd6c0a..2d21162d4248 100644 --- a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Tests/ListBucketsOptionsTest.cs +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Tests/ListBucketsOptionsTest.cs @@ -30,6 +30,7 @@ public void ModifyRequest_DefaultOptions() Assert.Null(request.Prefix); Assert.Null(request.MaxResults); Assert.Null(request.PageToken); + Assert.Null(request.SoftDeleted); } [Fact] @@ -42,7 +43,8 @@ public void ModifyRequest_AllOptions() Prefix = "prefix", Projection = Projection.Full, PageToken = "nextpage", - Fields = "items(name),nextPageToken" + Fields = "items(name),nextPageToken", + SoftDeletedOnly = true, }; options.ModifyRequest(request); Assert.Equal(10, request.MaxResults); @@ -50,6 +52,7 @@ public void ModifyRequest_AllOptions() Assert.Equal(ProjectionEnum.Full, request.Projection); Assert.Equal("nextpage", request.PageToken); Assert.Equal("items(name),nextPageToken", request.Fields); + Assert.Equal(true, request.SoftDeleted); } } } diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Tests/RestoreBucketOptionsTest.cs b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Tests/RestoreBucketOptionsTest.cs new file mode 100644 index 000000000000..8a5fe2503c69 --- /dev/null +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.Tests/RestoreBucketOptionsTest.cs @@ -0,0 +1,45 @@ +// Copyright 2024 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. + +using Xunit; +using static Google.Apis.Storage.v1.BucketsResource; +using static Google.Apis.Storage.v1.BucketsResource.RestoreRequest; + +namespace Google.Cloud.Storage.V1.Tests; +public class RestoreBucketOptionsTest +{ + [Fact] + public void ModifyRequest_DefaultOptions() + { + var request = new RestoreRequest(null, "bucket", 2L); + var options = new RestoreBucketOptions(); + options.ModifyRequest(request); + Assert.Null(request.Projection); + Assert.Null(request.UserProject); + } + + [Fact] + public void ModifyRequest_AllOptions() + { + var request = new RestoreRequest(null, "bucket", 2L); + var options = new RestoreBucketOptions + { + Projection = Projection.Full, + UserProject = "proj" + }; + options.ModifyRequest(request); + Assert.Equal(ProjectionEnum.Full, request.Projection); + Assert.Equal("proj", request.UserProject); + } +} diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/GetBucketOptions.cs b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/GetBucketOptions.cs index 266dab4389f6..c79ce8762512 100644 --- a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/GetBucketOptions.cs +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/GetBucketOptions.cs @@ -52,6 +52,17 @@ public sealed class GetBucketOptions /// public RetryOptions RetryOptions { get; set; } + /// + /// The bucket generation to be retrieved. It must be set if is true. + /// + public long? Generation { get; set; } + + /// + /// If true, the soft-deleted version of the bucket will be retrieved. + /// If true, must be set. + /// + public bool? SoftDeleted { get; set; } + internal void ModifyRequest(GetRequest request) { if (IfMetagenerationMatch != null && IfMetagenerationNotMatch != null) @@ -75,6 +86,15 @@ internal void ModifyRequest(GetRequest request) { request.UserProject = UserProject; } + if (Generation != null) + { + request.Generation = Generation; + } + if (SoftDeleted != null) + { + request.SoftDeleted = SoftDeleted; + } + } } } diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.csproj b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.csproj index 9cfae17c9fd9..d0fb5ece91fe 100644 --- a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.csproj +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/ListBucketsOptions.cs b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/ListBucketsOptions.cs index 3fe2fccd2110..13881e8bce8f 100644 --- a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/ListBucketsOptions.cs +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/ListBucketsOptions.cs @@ -62,6 +62,11 @@ public sealed class ListBucketsOptions /// public RetryOptions RetryOptions { get; set; } + /// + /// If true, only soft-deleted buckets will be listed. The default is false. + /// + public bool? SoftDeletedOnly { get; set; } + /// /// Modifies the specified request for all non-null properties of this options object. /// @@ -88,6 +93,10 @@ internal void ModifyRequest(ListRequest request) { request.Fields = Fields; } + if (SoftDeletedOnly != null) + { + request.SoftDeleted = SoftDeletedOnly; + } } } } diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/RestoreBucketOptions.cs b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/RestoreBucketOptions.cs new file mode 100644 index 000000000000..200dc17dc759 --- /dev/null +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/RestoreBucketOptions.cs @@ -0,0 +1,61 @@ +// Copyright 2024 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. + +using Google.Api.Gax; +using static Google.Apis.Storage.v1.BucketsResource; +using static Google.Apis.Storage.v1.BucketsResource.RestoreRequest; + +namespace Google.Cloud.Storage.V1; + +/// +/// Options for RestoreBucket operations. +/// +public sealed class RestoreBucketOptions +{ + /// + /// The projection of the restored bucket to return. Note the whole bucket will be restored, + /// except for the bucket's access controls. This only affects + /// what information is returned when restoration is successful. + /// + public Projection? Projection { get; set; } + + /// + /// The encryption key to use for this operation. If this property is null, the + /// will be used instead. Use to remove encryption headers from this request. + /// + public EncryptionKey EncryptionKey { get; set; } + + /// + /// If set, this is the ID of the project which will be billed for the request. + /// The caller must have suitable permissions for the project being billed. + /// + public string UserProject { get; set; } + + /// + /// Options to pass custom retry configuration for each API request. + /// + public RetryOptions RetryOptions { get; set; } + + internal void ModifyRequest(RestoreRequest request) + { + if (Projection != null) + { + request.Projection = GaxPreconditions.CheckEnumValue((ProjectionEnum) Projection, nameof(Projection)); + } + if (UserProject != null) + { + request.UserProject = UserProject; + } + } +} diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/StorageClient.RestoreBucket.cs b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/StorageClient.RestoreBucket.cs new file mode 100644 index 000000000000..37f7b707120f --- /dev/null +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/StorageClient.RestoreBucket.cs @@ -0,0 +1,49 @@ +// Copyright 2024 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. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Bucket = Google.Apis.Storage.v1.Data.Bucket; + +namespace Google.Cloud.Storage.V1; +public abstract partial class StorageClient +{ + /// + /// Restores a soft-deleted bucket. + /// + /// The name of the bucket. Must not be null. + /// The specific revision of the bucket to restore. + /// Additional options for the restore operation. May be null, in which case appropriate + /// defaults will be used. + /// The representation of the restored Storage bucket. + public virtual Bucket RestoreBucket(string bucket, long generation, RestoreBucketOptions options = null) => + throw new NotImplementedException(); + + /// + /// Restores a soft-deleted bucket. + /// + /// The name of the bucket. Must not be null. + /// The specific revision of the bucket to restore. + /// Additional options for the restore operation. May be null, in which case appropriate + /// defaults will be used. + /// The token to monitor for cancellation requests. + /// A task representing the asynchronous operation, with a result returning the + /// representation of the restored Storage bucket. + public virtual Task RestoreBucketAsync( + string bucket, + long generation, + RestoreBucketOptions options = null, + CancellationToken cancellationToken = default) => throw new NotImplementedException(); +} diff --git a/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/StorageClientImpl.RestoreBucket.cs b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/StorageClientImpl.RestoreBucket.cs new file mode 100644 index 000000000000..ad3be96a5c46 --- /dev/null +++ b/apis/Google.Cloud.Storage.V1/Google.Cloud.Storage.V1/StorageClientImpl.RestoreBucket.cs @@ -0,0 +1,44 @@ +// Copyright 2024 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. + +using System.Threading.Tasks; +using System.Threading; +using Google.Apis.Storage.v1; +using Bucket = Google.Apis.Storage.v1.Data.Bucket; + +namespace Google.Cloud.Storage.V1; +public sealed partial class StorageClientImpl : StorageClient +{ + /// + public override Bucket RestoreBucket(string bucket, long generation, RestoreBucketOptions options = null) => + CreateRestoreBucketRequest(bucket, generation, options).Execute(); + + /// + public override Task RestoreBucketAsync( + string bucket, + long generation, + RestoreBucketOptions options = null, + CancellationToken cancellationToken = default) => + CreateRestoreBucketRequest(bucket, generation, options).ExecuteAsync(cancellationToken); + + private BucketsResource.RestoreRequest CreateRestoreBucketRequest(string bucket, long generation, RestoreBucketOptions options) + { + ValidateBucketName(bucket); + var request = Service.Buckets.Restore(bucket, generation); + options?.ModifyRequest(request); + RetryOptions retryOptions = options?.RetryOptions ?? RetryOptions.IdempotentRetryOptions; + MarkAsRetriable(request, retryOptions); + return request; + } +} diff --git a/generator-input/apis.json b/generator-input/apis.json index 364541b497fb..ab03c7130f91 100644 --- a/generator-input/apis.json +++ b/generator-input/apis.json @@ -5152,7 +5152,7 @@ "description": "Recommended Google client library to access the Google Cloud Storage API. It wraps the Google.Apis.Storage.v1 client library, making common operations simpler in client code. Google Cloud Storage stores and retrieves potentially large, immutable data objects.", "dependencies": { "Google.Api.Gax.Rest": "default", - "Google.Apis.Storage.v1": "1.68.0.3431" + "Google.Apis.Storage.v1": "1.68.0.3604" }, "testDependencies": { "Google.Api.Gax.Testing": "default",