Skip to content

Commit

Permalink
Implement CosmosDBOptions to allow configuration of the CosmosDB s…
Browse files Browse the repository at this point in the history
…ervice client via `CosmosClientOptions` (#2483)
  • Loading branch information
liliankasem authored Jun 12, 2024
1 parent 9994356 commit ab70ee0
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 22 deletions.
19 changes: 15 additions & 4 deletions extensions/Worker.Extensions.CosmosDB/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,19 @@
- My change description (#PR/#issue)
-->

### Microsoft.Azure.Functions.Worker.Extensions.CosmosDB 4.8.1
### Microsoft.Azure.Functions.Worker.Extensions.CosmosDB 4.9.0

- Updating `Microsoft.Azure.WebJobs.Extensions.CosmosDB` reference to 4.6.1
- Updating `Microsoft.Extensions.Azure` reference to 1.7.3
- Updating `Microsoft.Azure.Cosmos` reference to 3.39.1
- Implement `CosmosDBExtensionOptions` to allow configuration of the CosmosDB service client via `CosmosClientOptions` (#2483)

#### Example Usage

```csharp
.ConfigureFunctionsWorkerDefaults((builder) =>
{
builder.ConfigureCosmosDBExtensionOptions((options) =>
{
options.ClientOptions.ConnectionMode = ConnectionMode.Direct;
options.ClientOptions.ApplicationName = "MyApp";
});
})
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@

namespace Microsoft.Azure.Functions.Worker
{
/// <summary>
/// Internal options for configuring the CosmosDB binding.
/// This class is used internally by the Azure Functions runtime to manage the CosmosDB connection and clients.
/// It is not intended to be used directly in user code.
/// Any public configuration options should be set on the <see cref="CosmosDBExtensionOptions"/> class, which is publicly accessible.
/// </summary>
internal class CosmosDBBindingOptions
{
public string? ConnectionName { get; set; }
Expand All @@ -19,7 +25,7 @@ internal class CosmosDBBindingOptions

public TokenCredential? Credential { get; set; }

public CosmosSerializer? Serializer { get; set; }
public CosmosDBExtensionOptions? CosmosExtensionOptions { get; set; }

internal string BuildCacheKey(string connection, string region) => $"{connection}|{region}";

Expand All @@ -32,24 +38,23 @@ internal virtual CosmosClient GetClient(string preferredLocations = "")
throw new ArgumentNullException(nameof(ConnectionName));
}

string cacheKey = BuildCacheKey(ConnectionName!, preferredLocations);

CosmosClientOptions cosmosClientOptions = new ()
{
ConnectionMode = ConnectionMode.Gateway
};

if (!string.IsNullOrEmpty(preferredLocations))
if (CosmosExtensionOptions is null)
{
cosmosClientOptions.ApplicationPreferredRegions = Utilities.ParsePreferredLocations(preferredLocations);
CosmosExtensionOptions = new CosmosDBExtensionOptions();
}

if (Serializer is not null)
string cacheKey = BuildCacheKey(ConnectionName!, preferredLocations);

// Do not override if preferred locations is configured via CosmosClientOptions
if (!string.IsNullOrEmpty(preferredLocations) && CosmosExtensionOptions.ClientOptions.ApplicationPreferredRegions is null)
{
cosmosClientOptions.Serializer = Serializer;
// Clone to avoid modifying the original options
CosmosClientOptions cosmosClientOptions = CosmosExtensionOptions.ClientOptions.MemberwiseClone<CosmosClientOptions>();
cosmosClientOptions.ApplicationPreferredRegions = Utilities.ParsePreferredLocations(preferredLocations);
return ClientCache.GetOrAdd(cacheKey, (c) => CreateService(cosmosClientOptions));
}

return ClientCache.GetOrAdd(cacheKey, (c) => CreateService(cosmosClientOptions));
return ClientCache.GetOrAdd(cacheKey, (c) => CreateService(CosmosExtensionOptions.ClientOptions));
}

private CosmosClient CreateService(CosmosClientOptions cosmosClientOptions)
Expand All @@ -59,4 +64,4 @@ private CosmosClient CreateService(CosmosClientOptions cosmosClientOptions)
: new CosmosClient(ConnectionString, cosmosClientOptions); // Connection string based auth
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Functions.Worker.Extensions;
using Microsoft.Azure.Functions.Worker.Extensions.CosmosDB;
using Microsoft.Extensions.Azure;
Expand All @@ -15,12 +16,14 @@ internal class CosmosDBBindingOptionsSetup : IConfigureNamedOptions<CosmosDBBind
private readonly IConfiguration _configuration;
private readonly AzureComponentFactory _componentFactory;
private readonly IOptionsMonitor<WorkerOptions> _workerOptions;
private readonly IOptionsMonitor<CosmosDBExtensionOptions> _cosmosExtensionOptions;

public CosmosDBBindingOptionsSetup(IConfiguration configuration, AzureComponentFactory componentFactory, IOptionsMonitor<WorkerOptions> workerOptions)
public CosmosDBBindingOptionsSetup(IConfiguration configuration, AzureComponentFactory componentFactory, IOptionsMonitor<WorkerOptions> workerOptions, IOptionsMonitor<CosmosDBExtensionOptions> cosmosExtensionOptions)
{
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
_componentFactory = componentFactory ?? throw new ArgumentNullException(nameof(componentFactory));
_workerOptions = workerOptions ?? throw new ArgumentNullException(nameof(workerOptions));
_cosmosExtensionOptions = cosmosExtensionOptions ?? throw new ArgumentNullException(nameof(cosmosExtensionOptions));
}

public void Configure(CosmosDBBindingOptions options)
Expand Down Expand Up @@ -56,7 +59,7 @@ public void Configure(string connectionName, CosmosDBBindingOptions options)
options.Credential = _componentFactory.CreateTokenCredential(connectionSection);
}

options.Serializer = new WorkerCosmosSerializer(_workerOptions.CurrentValue.Serializer);
options.CosmosExtensionOptions = _cosmosExtensionOptions.CurrentValue;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using Microsoft.Azure.Cosmos;

namespace Microsoft.Azure.Functions.Worker
{
public class CosmosDBExtensionOptions
{
/// <summary>
/// Gets or sets the CosmosClientOptions.
/// </summary>
public CosmosClientOptions ClientOptions { get; set; } = new() { ConnectionMode = ConnectionMode.Gateway };
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Reflection;
using Microsoft.Azure.Cosmos;

namespace Microsoft.Azure.Functions.Worker
{
internal static class CloningExtensions
{
public static T MemberwiseClone<T>(this T original)
{
if (original is null)
{
throw new ArgumentNullException(nameof(original));
}

// Get the type of the object
Type type = original.GetType();

// Get the internal Clone method
MethodInfo cloneMethod = type.GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);

if (cloneMethod is null)
{
throw new InvalidOperationException("The MemberwiseClone method could not be found.");
}

// Invoke the Clone method
return (T)cloneMethod.Invoke(original, null);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
Expand All @@ -27,9 +29,30 @@ public static IFunctionsWorkerApplicationBuilder ConfigureCosmosDBExtension(this

builder.Services.AddAzureClientsCore(); // Adds AzureComponentFactory
builder.Services.AddOptions<CosmosDBBindingOptions>();
builder.Services.AddOptions<CosmosDBExtensionOptions>()
.Configure<IOptions<WorkerOptions>>((cosmos, worker) =>
{
if (cosmos.ClientOptions.Serializer is null)
{
cosmos.ClientOptions.Serializer = new WorkerCosmosSerializer(worker.Value.Serializer);
}
});

builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<CosmosDBBindingOptions>, CosmosDBBindingOptionsSetup>());

return builder;
}

/// <summary>
/// Configures the CosmosDBExtensionOptions for the Functions Worker Cosmos extension.
/// </summary>
/// <param name="builder">The IFunctionsWorkerApplicationBuilder to add the configuration to.</param>
/// <param name="options">An Action to configure the CosmosDBExtensionOptions.</param>
/// <returns>The same instance of the <see cref="IFunctionsWorkerApplicationBuilder"/> for chaining.</returns>
public static IFunctionsWorkerApplicationBuilder ConfigureCosmosDBExtensionOptions(this IFunctionsWorkerApplicationBuilder builder, Action<CosmosDBExtensionOptions> options)
{
builder.Services.Configure(options);
return builder;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Description>Azure Cosmos DB extensions for .NET isolated functions</Description>

<!--Version information-->
<VersionPrefix>4.8.1</VersionPrefix>
<VersionPrefix>4.9.0</VersionPrefix>

<!--Temporarily opting out of documentation. Pending documentation-->
<GenerateDocumentationFile>false</GenerateDocumentationFile>
Expand Down

0 comments on commit ab70ee0

Please sign in to comment.