//------------------------------------------------------------ // 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; } } }