Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
56 changes: 56 additions & 0 deletions storage/api/Storage.Samples.Tests/GetObjectContextsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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 customContexts = new Dictionary<string, ObjectCustomContextPayload>
{
{ contextKey, new ObjectCustomContextPayload { Value = contextValue } }
};

var bucketName = _fixture.GenerateBucketName();
var objectName = _fixture.GenerateName();
_fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);
var content = _fixture.GenerateContent();
uploadObjectSample.UploadObjectFromMemory(bucketName, objectName, content);
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);
Assert.Equal(contextKey, singleContext.Key);
Assert.Equal(contextValue, singleContext.Value.Value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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 customContexts = new Dictionary<string, ObjectCustomContextPayload>
{
{ contextKey, new ObjectCustomContextPayload { Value = contextValue } }
};

var bucketName = _fixture.GenerateBucketName();
var objectName = _fixture.GenerateName();
_fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);
var content = _fixture.GenerateContent();
uploadObjectSample.UploadObjectFromMemory(bucketName, objectName, content);
setContextsSample.SetObjectContexts(bucketName, objectName, customContexts);

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);
}
}
56 changes: 56 additions & 0 deletions storage/api/Storage.Samples.Tests/SetObjectContextsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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();
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 SetObjectContexts method in SetObjectContextsSample does not rely on any instance state. It creates a StorageClient internally and performs an operation. Consider making the SetObjectContexts method static in SetObjectContexts.cs to reflect its stateless nature and avoid unnecessary object instantiation in tests and other sample calls.

        SetObjectContextsSample.SetObjectContexts(bucketName, objectName, contextsData);

UploadObjectFromMemorySample uploadObjectSample = new UploadObjectFromMemorySample();

string contextKey = "A\u00F1\u03A9\U0001F680";
string contextValue = "Ab\u00F1\u03A9\U0001F680";

var customContexts = new Dictionary<string, ObjectCustomContextPayload>
{
{ contextKey, new ObjectCustomContextPayload { Value = contextValue } }
};

var bucketName = _fixture.GenerateBucketName();
var objectName = _fixture.GenerateName();
_fixture.CreateBucket(bucketName, multiVersion: false, softDelete: true, registerForDeletion: true);

var content = _fixture.GenerateContent();
uploadObjectSample.UploadObjectFromMemory(bucketName, objectName, content);
var appliedContexts = contextsSample.SetObjectContexts(bucketName, objectName, customContexts);
var retrievedObject = _fixture.Client.GetObject(bucketName, objectName);
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);
}
}
49 changes: 49 additions & 0 deletions storage/api/Storage.Samples/GetObjectContexts.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Gets the context data associated with the object.
/// </summary>
/// <param name="bucketName">The name of the bucket containing the object.</param>
/// <param name="objectName">The name of the object.</param>
public Google.Apis.Storage.v1.Data.Object.ContextsData GetObjectContexts(
string bucketName = "your-unique-bucket-name",
string objectName = "your-object-name")
{
var storage = StorageClient.Create();
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

Creating a new StorageClient instance with StorageClient.Create() for each operation can introduce unnecessary overhead. For better efficiency and resource management in a production application, it's generally recommended to reuse a single StorageClient instance across multiple calls, perhaps by making it a static field or injecting it.

var obj = storage.GetObject(bucketName, objectName);
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

For efficiency, it's better to request only the fields you need. Since this sample only uses the Contexts property of the object, you can specify that using GetObjectOptions. This avoids fetching the entire object metadata.

        var obj = storage.GetObject(bucketName, objectName, new GetObjectOptions { Fields = "contexts" });

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}");
}
Comment thread
mahendra-google marked this conversation as resolved.
return contextsData;
}
}
// [END storage_get_object_contexts]
51 changes: 51 additions & 0 deletions storage/api/Storage.Samples/ListObjectsWithContextFilter.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// List objects in the specified bucket that match a context filter.
/// </summary>
/// <param name="bucketName">The name of the bucket.</param>
/// <param name="filter">The metadata context filter (e.g. "contexts.\"KEY\":*", "-contexts.\"KEY\":*", "contexts.\"KEY\"=\"VALUE\"", "-contexts.\"KEY\"=\"VALUE\"").</param>
public IEnumerable<Google.Apis.Storage.v1.Data.Object> 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}");
}
}
Comment thread
mahendra-google marked this conversation as resolved.
Comment on lines +38 to +46
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 code accesses obj.Contexts.Custom without checking if obj.Contexts or obj.Contexts.Custom might be null. If an object in the list does not have context data, this will cause a NullReferenceException. You should add a check to ensure obj.Contexts.Custom is not null and not empty before iterating over it.

            if (obj.Contexts?.Custom != null && obj.Contexts.Custom.Count > 0)
            {
                Console.WriteLine($"Context Data for the Object {obj.Name} is as follows:");
                foreach (var (key, contextPayload) in obj.Contexts.Custom)
                {
                    Console.WriteLine($"Context Key: {key}, Context Value: {contextPayload.Value}");
                }
            }

}
return objects;
}
}
// [END storage_list_object_contexts]
49 changes: 49 additions & 0 deletions storage/api/Storage.Samples/SetObjectContexts.cs
Original file line number Diff line number Diff line change
@@ -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_set_object_contexts]

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

