diff --git a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
index b074e6d059..8d845d9297 100644
--- a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
+++ b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
@@ -68,7 +68,6 @@ public class CosmosClientOptions
///
public CosmosClientOptions()
{
- this.UserAgentContainer = new Cosmos.UserAgentContainer();
this.GatewayModeMaxConnectionLimit = ConnectionPolicy.Default.MaxConnectionLimit;
this.RequestTimeout = ConnectionPolicy.Default.RequestTimeout;
this.ConnectionMode = CosmosClientOptions.DefaultConnectionMode;
@@ -83,11 +82,7 @@ public CosmosClientOptions()
///
/// Setting this property after sending any request won't have any effect.
///
- public string ApplicationName
- {
- get => this.UserAgentContainer.Suffix;
- set => this.UserAgentContainer.Suffix = value;
- }
+ public string ApplicationName { get; set; }
///
/// Get or set the preferred geo-replicated region to be used for Azure Cosmos DB service interaction.
@@ -398,8 +393,6 @@ internal Protocol ConnectionProtocol
}
}
- internal UserAgentContainer UserAgentContainer { get; private set; }
-
///
/// The event handler to be invoked before the request is sent.
///
@@ -509,13 +502,15 @@ internal ConnectionPolicy GetConnectionPolicy()
{
this.ValidateDirectTCPSettings();
this.ValidateLimitToEndpointSettings();
+ UserAgentContainer userAgent = this.BuildUserAgentContainer();
+
ConnectionPolicy connectionPolicy = new ConnectionPolicy()
{
MaxConnectionLimit = this.GatewayModeMaxConnectionLimit,
RequestTimeout = this.RequestTimeout,
ConnectionMode = this.ConnectionMode,
ConnectionProtocol = this.ConnectionProtocol,
- UserAgentContainer = this.UserAgentContainer,
+ UserAgentContainer = userAgent,
UseMultipleWriteLocations = true,
IdleTcpConnectionTimeout = this.IdleTcpConnectionTimeout,
OpenTcpConnectionTimeout = this.OpenTcpConnectionTimeout,
@@ -657,6 +652,40 @@ private void ValidateDirectTCPSettings()
}
}
+ internal UserAgentContainer BuildUserAgentContainer()
+ {
+ UserAgentContainer userAgent = new UserAgentContainer();
+ string features = this.GetUserAgentFeatures();
+
+ if (!string.IsNullOrEmpty(features))
+ {
+ userAgent.SetFeatures(features.ToString());
+ }
+
+ if (!string.IsNullOrEmpty(this.ApplicationName))
+ {
+ userAgent.Suffix = this.ApplicationName;
+ }
+
+ return userAgent;
+ }
+
+ private string GetUserAgentFeatures()
+ {
+ CosmosClientOptionsFeatures features = CosmosClientOptionsFeatures.NoFeatures;
+ if (this.AllowBulkExecution)
+ {
+ features |= CosmosClientOptionsFeatures.AllowBulkExecution;
+ }
+
+ if (features == CosmosClientOptionsFeatures.NoFeatures)
+ {
+ return null;
+ }
+
+ return Convert.ToString((int)features, 2).PadLeft(8, '0');
+ }
+
///
/// Serialize the current configuration into a JSON string
///
diff --git a/Microsoft.Azure.Cosmos/src/CosmosClientOptionsFeatures.cs b/Microsoft.Azure.Cosmos/src/CosmosClientOptionsFeatures.cs
new file mode 100644
index 0000000000..02eef19178
--- /dev/null
+++ b/Microsoft.Azure.Cosmos/src/CosmosClientOptionsFeatures.cs
@@ -0,0 +1,15 @@
+// ------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// ------------------------------------------------------------
+
+namespace Microsoft.Azure.Cosmos
+{
+ using System;
+
+ [Flags]
+ internal enum CosmosClientOptionsFeatures
+ {
+ NoFeatures = 0,
+ AllowBulkExecution = 1
+ }
+}
diff --git a/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs b/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
index c40617c2d6..eb70d570ae 100644
--- a/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
+++ b/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
@@ -370,7 +370,7 @@ public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSeria
/// Whether is enabled.
/// The object
///
- public CosmosClientBuilder WithBulkexecution(bool enabled)
+ public CosmosClientBuilder WithBulkExecution(bool enabled)
{
this.clientOptions.AllowBulkExecution = enabled;
return this;
diff --git a/Microsoft.Azure.Cosmos/src/UserAgentContainer.cs b/Microsoft.Azure.Cosmos/src/UserAgentContainer.cs
index 3333d304a8..0b8f597f87 100644
--- a/Microsoft.Azure.Cosmos/src/UserAgentContainer.cs
+++ b/Microsoft.Azure.Cosmos/src/UserAgentContainer.cs
@@ -31,7 +31,13 @@ internal override string BaseUserAgent
}
}
- private string CreateBaseUserAgentString()
+ internal void SetFeatures(string features)
+ {
+ // Regenerate base user agent to account for features
+ this.cosmosBaseUserAgent = this.CreateBaseUserAgentString(features);
+ }
+
+ private string CreateBaseUserAgentString(string features = null)
{
EnvironmentInformation environmentInformation = new EnvironmentInformation();
string operatingSystem = environmentInformation.OperatingSystem;
@@ -42,7 +48,14 @@ private string CreateBaseUserAgentString()
// Regex replaces all special characters with empty space except . - | since they do not cause format exception for the user agent string.
// Do not change the cosmos-netstandard-sdk as it is required for reporting
- return $"cosmos-netstandard-sdk/{environmentInformation.ClientVersion}" + Regex.Replace($"|{environmentInformation.DirectVersion}|{environmentInformation.ClientId}|{environmentInformation.ProcessArchitecture}|{operatingSystem}|{environmentInformation.RuntimeFramework}|", @"[^0-9a-zA-Z\.\|\-]+", " ");
+ string baseUserAgent = $"cosmos-netstandard-sdk/{environmentInformation.ClientVersion}" + Regex.Replace($"|{environmentInformation.DirectVersion}|{environmentInformation.ClientId}|{environmentInformation.ProcessArchitecture}|{operatingSystem}|{environmentInformation.RuntimeFramework}|", @"[^0-9a-zA-Z\.\|\-]+", " ");
+
+ if (!string.IsNullOrEmpty(features))
+ {
+ baseUserAgent += $"F {features}|";
+ }
+
+ return baseUserAgent;
}
}
}
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/UserAgentTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/UserAgentTests.cs
index 6d207ffad5..8d728b43ba 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/UserAgentTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/UserAgentTests.cs
@@ -102,6 +102,44 @@ public void VerifyUserAgentContent(bool useMacOs)
Assert.AreEqual(envInfo.RuntimeFramework, values[5]);
}
+ [TestMethod]
+ [DataRow(true)]
+ [DataRow(false)]
+ public async Task VerifyUserAgentWithFeatures(bool useMacOs)
+ {
+ this.SetEnvironmentInformation(useMacOs);
+
+ const string suffix = " UserApplicationName/1.0";
+
+ string features = Convert.ToString((int)CosmosClientOptionsFeatures.AllowBulkExecution, 2).PadLeft(8, '0');
+
+ using (CosmosClient client = TestCommon.CreateCosmosClient(builder => builder.WithApplicationName(suffix).WithBulkExecution(true)))
+ {
+ Cosmos.UserAgentContainer userAgentContainer = client.ClientOptions.GetConnectionPolicy().UserAgentContainer;
+
+ string userAgentString = userAgentContainer.UserAgent;
+ Assert.IsTrue(userAgentString.Contains(suffix));
+ Assert.IsTrue(userAgentString.Contains($"|F {features}"));
+ if (useMacOs)
+ {
+ Assert.IsTrue(userAgentString.Contains("Darwin 18.0.0"));
+ }
+
+ Cosmos.Database db = await client.CreateDatabaseIfNotExistsAsync(Guid.NewGuid().ToString());
+ Assert.IsNotNull(db);
+ await db.DeleteAsync();
+ }
+
+ using (CosmosClient client = TestCommon.CreateCosmosClient(builder => builder.WithApplicationName(suffix).WithBulkExecution(false)))
+ {
+ Cosmos.UserAgentContainer userAgentContainer = client.ClientOptions.GetConnectionPolicy().UserAgentContainer;
+
+ string userAgentString = userAgentContainer.UserAgent;
+ Assert.IsTrue(userAgentString.Contains(suffix));
+ Assert.IsFalse(userAgentString.Contains($"|F {features}"));
+ }
+ }
+
private void SetEnvironmentInformation(bool useMacOs)
{
//This changes the runtime information to simulate a max os x response. Windows user agent are tested by every other emulator test.
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
index 3bc589aabf..3400a62a6f 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
@@ -89,7 +89,7 @@ public void VerifyCosmosConfigurationPropertiesGetUpdated()
.AddCustomHandlers(preProcessHandler)
.WithApiType(apiType)
.WithThrottlingRetryOptions(maxRetryWaitTime, maxRetryAttemptsOnThrottledRequests)
- .WithBulkexecution(true)
+ .WithBulkExecution(true)
.WithSerializerOptions(cosmosSerializerOptions);
cosmosClient = cosmosClientBuilder.Build(new MockDocumentClient());
@@ -180,9 +180,10 @@ public void UserAgentContainsEnvironmentInformation()
string userAgentSuffix = "testSuffix";
cosmosClientOptions.ApplicationName = userAgentSuffix;
Assert.AreEqual(userAgentSuffix, cosmosClientOptions.ApplicationName);
- Assert.AreEqual(userAgentSuffix, cosmosClientOptions.UserAgentContainer.Suffix);
- Assert.IsTrue(cosmosClientOptions.UserAgentContainer.UserAgent.StartsWith(expectedValue));
- Assert.IsTrue(cosmosClientOptions.UserAgentContainer.UserAgent.EndsWith(userAgentSuffix));
+ UserAgentContainer userAgentContainer = cosmosClientOptions.BuildUserAgentContainer();
+ Assert.AreEqual(userAgentSuffix, userAgentContainer.Suffix);
+ Assert.IsTrue(userAgentContainer.UserAgent.StartsWith(expectedValue));
+ Assert.IsTrue(userAgentContainer.UserAgent.EndsWith(userAgentSuffix));
ConnectionPolicy connectionPolicy = cosmosClientOptions.GetConnectionPolicy();
Assert.AreEqual(userAgentSuffix, connectionPolicy.UserAgentSuffix);
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/DotNetSDKAPI.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/DotNetSDKAPI.json
index 61a51c246f..d7de0a7688 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/DotNetSDKAPI.json
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/DotNetSDKAPI.json
@@ -1415,9 +1415,11 @@
"Attributes": [],
"MethodInfo": null
},
- "System.String get_ApplicationName()": {
+ "System.String get_ApplicationName()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
"Type": "Method",
- "Attributes": [],
+ "Attributes": [
+ "CompilerGeneratedAttribute"
+ ],
"MethodInfo": "System.String get_ApplicationName()"
},
"System.String get_ApplicationRegion()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
@@ -1451,9 +1453,11 @@
],
"MethodInfo": "Void set_AllowBulkExecution(Boolean)"
},
- "Void set_ApplicationName(System.String)": {
+ "Void set_ApplicationName(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
"Type": "Method",
- "Attributes": [],
+ "Attributes": [
+ "CompilerGeneratedAttribute"
+ ],
"MethodInfo": "Void set_ApplicationName(System.String)"
},
"Void set_ApplicationRegion(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
@@ -2390,10 +2394,10 @@
"Attributes": [],
"MethodInfo": "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithApplicationRegion(System.String)"
},
- "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithBulkexecution(Boolean)": {
+ "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithBulkExecution(Boolean)": {
"Type": "Method",
"Attributes": [],
- "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithBulkexecution(Boolean)"
+ "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithBulkExecution(Boolean)"
},
"Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithConnectionModeDirect()": {
"Type": "Method",
diff --git a/changelog.md b/changelog.md
index cc85828149..8053d760d5 100644
--- a/changelog.md
+++ b/changelog.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- [#923](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/923) Bulk Support is now public
+- [#922](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/922) Included information of bulk support usage in user agent
## [3.3.2](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.3.2) - 2019-10-16