//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Fluent
{
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using global::Azure;
using global::Azure.Core;
using Microsoft.Azure.Cosmos.Core.Trace;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
///
/// This is a Builder class that creates a cosmos client
///
public class CosmosClientBuilder
{
private readonly CosmosClientOptions clientOptions = new CosmosClientOptions();
private readonly string accountEndpoint;
private readonly string accountKey;
private readonly AzureKeyCredential azureKeyCredential;
private readonly TokenCredential tokenCredential;
///
/// Initialize a new CosmosConfiguration class that holds all the properties the CosmosClient requires.
///
/// The Uri to the Cosmos Account. Example: https://{Cosmos Account Name}.documents.azure.com:443/
/// The key to the account or resource token.
///
/// The example below creates a new
///
///
///
///
///
/// The example below creates a new with a ConsistencyLevel and a list of preferred locations.
///
///
///
///
public CosmosClientBuilder(
string accountEndpoint,
string authKeyOrResourceToken)
{
if (string.IsNullOrEmpty(accountEndpoint))
{
throw new ArgumentNullException(nameof(CosmosClientBuilder.accountEndpoint));
}
if (string.IsNullOrEmpty(authKeyOrResourceToken))
{
throw new ArgumentNullException(nameof(authKeyOrResourceToken));
}
this.accountEndpoint = accountEndpoint;
this.accountKey = authKeyOrResourceToken;
}
///
/// Initialize a new CosmosConfiguration class that holds all the properties the CosmosClient requires with the account endpoint URI string and AzureKeyCredential.
/// AzureKeyCredential enables changing/updating master-key/ResourceToken while CosmosClient is still in use.
///
///
/// The Uri to the Cosmos Account. Example: https://{Cosmos Account Name}.documents.azure.com:443/
/// AzureKeyCredential with master-key or resource token.
///
/// The example below creates a new
///
///
///
///
///
/// The example below creates a new with a ConsistencyLevel and a list of preferred locations.
///
///
///
///
/// AzureKeyCredential enables changing/updating master-key/ResourceToken whle CosmosClient is still in use.
public CosmosClientBuilder(
string accountEndpoint,
AzureKeyCredential authKeyOrResourceTokenCredential)
{
if (string.IsNullOrEmpty(accountEndpoint))
{
throw new ArgumentNullException(nameof(CosmosClientBuilder.accountEndpoint));
}
this.accountEndpoint = accountEndpoint;
this.azureKeyCredential = authKeyOrResourceTokenCredential ?? throw new ArgumentNullException(nameof(authKeyOrResourceTokenCredential));
}
///
/// Extracts the account endpoint and key from the connection string.
///
/// "AccountEndpoint=https://mytestcosmosaccount.documents.azure.com:443/;AccountKey={SecretAccountKey};"
/// The connection string must contain AccountEndpoint and AccountKey or ResourceToken.
///
/// Emulator: To ignore SSL Certificate please suffix connectionstring with "DisableServerCertificateValidation=True;".
/// When CosmosClientOptions.HttpClientFactory is used, SSL certificate needs to be handled appropriately.
/// NOTE: DO NOT use this flag in production (only for emulator)
///
public CosmosClientBuilder(string connectionString)
{
if (connectionString == null)
{
throw new ArgumentNullException(nameof(connectionString));
}
this.accountEndpoint = CosmosClientOptions.GetAccountEndpoint(connectionString);
this.accountKey = CosmosClientOptions.GetAccountKey(connectionString);
this.clientOptions = CosmosClientOptions.GetCosmosClientOptionsWithCertificateFlag(connectionString, this.clientOptions);
}
///
/// Initializes a new with a instance.
///
/// The Uri to the Cosmos Account. Example: https://{Cosmos Account Name}.documents.azure.com:443/
/// An instance of
///
/// The example below creates a new using a .
///
///
///
///
public CosmosClientBuilder(
string accountEndpoint,
TokenCredential tokenCredential)
{
if (string.IsNullOrEmpty(accountEndpoint))
{
throw new ArgumentNullException(nameof(CosmosClientBuilder.accountEndpoint));
}
this.accountEndpoint = accountEndpoint;
this.tokenCredential = tokenCredential ?? throw new ArgumentNullException(nameof(CosmosClientBuilder.tokenCredential));
}
///
/// A method to create the cosmos client
/// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime
/// of the application which enables efficient connection management and performance. Please refer to the
/// performance guide.
///
///
/// Setting this property after sending any request won't have any effect.
/// The returned reference doesn't guarantee credentials or connectivity validations because creation doesn't make any network calls.
///
/// An instance of .
public CosmosClient Build()
{
DefaultTrace.TraceInformation($"CosmosClientBuilder.Build with configuration: {this.clientOptions.GetSerializedConfiguration()}");
if (this.tokenCredential != null)
{
return new CosmosClient(this.accountEndpoint, this.tokenCredential, this.clientOptions);
}
if (this.azureKeyCredential != null)
{
return new CosmosClient(this.accountEndpoint, this.azureKeyCredential, this.clientOptions);
}
return new CosmosClient(this.accountEndpoint, this.accountKey, this.clientOptions);
}
///
/// A method to create the cosmos client and initialize the provided containers.
/// In addition to that it initializes the client with containers provided i.e The SDK warms up the caches and
/// connections before the first call to the service is made. Use this to obtain lower latency while startup of your application.
/// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime
/// of the application which enables efficient connection management and performance. Please refer to the
/// performance guide.
///
/// Containers to be initialized identified by it's database name and container name.
/// (Optional) Cancellation Token
///
/// A CosmosClient object.
///
public Task BuildAndInitializeAsync(IReadOnlyList<(string databaseId, string containerId)> containers, CancellationToken cancellationToken = default)
{
if (this.tokenCredential != null)
{
return CosmosClient.CreateAndInitializeAsync(this.accountEndpoint, this.tokenCredential, containers, this.clientOptions, cancellationToken);
}
if (this.azureKeyCredential != null)
{
return CosmosClient.CreateAndInitializeAsync(this.accountEndpoint, this.azureKeyCredential, containers, this.clientOptions, cancellationToken);
}
return CosmosClient.CreateAndInitializeAsync(this.accountEndpoint, this.accountKey, containers, this.clientOptions, cancellationToken);
}
///
/// A method to create the cosmos client
/// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime
/// of the application which enables efficient connection management and performance. Please refer to the
/// performance guide.
///
///
/// Setting this property after sending any request won't have any effect.
/// The returned reference doesn't guarantee credentials or connectivity validations because creation doesn't make any network calls.
///
internal virtual CosmosClient Build(DocumentClient documentClient)
{
DefaultTrace.TraceInformation($"CosmosClientBuilder.Build(DocumentClient) with configuration: {this.clientOptions.GetSerializedConfiguration()}");
return new CosmosClient(this.accountEndpoint, this.accountKey, this.clientOptions, documentClient);
}
///
/// A suffix to be added to the default user-agent for the Azure Cosmos DB service.
///
/// A string to use as suffix in the User Agent.
///
/// Setting this property after sending any request won't have any effect.
///
/// The current .
public CosmosClientBuilder WithApplicationName(string applicationName)
{
this.clientOptions.ApplicationName = applicationName;
return this;
}
///
/// Set the preferred geo-replicated region to be used in the Azure Cosmos DB service.
///
/// Azure region where application is running. lists valid Cosmos DB regions.
///
/// The example below creates a new with a of preferred region.
///
///
///
///
/// The current .
///
public CosmosClientBuilder WithApplicationRegion(string applicationRegion)
{
this.clientOptions.ApplicationRegion = applicationRegion;
return this;
}
///
/// Set the preferred regions for geo-replicated database accounts in the Azure Cosmos DB service.
///
/// A list of preferred Azure regions used for SDK to define failover order.
///
/// This function is an alternative to , either one can be set but not both.
///
///
/// The example below creates a new with a of preferred regions.
///
///
///
///
/// The current .
///
public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions)
{
this.clientOptions.ApplicationPreferredRegions = applicationPreferredRegions;
return this;
}
///
/// Sets the custom endpoints to use for account initialization for geo-replicated database accounts in the Azure Cosmos DB service.
/// During the CosmosClient initialization the account information, including the available regions, is obtained from the .
/// Should the global endpoint become inaccessible, the CosmosClient will attempt to obtain the account information issuing requests to the custom endpoints
/// provided in the customAccountEndpoints list.
///
/// An instance of of string containing the custom private endpoints for the cosmos db account.
///
/// This function is optional and is recommended for implementation when a customer has configured one or more endpoints with a custom DNS
/// hostname (instead of accountname-region.documents.azure.com) etc. for their Cosmos DB account.
///
///
/// The example below creates a new instance of with the regional endpoints.
///
/// () { "https://region-1.documents-test.windows-int.net:443/", "https://region-2.documents-test.windows-int.net:443/" });
/// CosmosClient client = cosmosClientBuilder.Build();
/// ]]>
///
///
/// The current .
///
public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints)
{
this.clientOptions.AccountInitializationCustomEndpoints = customAccountEndpoints;
return this;
}
///
/// Limits the operations to the provided endpoint on the CosmosClientBuilder constructor.
///
/// Whether operations are limited to the endpoint or not.
/// Default value is false.
///
/// When the value of is false, the SDK will automatically discover all account write and read regions, and use them when the configured application region is not available.
/// When set to true, availability is limited to the endpoint specified on the CosmosClientBuilder constructor.
/// Using is not allowed when the value is true.
///
/// The example below creates a new to limit the endpoint to East US.
///
///
///
///
/// The current .
/// High availability
///
public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint)
{
this.clientOptions.LimitToEndpoint = limitToEndpoint;
return this;
}
///
/// Sets the request timeout in seconds when connecting to the Azure Cosmos DB service.
///
/// A time to use as timeout for operations.
/// Default value is 60 seconds.
/// The current .
///
public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout)
{
this.clientOptions.RequestTimeout = requestTimeout;
return this;
}
///
/// Sets the connection mode to Direct. This is used by the client when connecting to the Azure Cosmos DB service.
///
///
/// For more information, see Connection policy: Use direct connection mode.
///
/// The current .
///
public CosmosClientBuilder WithConnectionModeDirect()
{
this.clientOptions.ConnectionMode = ConnectionMode.Direct;
this.clientOptions.ConnectionProtocol = Protocol.Tcp;
return this;
}
///
/// Sets the connection mode to Direct. This is used by the client when connecting to the Azure Cosmos DB service.
///
///
/// Controls the amount of idle time after which unused connections are closed.
/// By default, idle connections are kept open indefinitely. Value must be greater than or equal to 10 minutes. Recommended values are between 20 minutes and 24 hours.
/// Mainly useful for sparse infrequent access to a large database account.
///
///
/// Controls the amount of time allowed for trying to establish a connection.
/// The default timeout is 5 seconds. Recommended values are greater than or equal to 5 seconds.
/// When the time elapses, the attempt is cancelled and an error is returned. Longer timeouts will delay retries and failures.
///
///
/// Controls the number of requests allowed simultaneously over a single TCP connection. When more requests are in flight simultaneously, the direct/TCP client will open additional connections.
/// The default settings allow 30 simultaneous requests per connection.
/// Do not set this value lower than 4 requests per connection or higher than 50-100 requests per connection.
/// The former can lead to a large number of connections to be created.
/// The latter can lead to head of line blocking, high latency and timeouts.
/// Applications with a very high degree of parallelism per connection, with large requests or responses, or with very tight latency requirements might get better performance with 8-16 requests per connection.
///
///
/// Controls the maximum number of TCP connections that may be opened to each Cosmos DB back-end.
/// Together with MaxRequestsPerTcpConnection, this setting limits the number of requests that are simultaneously sent to a single Cosmos DB back-end(MaxRequestsPerTcpConnection x MaxTcpConnectionPerEndpoint).
/// The default value is 65,535. Value must be greater than or equal to 16.
///
///
/// (Direct/TCP) Controls the client port reuse policy used by the transport stack.
/// The default value is PortReuseMode.ReuseUnicastPort.
///
/// ///
/// (Direct/TCP) Controls the address cache refresh on TCP connection reset notification.
/// The default value is false.
///
///
/// For more information, see Connection policy: Use direct connection mode.
///
/// The current .
///
public CosmosClientBuilder WithConnectionModeDirect(TimeSpan? idleTcpConnectionTimeout = null,
TimeSpan? openTcpConnectionTimeout = null,
int? maxRequestsPerTcpConnection = null,
int? maxTcpConnectionsPerEndpoint = null,
Cosmos.PortReuseMode? portReuseMode = null,
bool? enableTcpConnectionEndpointRediscovery = null)
{
this.clientOptions.IdleTcpConnectionTimeout = idleTcpConnectionTimeout;
this.clientOptions.OpenTcpConnectionTimeout = openTcpConnectionTimeout;
this.clientOptions.MaxRequestsPerTcpConnection = maxRequestsPerTcpConnection;
this.clientOptions.MaxTcpConnectionsPerEndpoint = maxTcpConnectionsPerEndpoint;
this.clientOptions.PortReuseMode = portReuseMode;
if (enableTcpConnectionEndpointRediscovery.HasValue)
{
this.clientOptions.EnableTcpConnectionEndpointRediscovery = enableTcpConnectionEndpointRediscovery.Value;
}
this.clientOptions.ConnectionMode = ConnectionMode.Direct;
this.clientOptions.ConnectionProtocol = Protocol.Tcp;
return this;
}
///
/// This can be used to weaken the database account consistency level for read operations.
/// If this is not set the database account consistency level will be used for all requests.
///
/// The desired consistency level for the client.
/// The current .
public CosmosClientBuilder WithConsistencyLevel(Cosmos.ConsistencyLevel consistencyLevel)
{
this.clientOptions.ConsistencyLevel = consistencyLevel;
return this;
}
///
/// Sets the connection mode to Gateway. This is used by the client when connecting to the Azure Cosmos DB service.
///
/// The number specifies the number of connections that may be opened simultaneously. Default is 50 connections
/// Get or set the proxy information used for web requests.
///
/// For more information, see Connection policy: Use direct connection mode.
///
/// The current .
///
///
public CosmosClientBuilder WithConnectionModeGateway(
int? maxConnectionLimit = null,
IWebProxy webProxy = null)
{
this.clientOptions.ConnectionMode = ConnectionMode.Gateway;
this.clientOptions.ConnectionProtocol = Protocol.Https;
if (maxConnectionLimit.HasValue)
{
this.clientOptions.GatewayModeMaxConnectionLimit = maxConnectionLimit.Value;
}
this.clientOptions.WebProxy = webProxy;
return this;
}
///
/// Sets an array of custom handlers to the request. The handlers will be chained in
/// the order listed. The InvokerHandler.InnerHandler is required to be null to allow the
/// pipeline to chain the handlers.
///
/// The current .
/// A list of instaces to add to the pipeline.
///
public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers)
{
foreach (RequestHandler handler in customHandlers)
{
if (handler.InnerHandler != null)
{
throw new ArgumentException(nameof(customHandlers) + " requires all DelegatingHandler.InnerHandler to be null. The CosmosClient uses the inner handler in building the pipeline.");
}
this.clientOptions.CustomHandlers.Add(handler);
}
return this;
}
///
/// Sets the maximum time to wait between retry and the max number of times to retry on throttled requests.
///
/// The maximum retry timespan for the Azure Cosmos DB service. Any interval that is smaller than a second will be ignored.
/// The number specifies the times retry requests for throttled requests.
///
/// When a request fails due to a rate limiting error, the service sends back a response that
/// contains a value indicating the client should not retry before the time period has
/// elapsed. This property allows the application to set a maximum wait time for all retry attempts.
/// If the cumulative wait time exceeds the this value, the client will stop retrying and return the error to the application.
///
///
/// For more information, see Handle rate limiting/request rate too large.
///
/// The current .
///
///
public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests,
int maxRetryAttemptsOnThrottledRequests)
{
this.clientOptions.MaxRetryWaitTimeOnRateLimitedRequests = maxRetryWaitTimeOnThrottledRequests;
this.clientOptions.MaxRetryAttemptsOnRateLimitedRequests = maxRetryAttemptsOnThrottledRequests;
return this;
}
///
/// Set a custom serializer option.
///
/// The custom class that implements
/// The object
///
///
public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions)
{
this.clientOptions.SerializerOptions = cosmosSerializerOptions;
return this;
}
///
/// Set a custom JSON serializer.
///
/// The custom class that implements
/// The object
///
///
public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer)
{
this.clientOptions.Serializer = cosmosJsonSerializer;
return this;
}
///
/// Allows optimistic batching of requests to service. Setting this option might impact the latency of the operations. Hence this option is recommended for non-latency sensitive scenarios only.
///
/// Whether is enabled.
/// The object
///
public CosmosClientBuilder WithBulkExecution(bool enabled)
{
this.clientOptions.AllowBulkExecution = enabled;
return this;
}
///
/// Sets a delegate to use to obtain an HttpClient instance to be used for HTTPS communication.
///
/// A delegate function to generate instances of HttpClient.
///
///
/// HTTPS communication is used when is set to for all operations and when is (default) for metadata operations.
///
///
/// Useful in scenarios where the application is using a pool of HttpClient instances to be shared, like ASP.NET Core applications with IHttpClientFactory or Blazor WebAssembly applications.
///
///
/// The object
///
public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory)
{
this.clientOptions.HttpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
return this;
}
///
/// Gets or sets the boolean to only return the headers and status code in
/// the Cosmos DB response for write item operation like Create, Upsert, Patch and Replace.
/// Setting the option to false will cause the response to have a null resource. This reduces networking and CPU load by not sending
/// the resource back over the network and serializing it on the client.
///
/// a boolean indicating whether payload will be included in the response or not.
///
///
/// This option can be overriden by similar property in ItemRequestOptions and TransactionalBatchItemRequestOptions
///
///
/// The object
///
///
public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite)
{
this.clientOptions.EnableContentResponseOnWrite = contentResponseOnWrite;
return this;
}
///
/// The event handler to be invoked before the request is sent.
///
internal CosmosClientBuilder WithSendingRequestEventArgs(EventHandler sendingRequestEventArgs)
{
this.clientOptions.SendingRequestEventArgs = sendingRequestEventArgs;
return this;
}
///
/// Sets the ambient Session Container to use for this CosmosClient.
/// This is used to track session tokens per client for requests made to the store.
///
internal CosmosClientBuilder WithSessionContainer(ISessionContainer sessionContainer)
{
this.clientOptions.SessionContainer = sessionContainer;
return this;
}
///
/// (Optional) transport interceptor factory
///
internal CosmosClientBuilder WithTransportClientHandlerFactory(Func transportClientHandlerFactory)
{
this.clientOptions.TransportClientHandlerFactory = transportClientHandlerFactory;
return this;
}
///
/// ApiType for the account
///
internal CosmosClientBuilder WithApiType(ApiType apiType)
{
this.clientOptions.ApiType = apiType;
return this;
}
///
/// Specify a store client factory to use for all transport requests for cosmos client.
///
///
/// This method enables transport client sharing among multiple cosmos client instances inside a single process.
///
/// Instance of store client factory to use to create transport client for an instance of cosmos client.
internal CosmosClientBuilder WithStoreClientFactory(IStoreClientFactory storeClientFactory)
{
this.clientOptions.StoreClientFactory = storeClientFactory;
return this;
}
///
/// Disables CPU monitoring for transport client which will inhibit troubleshooting of timeout exceptions.
///
internal CosmosClientBuilder WithCpuMonitorDisabled()
{
this.clientOptions.EnableCpuMonitor = false;
return this;
}
///
/// Enabled partition level failover in the SDK
///
internal CosmosClientBuilder WithPartitionLevelFailoverEnabled()
{
this.clientOptions.EnablePartitionLevelFailover = true;
return this;
}
///
/// Enables SDK to inject fault. Used for testing applications.
///
///
internal CosmosClientBuilder WithFaultInjection(IChaosInterceptorFactory chaosInterceptorFactory)
{
this.clientOptions.ChaosInterceptorFactory = chaosInterceptorFactory;
return this;
}
///
/// To enable LocalQuorum Consistency, i.e. Allows Quorum read with Eventual Consistency Account or with Consistent Prefix Account.
/// Use By Compute Only
///
internal CosmosClientBuilder AllowUpgradeConsistencyToLocalQuorum()
{
this.clientOptions.EnableUpgradeConsistencyToLocalQuorum = true;
return this;
}
internal CosmosClientBuilder WithRetryWithOptions(
int? initialRetryForRetryWithMilliseconds,
int? maximumRetryForRetryWithMilliseconds,
int? randomSaltForRetryWithMilliseconds,
int? totalWaitTimeForRetryWithMilliseconds)
{
this.clientOptions.InitialRetryForRetryWithMilliseconds = initialRetryForRetryWithMilliseconds;
this.clientOptions.MaximumRetryForRetryWithMilliseconds = maximumRetryForRetryWithMilliseconds;
this.clientOptions.RandomSaltForRetryWithMilliseconds = randomSaltForRetryWithMilliseconds;
this.clientOptions.TotalWaitTimeForRetryWithMilliseconds = totalWaitTimeForRetryWithMilliseconds;
return this;
}
///
/// To enable Telemetry features with corresponding options
///
///
/// The object
public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options)
{
this.clientOptions.CosmosClientTelemetryOptions = options;
return this;
}
}
}