public class SetObjectContextsSample
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 SetObjectContexts method within this class does not utilize any instance members. To improve clarity and avoid unnecessary object instantiation, consider making both the class SetObjectContextsSample and the SetObjectContexts method static.

public static class SetObjectContextsSample

{
/// <summary>
/// Sets the context data associated with the object.
/// </summary>
/// <param name="bucketName">The name of the bucket containing the object.</param>
/// <param name="objectName">The name of the object.</param>
/// <param name="customContexts">The new user-defined contexts to set context data associated with the object.</param>
public Google.Apis.Storage.v1.Data.Object.ContextsData SetObjectContexts(
string bucketName = "your-unique-bucket-name",
string objectName = "your-object-name",
IDictionary<string, ObjectCustomContextPayload> customContexts = null)
{
var storage = StorageClient.Create();
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

Creating a new StorageClient instance with StorageClient.Create() for each operation can introduce unnecessary overhead. For better efficiency and resource management in a production application, it's generally recommended to reuse a single StorageClient instance across multiple calls, perhaps by making it a static field or injecting it.

customContexts ??= new Dictionary<string, ObjectCustomContextPayload>();
var contextsData = new Google.Apis.Storage.v1.Data.Object.ContextsData { Custom = customContexts };
var obj = new Google.Apis.Storage.v1.Data.Object
{
Bucket = bucketName,
Name = objectName,
Contexts = contextsData
};
var updatedObject = storage.UpdateObject(obj);
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

For better performance, you can specify that only the Contexts field should be returned in the response object. This reduces the amount of data transferred from the service.

        var updatedObject = storage.UpdateObject(obj, new UpdateObjectOptions { Fields = "contexts" });

Console.WriteLine($"Updated Context Data for the Object {objectName} in the Bucket {bucketName}");
return updatedObject.Contexts;
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 console message currently specifies "custom contexts data". However, the contextsData parameter description mentions "User-defined or system-defined object contexts". To be more accurate and general, the message should reflect the possibility of both types of contexts.

        Console.WriteLine($"Updated contexts data for the object {objectName}");

}
}
// [END storage_set_object_contexts]
2 changes: 1 addition & 1 deletion storage/api/Storage.Samples/Storage.Samples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<ItemGroup>
<PackageReference Include="Google.Cloud.Storage.Control.V2" Version="1.5.0" />
<PackageReference Include="Google.Cloud.Storage.V1" Version="4.14.0" />
<PackageReference Include="Google.Cloud.Storage.V1" Version="4.15.0" />
</ItemGroup>

</Project>