From 4e50755b468393f2b9271b24db7aadf4c961ae18 Mon Sep 17 00:00:00 2001 From: mahendra-google Date: Tue, 24 Mar 2026 23:29:49 -0700 Subject: [PATCH 1/2] samples(Storage): Add samples and tests for ObjectContext --- .../GetObjectContextsTest.cs | 57 ++++++++++++++++++ .../ListObjectsWithContextFilterTest.cs | 59 +++++++++++++++++++ .../SetObjectContextsTest.cs | 57 ++++++++++++++++++ .../api/Storage.Samples/GetObjectContexts.cs | 49 +++++++++++++++ .../ListObjectsWithContextFilter.cs | 51 ++++++++++++++++ .../api/Storage.Samples/SetObjectContexts.cs | 46 +++++++++++++++ .../Storage.Samples/Storage.Samples.csproj | 2 +- 7 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 storage/api/Storage.Samples.Tests/GetObjectContextsTest.cs create mode 100644 storage/api/Storage.Samples.Tests/ListObjectsWithContextFilterTest.cs create mode 100644 storage/api/Storage.Samples.Tests/SetObjectContextsTest.cs create mode 100644 storage/api/Storage.Samples/GetObjectContexts.cs create mode 100644 storage/api/Storage.Samples/ListObjectsWithContextFilter.cs create mode 100644 storage/api/Storage.Samples/SetObjectContexts.cs diff --git a/storage/api/Storage.Samples.Tests/GetObjectContextsTest.cs b/storage/api/Storage.Samples.Tests/GetObjectContextsTest.cs new file mode 100644 index 00000000000..d5626da822a --- /dev/null +++ b/storage/api/Storage.Samples.Tests/GetObjectContextsTest.cs @@ -0,0 +1,57 @@ +// Copyright 2026 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.Apis.Storage.v1.Data; +using System.Collections.Generic; +using Xunit; + +[Collection(nameof(StorageFixture))] +public class GetObjectContextsTest +{ + private readonly StorageFixture _fixture; + + public GetObjectContextsTest(StorageFixture fixture) + { + _fixture = fixture; + } + + [Fact] + public void GetObjectContexts() + { + GetObjectContextsSample getContextsSample = new GetObjectContextsSample(); + SetObjectContextsSample setContextsSample = new SetObjectContextsSample(); + UploadObjectFromMemorySample uploadObjectSample = new UploadObjectFromMemorySample(); + + string contextKey = "A\u00F1\u03A9\U0001F680"; + string contextValue = "Ab\u00F1\u03A9\U0001F680"; + + var custom = new Dictionary + { + { contextKey, new ObjectCustomContextPayload { Value = contextValue } } + }; + + var bucketName = _fixture.GenerateBucketName(); + var objectName = _fixture.GenerateName(); + var contextsData = new Object.ContextsData { Custom = custom }; + _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true); + var content = _fixture.GenerateContent(); + uploadObjectSample.UploadObjectFromMemory(bucketName, objectName, content); + var appliedContexts = setContextsSample.SetObjectContexts(bucketName, objectName, contextsData); + var retrievedContexts = getContextsSample.GetObjectContexts(bucketName, objectName); + Assert.Equal(appliedContexts.Custom.Count, retrievedContexts.Custom.Count); + var singleContext = Assert.Single(retrievedContexts.Custom); + Assert.Equal(contextKey, singleContext.Key); + Assert.Equal(contextValue, singleContext.Value.Value); + } +} diff --git a/storage/api/Storage.Samples.Tests/ListObjectsWithContextFilterTest.cs b/storage/api/Storage.Samples.Tests/ListObjectsWithContextFilterTest.cs new file mode 100644 index 00000000000..938a33ee2ba --- /dev/null +++ b/storage/api/Storage.Samples.Tests/ListObjectsWithContextFilterTest.cs @@ -0,0 +1,59 @@ +// Copyright 2026 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.Apis.Storage.v1.Data; +using System.Collections.Generic; +using Xunit; + +[Collection(nameof(StorageFixture))] +public class ListObjectsWithContextFilterTest +{ + private readonly StorageFixture _fixture; + + public ListObjectsWithContextFilterTest(StorageFixture fixture) + { + _fixture = fixture; + } + + [Fact] + public void ListObjectsWithContextFilter() + { + ListObjectsWithContextFilterSample listContextsSample = new ListObjectsWithContextFilterSample(); + SetObjectContextsSample setContextsSample = new SetObjectContextsSample(); + UploadObjectFromMemorySample uploadObjectSample = new UploadObjectFromMemorySample(); + + string contextKey = "A\u00F1\u03A9\U0001F680"; + string contextValue = "Ab\u00F1\u03A9\U0001F680"; + + var custom = new Dictionary + { + { contextKey, new ObjectCustomContextPayload { Value = contextValue } } + }; + + var bucketName = _fixture.GenerateBucketName(); + var objectName = _fixture.GenerateName(); + var contextsData = new Object.ContextsData { Custom = custom }; + _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true); + var content = _fixture.GenerateContent(); + uploadObjectSample.UploadObjectFromMemory(bucketName, objectName, content); + setContextsSample.SetObjectContexts(bucketName, objectName, contextsData); + + string filter = $@"contexts.""{contextKey}""=""{contextValue}"""; + var filteredObjects = listContextsSample.ListObjectsWithContextFilter(bucketName, filter); + var obj = Assert.Single(filteredObjects); + var fetchedContext = Assert.Single(obj.Contexts.Custom); + Assert.Equal(contextKey, fetchedContext.Key); + Assert.Equal(contextValue, fetchedContext.Value.Value); + } +} diff --git a/storage/api/Storage.Samples.Tests/SetObjectContextsTest.cs b/storage/api/Storage.Samples.Tests/SetObjectContextsTest.cs new file mode 100644 index 00000000000..2135247aead --- /dev/null +++ b/storage/api/Storage.Samples.Tests/SetObjectContextsTest.cs @@ -0,0 +1,57 @@ +// Copyright 2026 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.Apis.Storage.v1.Data; +using System.Collections.Generic; +using Xunit; + +[Collection(nameof(StorageFixture))] +public class SetObjectContextsTest +{ + private readonly StorageFixture _fixture; + + public SetObjectContextsTest(StorageFixture fixture) + { + _fixture = fixture; + } + + [Fact] + public void SetObjectContexts() + { + SetObjectContextsSample contextsSample = new SetObjectContextsSample(); + UploadObjectFromMemorySample uploadObjectSample = new UploadObjectFromMemorySample(); + + string contextKey = "A\u00F1\u03A9\U0001F680"; + string contextValue = "Ab\u00F1\u03A9\U0001F680"; + + var custom = new Dictionary + { + { contextKey, new ObjectCustomContextPayload { Value = contextValue } } + }; + + var bucketName = _fixture.GenerateBucketName(); + var objectName = _fixture.GenerateName(); + _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true); + + var contextsData = new Object.ContextsData { Custom = custom }; + var content = _fixture.GenerateContent(); + uploadObjectSample.UploadObjectFromMemory(bucketName, objectName, content); + contextsSample.SetObjectContexts(bucketName, objectName, contextsData); + var retrievedObject = _fixture.Client.GetObject(bucketName, objectName); + Assert.Equal(contextsData.Custom.Count, retrievedObject.Contexts.Custom.Count); + var singleContext = Assert.Single(retrievedObject.Contexts.Custom); + Assert.Equal(contextKey, singleContext.Key); + Assert.Equal(contextValue, singleContext.Value.Value); + } +} diff --git a/storage/api/Storage.Samples/GetObjectContexts.cs b/storage/api/Storage.Samples/GetObjectContexts.cs new file mode 100644 index 00000000000..1ff29d89430 --- /dev/null +++ b/storage/api/Storage.Samples/GetObjectContexts.cs @@ -0,0 +1,49 @@ +// Copyright 2026 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. + +// [START storage_get_object_contexts] + +using Google.Cloud.Storage.V1; +using System; + +public class GetObjectContextsSample +{ + /// + /// Gets the context data associated with the object. + /// + /// The name of the bucket containing the object. + /// The name of the object. + public Google.Apis.Storage.v1.Data.Object.ContextsData GetObjectContexts( + string bucketName = "your-unique-bucket-name", + string objectName = "your-object-name") + { + var storage = StorageClient.Create(); + var obj = storage.GetObject(bucketName, objectName); + Google.Apis.Storage.v1.Data.Object.ContextsData contextsData = obj.Contexts; + + if (contextsData?.Custom == null || contextsData.Custom.Count == 0) + { + Console.WriteLine($"No Context Data found for the Object {objectName}"); + return contextsData; + } + + Console.WriteLine($"Context Data for the Object {objectName} is as follows:"); + foreach (var (key, contextPayload) in contextsData.Custom) + { + Console.WriteLine($"Context Key: {key}, Context Value: {contextPayload.Value}"); + } + return contextsData; + } +} +// [END storage_get_object_contexts] diff --git a/storage/api/Storage.Samples/ListObjectsWithContextFilter.cs b/storage/api/Storage.Samples/ListObjectsWithContextFilter.cs new file mode 100644 index 00000000000..afdda45c363 --- /dev/null +++ b/storage/api/Storage.Samples/ListObjectsWithContextFilter.cs @@ -0,0 +1,51 @@ +// Copyright 2026 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_list_object_contexts] + +using Google.Cloud.Storage.V1; +using System; +using System.Collections.Generic; +using System.Linq; + +public class ListObjectsWithContextFilterSample +{ + /// + /// List objects in the specified bucket that match a context filter. + /// + /// The name of the bucket. + /// The metadata context filter (e.g. "contexts.\"KEY\":*", "-contexts.\"KEY\":*", "contexts.\"KEY\"=\"VALUE\"", "-contexts.\"KEY\"=\"VALUE\""). + public IEnumerable ListObjectsWithContextFilter(string bucketName = "your-unique-bucket-name", + string filter = "contexts.\"KEY\"=\"VALUE\"") + { + var storage = StorageClient.Create(); + var objects = storage.ListObjects(bucketName, prefix: null, new ListObjectsOptions { Filter = filter, Fields = "items(name,contexts)" }).ToList(); + Console.WriteLine($"The Names and Context Data for the Objects in the {bucketName} with Context Filter {filter} are as follows:"); + foreach (var obj in objects) + { + Console.WriteLine($"Object: {obj.Name}"); + Console.WriteLine($"Context Data for the Object {obj.Name} is as follows:"); + + if (obj.Contexts?.Custom is { } customContexts) + { + foreach (var (key, contextPayload) in customContexts) + { + Console.WriteLine($"Context Key: {key}, Context Value: {contextPayload.Value}"); + } + } + } + return objects; + } +} +// [END storage_list_object_contexts] diff --git a/storage/api/Storage.Samples/SetObjectContexts.cs b/storage/api/Storage.Samples/SetObjectContexts.cs new file mode 100644 index 00000000000..6459a4161cf --- /dev/null +++ b/storage/api/Storage.Samples/SetObjectContexts.cs @@ -0,0 +1,46 @@ +// Copyright 2026 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. + +// [START storage_set_object_contexts] + +using Google.Cloud.Storage.V1; +using System; + +public class SetObjectContextsSample +{ + /// + /// Sets the context data associated with the object. + /// + /// The name of the bucket containing the object. + /// The name of the object. + /// User-defined or system-defined object contexts. Each object context is a key-payload pair, where the key + /// provides the identification and the payload holds the associated value and additional metadata. + public Google.Apis.Storage.v1.Data.Object.ContextsData SetObjectContexts( + string bucketName = "your-unique-bucket-name", + string objectName = "your-object-name", + Google.Apis.Storage.v1.Data.Object.ContextsData contextsData = null) + { + var storage = StorageClient.Create(); + var obj = new Google.Apis.Storage.v1.Data.Object + { + Bucket = bucketName, + Name = objectName, + Contexts = contextsData + }; + var updatedObject = storage.UpdateObject(obj); + Console.WriteLine($"Updated Context Data for the Object {objectName} in the Bucket {bucketName}"); + return updatedObject.Contexts; + } +} +// [END storage_set_object_contexts] diff --git a/storage/api/Storage.Samples/Storage.Samples.csproj b/storage/api/Storage.Samples/Storage.Samples.csproj index 07cace9f9ad..db145d68510 100644 --- a/storage/api/Storage.Samples/Storage.Samples.csproj +++ b/storage/api/Storage.Samples/Storage.Samples.csproj @@ -6,7 +6,7 @@ - + From 3a07701a5b98b06d0e6fd60b8b5839baa4f399e3 Mon Sep 17 00:00:00 2001 From: mahendra-google Date: Mon, 6 Apr 2026 01:34:43 -0700 Subject: [PATCH 2/2] refactor(Storage): Apply suggestions from code review --- .../api/Storage.Samples.Tests/GetObjectContextsTest.cs | 5 ++--- .../ListObjectsWithContextFilterTest.cs | 5 ++--- .../api/Storage.Samples.Tests/SetObjectContextsTest.cs | 7 +++---- storage/api/Storage.Samples/SetObjectContexts.cs | 9 ++++++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/storage/api/Storage.Samples.Tests/GetObjectContextsTest.cs b/storage/api/Storage.Samples.Tests/GetObjectContextsTest.cs index d5626da822a..9f32f9769f7 100644 --- a/storage/api/Storage.Samples.Tests/GetObjectContextsTest.cs +++ b/storage/api/Storage.Samples.Tests/GetObjectContextsTest.cs @@ -36,18 +36,17 @@ public void GetObjectContexts() string contextKey = "A\u00F1\u03A9\U0001F680"; string contextValue = "Ab\u00F1\u03A9\U0001F680"; - var custom = new Dictionary + var customContexts = new Dictionary { { contextKey, new ObjectCustomContextPayload { Value = contextValue } } }; var bucketName = _fixture.GenerateBucketName(); var objectName = _fixture.GenerateName(); - var contextsData = new Object.ContextsData { Custom = custom }; _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true); var content = _fixture.GenerateContent(); uploadObjectSample.UploadObjectFromMemory(bucketName, objectName, content); - var appliedContexts = setContextsSample.SetObjectContexts(bucketName, objectName, contextsData); + var appliedContexts = setContextsSample.SetObjectContexts(bucketName, objectName, customContexts); var retrievedContexts = getContextsSample.GetObjectContexts(bucketName, objectName); Assert.Equal(appliedContexts.Custom.Count, retrievedContexts.Custom.Count); var singleContext = Assert.Single(retrievedContexts.Custom); diff --git a/storage/api/Storage.Samples.Tests/ListObjectsWithContextFilterTest.cs b/storage/api/Storage.Samples.Tests/ListObjectsWithContextFilterTest.cs index 938a33ee2ba..bb5dbcfe8a6 100644 --- a/storage/api/Storage.Samples.Tests/ListObjectsWithContextFilterTest.cs +++ b/storage/api/Storage.Samples.Tests/ListObjectsWithContextFilterTest.cs @@ -36,18 +36,17 @@ public void ListObjectsWithContextFilter() string contextKey = "A\u00F1\u03A9\U0001F680"; string contextValue = "Ab\u00F1\u03A9\U0001F680"; - var custom = new Dictionary + var customContexts = new Dictionary { { contextKey, new ObjectCustomContextPayload { Value = contextValue } } }; var bucketName = _fixture.GenerateBucketName(); var objectName = _fixture.GenerateName(); - var contextsData = new Object.ContextsData { Custom = custom }; _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true); var content = _fixture.GenerateContent(); uploadObjectSample.UploadObjectFromMemory(bucketName, objectName, content); - setContextsSample.SetObjectContexts(bucketName, objectName, contextsData); + setContextsSample.SetObjectContexts(bucketName, objectName, customContexts); string filter = $@"contexts.""{contextKey}""=""{contextValue}"""; var filteredObjects = listContextsSample.ListObjectsWithContextFilter(bucketName, filter); diff --git a/storage/api/Storage.Samples.Tests/SetObjectContextsTest.cs b/storage/api/Storage.Samples.Tests/SetObjectContextsTest.cs index 2135247aead..c69556a1d96 100644 --- a/storage/api/Storage.Samples.Tests/SetObjectContextsTest.cs +++ b/storage/api/Storage.Samples.Tests/SetObjectContextsTest.cs @@ -35,7 +35,7 @@ public void SetObjectContexts() string contextKey = "A\u00F1\u03A9\U0001F680"; string contextValue = "Ab\u00F1\u03A9\U0001F680"; - var custom = new Dictionary + var customContexts = new Dictionary { { contextKey, new ObjectCustomContextPayload { Value = contextValue } } }; @@ -44,12 +44,11 @@ public void SetObjectContexts() var objectName = _fixture.GenerateName(); _fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true); - var contextsData = new Object.ContextsData { Custom = custom }; var content = _fixture.GenerateContent(); uploadObjectSample.UploadObjectFromMemory(bucketName, objectName, content); - contextsSample.SetObjectContexts(bucketName, objectName, contextsData); + var appliedContexts = contextsSample.SetObjectContexts(bucketName, objectName, customContexts); var retrievedObject = _fixture.Client.GetObject(bucketName, objectName); - Assert.Equal(contextsData.Custom.Count, retrievedObject.Contexts.Custom.Count); + Assert.Equal(appliedContexts.Custom.Count, retrievedObject.Contexts.Custom.Count); var singleContext = Assert.Single(retrievedObject.Contexts.Custom); Assert.Equal(contextKey, singleContext.Key); Assert.Equal(contextValue, singleContext.Value.Value); diff --git a/storage/api/Storage.Samples/SetObjectContexts.cs b/storage/api/Storage.Samples/SetObjectContexts.cs index 6459a4161cf..b6574170aa8 100644 --- a/storage/api/Storage.Samples/SetObjectContexts.cs +++ b/storage/api/Storage.Samples/SetObjectContexts.cs @@ -14,8 +14,10 @@ // [START storage_set_object_contexts] +using Google.Apis.Storage.v1.Data; using Google.Cloud.Storage.V1; using System; +using System.Collections.Generic; public class SetObjectContextsSample { @@ -24,14 +26,15 @@ public class SetObjectContextsSample /// /// The name of the bucket containing the object. /// The name of the object. - /// User-defined or system-defined object contexts. Each object context is a key-payload pair, where the key - /// provides the identification and the payload holds the associated value and additional metadata. + /// The new user-defined contexts to set context data associated with the object. public Google.Apis.Storage.v1.Data.Object.ContextsData SetObjectContexts( string bucketName = "your-unique-bucket-name", string objectName = "your-object-name", - Google.Apis.Storage.v1.Data.Object.ContextsData contextsData = null) + IDictionary customContexts = null) { var storage = StorageClient.Create(); + customContexts ??= new Dictionary(); + var contextsData = new Google.Apis.Storage.v1.Data.Object.ContextsData { Custom = customContexts }; var obj = new Google.Apis.Storage.v1.Data.Object { Bucket = bucketName,