diff --git a/.pipelines/templates/build-pipelines.yml b/.pipelines/templates/build-pipelines.yml
index ae9c7c7684..cd604917ef 100644
--- a/.pipelines/templates/build-pipelines.yml
+++ b/.pipelines/templates/build-pipelines.yml
@@ -14,6 +14,22 @@ steps:
# generate the prerelease nuget version.
# We cannot set this in variables section above because $(isNugetRelease)
# is not available at pipeline compilation time.
+- task: PowerShell@2
+ inputs:
+ targetType: 'inline'
+ script: |
+ [xml]$directoryBuildProps = Get-Content -Path $(Build.SourcesDirectory)/src/Directory.Build.props
+ # Get Version from Directory.Build.props
+ # When you access this XML property, it returns an array of elements (even if there's only one element with that name).
+ # To extract the actual value as a string, you need to access the first element of the array.
+ $version = $directoryBuildProps.Project.PropertyGroup.Version[0]
+ # Get Major and Minor version from the version extracted
+ $major = $version.Split([char]'.')[0]
+ $minor = $version.Split([char]'.')[1]
+ # store $major and $minor powershell variables into azure dev ops pipeline variables
+ Write-Host "##vso[task.setvariable variable=major]$major"
+ Write-Host "##vso[task.setvariable variable=minor]$minor"
+
- bash: |
echo IsNugetRelease = $ISNUGETRELEASE
echo IsReleaseCandidate = $ISRELEASECANDIDATE
diff --git a/.pipelines/templates/variables.yml b/.pipelines/templates/variables.yml
index 5f6ed4937c..f9c88132db 100644
--- a/.pipelines/templates/variables.yml
+++ b/.pipelines/templates/variables.yml
@@ -5,8 +5,6 @@ variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
- major: 0
- minor: 11
# Maintain a separate patch value between CI and PR runs.
# The counter is reset when the minor version is updated.
patch: $[counter(format('{0}_{1}', variables['build.reason'], variables['minor']), 0)]
diff --git a/src/Cli.Tests/EndToEndTests.cs b/src/Cli.Tests/EndToEndTests.cs
index eb6e863c46..806c36237e 100644
--- a/src/Cli.Tests/EndToEndTests.cs
+++ b/src/Cli.Tests/EndToEndTests.cs
@@ -122,7 +122,7 @@ public void TestInitializingRestAndGraphQLGlobalSettings()
replaceEnvVar: true));
SqlConnectionStringBuilder builder = new(runtimeConfig.DataSource.ConnectionString);
- Assert.AreEqual(ProductInfo.GetDataApiBuilderApplicationName(), builder.ApplicationName);
+ Assert.AreEqual(ProductInfo.GetDataApiBuilderUserAgent(), builder.ApplicationName);
Assert.IsNotNull(runtimeConfig);
Assert.AreEqual(DatabaseType.MSSQL, runtimeConfig.DataSource.DatabaseType);
@@ -771,22 +771,19 @@ public void TestMissingEntityFromCommand(
///
/// Test to verify that help writer window generates output on the console.
+ /// Every test here validates that the first line of the output contains the product name and version.
///
[DataTestMethod]
[DataRow("", "", new string[] { "ERROR" }, DisplayName = "No flags provided.")]
[DataRow("initialize", "", new string[] { "ERROR", "Verb 'initialize' is not recognized." }, DisplayName = "Wrong Command provided.")]
- [DataRow("", "--version", new string[] { "Microsoft.DataApiBuilder 1.0.0" }, DisplayName = "Checking version.")]
[DataRow("", "--help", new string[] { "init", "add", "update", "start" }, DisplayName = "Checking output for --help.")]
public void TestHelpWriterOutput(string command, string flags, string[] expectedOutputArray)
{
- using Process process = ExecuteDabCommand(
- command,
- flags
- );
+ using Process process = ExecuteDabCommand(command, flags);
string? output = process.StandardOutput.ReadToEnd();
Assert.IsNotNull(output);
- StringAssert.Contains(output, $"{Program.PRODUCT_NAME} {ProductInfo.GetProductVersion()}", StringComparison.Ordinal);
+ StringAssert.Contains(output, $"{Program.PRODUCT_NAME} {ProductInfo.GetProductVersion(includeCommitHash: true)}", StringComparison.Ordinal);
foreach (string expectedOutput in expectedOutputArray)
{
@@ -796,24 +793,25 @@ public void TestHelpWriterOutput(string command, string flags, string[] expected
process.Kill();
}
- [DataRow("", "--version", DisplayName = "Checking dab version with --version.")]
- [DataTestMethod]
- public void TestVersionHasBuildHash(
- string command,
- string options
- )
+ ///
+ /// When CLI is started via: dab --version, it should print the version number
+ /// which includes the commit hash. For example:
+ /// Microsoft.DataApiBuilder 0.12+2d181463e5dd46cf77fea31b7295c4e02e8ef031
+ ///
+ [TestMethod]
+ public void TestVersionHasBuildHash()
{
_fileSystem!.File.WriteAllText(TEST_RUNTIME_CONFIG_FILE, INITIAL_CONFIG);
using Process process = ExecuteDabCommand(
- command: $"{command} ",
- flags: $"--config {TEST_RUNTIME_CONFIG_FILE} {options}"
+ command: string.Empty,
+ flags: $"--config {TEST_RUNTIME_CONFIG_FILE} --version"
);
string? output = process.StandardOutput.ReadLine();
Assert.IsNotNull(output);
- // Check that build hash is returned as part of version number
+ // Check that the build hash is returned as part of the version number.
string[] versionParts = output.Split('+');
Assert.AreEqual(2, versionParts.Length, "Build hash not returned as part of version number.");
Assert.AreEqual(40, versionParts[1].Length, "Build hash is not of expected length.");
@@ -822,21 +820,17 @@ string options
}
///
- /// Test to verify that the version info is logged for both correct/incorrect command,
- /// and that the config name is displayed in the logs.
+ /// For valid CLI commands (Valid verbs and options) validate that the correct
+ /// version is logged (without commit hash) and that the config file name is printed to console.
///
- [DataRow("", "--version", false, DisplayName = "Checking dab version with --version.")]
- [DataRow("", "--help", false, DisplayName = "Checking version through --help option.")]
- [DataRow("edit", "--new-option", false, DisplayName = "Version printed with invalid command edit.")]
- [DataRow("init", "--database-type mssql", true, DisplayName = "Version printed with valid command init.")]
- [DataRow("add", "MyEntity -s my_entity --permissions \"anonymous:*\"", true, DisplayName = "Version printed with valid command add.")]
- [DataRow("update", "MyEntity -s my_entity", true, DisplayName = "Version printed with valid command update.")]
- [DataRow("start", "", true, DisplayName = "Version printed with valid command start.")]
+ [DataRow("init", "--database-type mssql", DisplayName = "Version printed with valid command init.")]
+ [DataRow("add", "MyEntity -s my_entity --permissions \"anonymous:*\"", DisplayName = "Version printed with valid command add.")]
+ [DataRow("update", "MyEntity -s my_entity", DisplayName = "Version printed with valid command update.")]
+ [DataRow("start", "", DisplayName = "Version printed with valid command start.")]
[DataTestMethod]
- public void TestVersionInfoAndConfigIsCorrectlyDisplayedWithDifferentCommand(
+ public void ValidCliVerbsAndOptions_DisplayVersionAndConfigFileName(
string command,
- string options,
- bool isParsableDabCommandName)
+ string options)
{
_fileSystem!.File.WriteAllText(TEST_RUNTIME_CONFIG_FILE, INITIAL_CONFIG);
@@ -849,13 +843,45 @@ public void TestVersionInfoAndConfigIsCorrectlyDisplayedWithDifferentCommand(
Assert.IsNotNull(output);
// Version Info logged by dab irrespective of commands being parsed correctly.
+ // When DAB CLI (CommandLineParser) detects that usage of parsable verbs and options, CommandLineParser
+ // uses the options.Handler() method to display relevant info and process the command.
+ // All options.Handler() methods print the version without commit hash.
StringAssert.Contains(output, $"{Program.PRODUCT_NAME} {ProductInfo.GetProductVersion()}", StringComparison.Ordinal);
+ output = process.StandardOutput.ReadLine();
+ StringAssert.Contains(output, TEST_RUNTIME_CONFIG_FILE, StringComparison.Ordinal);
- if (isParsableDabCommandName)
- {
- output = process.StandardOutput.ReadLine();
- StringAssert.Contains(output, TEST_RUNTIME_CONFIG_FILE, StringComparison.Ordinal);
- }
+ process.Kill();
+ }
+
+ ///
+ /// Validate that the DAB CLI logs the correct version (with commit hash) to the console
+ /// when invalid verbs, '--version', or '--help' are used.
+ /// Console Output:
+ /// Microsoft.DataApiBuilder 0.12+5009b8720409ab321fd8cd19c716835528c8385b
+ ///
+ [DataRow("", "--version", DisplayName = "Checking dab version with --version.")]
+ [DataRow("", "--help", DisplayName = "Checking version through --help option.")]
+ [DataRow("edit", "--new-option", DisplayName = "Version printed with invalid command edit.")]
+ [DataTestMethod]
+ public void InvalidCliVerbsAndOptions_DisplayVersionWithCommitHashAndConfigFileName(
+ string command,
+ string options)
+ {
+ _fileSystem!.File.WriteAllText(TEST_RUNTIME_CONFIG_FILE, INITIAL_CONFIG);
+
+ using Process process = ExecuteDabCommand(
+ command: $"{command} ",
+ flags: $"--config {TEST_RUNTIME_CONFIG_FILE} {options}"
+ );
+
+ string? output = process.StandardOutput.ReadLine();
+ Assert.IsNotNull(output);
+
+ // Version Info logged by dab irrespective of commands being parsed correctly.
+ // When DAB CLI (CommandLineParser) detects that usage includes --version, --help, or invalid verbs/options,
+ // CommandLineParser's default HelpWriter is used to display the version and help information.
+ // Because the HelpWriter uses fileVersionInfo.ProductVersion, the version includes the commit hash.
+ StringAssert.Contains(output, $"{Program.PRODUCT_NAME} {ProductInfo.GetProductVersion(includeCommitHash: true)}", StringComparison.Ordinal);
process.Kill();
}
@@ -882,7 +908,7 @@ public async Task TestExitOfRuntimeEngineWithInvalidConfig(
);
string? output = await process.StandardOutput.ReadLineAsync();
Assert.IsNotNull(output);
- StringAssert.Contains(output, $"{Program.PRODUCT_NAME} {ProductInfo.GetProductVersion()}", StringComparison.Ordinal);
+ StringAssert.Contains(output, $"{Program.PRODUCT_NAME} {ProductInfo.GetProductVersion(includeCommitHash: true)}", StringComparison.Ordinal);
output = await process.StandardOutput.ReadLineAsync();
Assert.IsNotNull(output);
diff --git a/src/Config/RuntimeConfigLoader.cs b/src/Config/RuntimeConfigLoader.cs
index f56db4bfa5..57b92bedac 100644
--- a/src/Config/RuntimeConfigLoader.cs
+++ b/src/Config/RuntimeConfigLoader.cs
@@ -199,7 +199,7 @@ internal static string GetConnectionStringWithApplicationName(string connectionS
return connectionString;
}
- string applicationName = ProductInfo.GetDataApiBuilderApplicationName();
+ string applicationName = ProductInfo.GetDataApiBuilderUserAgent();
// Create a StringBuilder from the connection string.
SqlConnectionStringBuilder connectionStringBuilder;
diff --git a/src/Core/Services/OpenAPI/OpenApiDocumentor.cs b/src/Core/Services/OpenAPI/OpenApiDocumentor.cs
index ebe09223e4..eba3c07427 100644
--- a/src/Core/Services/OpenAPI/OpenApiDocumentor.cs
+++ b/src/Core/Services/OpenAPI/OpenApiDocumentor.cs
@@ -13,6 +13,7 @@
using Azure.DataApiBuilder.Core.Parsers;
using Azure.DataApiBuilder.Core.Services.MetadataProviders;
using Azure.DataApiBuilder.Core.Services.OpenAPI;
+using Azure.DataApiBuilder.Product;
using Azure.DataApiBuilder.Service.Exceptions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Writers;
@@ -30,7 +31,6 @@ public class OpenApiDocumentor : IOpenApiDocumentor
private OpenApiResponses _defaultOpenApiResponses;
private OpenApiDocument? _openApiDocument;
- private const string DOCUMENTOR_VERSION = "PREVIEW";
private const string DOCUMENTOR_UI_TITLE = "Data API builder - REST Endpoint";
private const string GETALL_DESCRIPTION = "Returns entities.";
private const string GETONE_DESCRIPTION = "Returns an entity.";
@@ -132,8 +132,8 @@ public void CreateDocument()
{
Info = new OpenApiInfo
{
- Version = DOCUMENTOR_VERSION,
- Title = DOCUMENTOR_UI_TITLE,
+ Version = ProductInfo.GetProductVersion(),
+ Title = DOCUMENTOR_UI_TITLE
},
Servers = new List
{
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index dbdcf4145b..a64ede0ce6 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -2,6 +2,7 @@
enable
..\out
+ 0.12
diff --git a/src/Product/ProductInfo.cs b/src/Product/ProductInfo.cs
index bbce25aa95..781317b157 100644
--- a/src/Product/ProductInfo.cs
+++ b/src/Product/ProductInfo.cs
@@ -8,22 +8,39 @@ namespace Azure.DataApiBuilder.Product;
public static class ProductInfo
{
- public const string DEFAULT_VERSION = "0.0.0";
public const string DAB_APP_NAME_ENV = "DAB_APP_NAME_ENV";
- public static readonly string DEFAULT_APP_NAME = $"dab_oss_{ProductInfo.GetProductVersion()}";
- public static readonly string ROLE_NAME = "DataApiBuilder";
+ public static readonly string DAB_USER_AGENT = $"dab_oss_{GetProductVersion()}";
+ public static readonly string CLOUD_ROLE_NAME = "DataApiBuilder";
///
- /// Reads the product version from the executing assembly's file version information.
+ /// Returns the Product version in Major.Minor.Patch format without a commit hash.
+ /// FileVersionInfo.ProductBuildPart is used to represent the Patch version.
+ /// FileVersionInfo is used to retrieve the version information from the executing assembly
+ /// set by the Version property in Directory.Build.props.
+ /// FileVersionInfo.ProductVersion includes the commit hash.
///
- /// Product version if not null, default version 0.0.0 otherwise.
- public static string GetProductVersion()
+ /// If true, returns the version string with the commit hash
+ /// Version string without commit hash: Major.Minor.Patch
+ /// Version string with commit hash: Major.Minor.Patch+COMMIT_ID"
+ public static string GetProductVersion(bool includeCommitHash = false)
{
Assembly assembly = Assembly.GetExecutingAssembly();
- FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(assembly.Location);
- string? version = fileVersionInfo.ProductVersion;
-
- return version ?? DEFAULT_VERSION;
+ FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(fileName: assembly.Location);
+
+ string versionString;
+
+ // fileVersionInfo's ProductVersion is nullable, while PoductMajorPart, ProductMinorPart, and ProductBuildPart are not.
+ // if ProductVersion is null, the other properties will be 0 since they do not return null.
+ if (includeCommitHash && fileVersionInfo.ProductVersion is not null)
+ {
+ versionString = fileVersionInfo.ProductVersion;
+ }
+ else
+ {
+ versionString = fileVersionInfo.ProductMajorPart + "." + fileVersionInfo.ProductMinorPart + "." + fileVersionInfo.ProductBuildPart;
+ }
+
+ return versionString;
}
///
@@ -31,21 +48,11 @@ public static string GetProductVersion()
/// DAB_APP_NAME_ENV environment variable. If the environment variable is not set,
/// it returns a default value indicating connections from open source.
///
+ /// Returns the value in the environment variable DAB_APP_NAME_ENV, when set.
+ /// Otherwise, returns user agent string: dab_oss_Major.Minor.Patch
public static string GetDataApiBuilderUserAgent()
{
- return Environment.GetEnvironmentVariable(DAB_APP_NAME_ENV) ?? DEFAULT_APP_NAME;
- }
-
- ///
- /// Returns the application name to be used for database connections for the DataApiBuilder.
- /// It strips the hash value from the user agent string to only return the application name and the version.
- /// The method serves as a means of identifying the source of connections made through the DataApiBuilder.
- ///
- public static string GetDataApiBuilderApplicationName()
- {
- string dabVersion = ProductInfo.GetDataApiBuilderUserAgent();
- int hashStartPosition = dabVersion.LastIndexOf('+');
- return hashStartPosition != -1 ? dabVersion[..hashStartPosition] : dabVersion;
+ return Environment.GetEnvironmentVariable(DAB_APP_NAME_ENV) ?? DAB_USER_AGENT;
}
}
diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs
index d1d7f313e0..5d69f83705 100644
--- a/src/Service.Tests/Configuration/ConfigurationTests.cs
+++ b/src/Service.Tests/Configuration/ConfigurationTests.cs
@@ -32,6 +32,7 @@
using Azure.DataApiBuilder.Product;
using Azure.DataApiBuilder.Service.Controllers;
using Azure.DataApiBuilder.Service.Exceptions;
+using Azure.DataApiBuilder.Service.HealthCheck;
using Azure.DataApiBuilder.Service.Tests.Authorization;
using Azure.DataApiBuilder.Service.Tests.OpenApiIntegration;
using Azure.DataApiBuilder.Service.Tests.SqlTests;
@@ -552,64 +553,129 @@ public void TestCorrectSerializationOfSourceObject(
}
///
- /// Checks if the connection string provided in the config is correctly updated for MSSQL.
- /// If the connection string already contains the `Application Name` property, it should append the DataApiBuilder Application Name to the existing value.
- /// If not, it should append the property `Application Name` to the connection string.
+ /// Validates that DAB supplements the MSSQL database connection strings with the property "Application Name" and
+ /// 1. Adds the property/value "Application Name=dab_oss_Major.Minor.Patch" when the env var DAB_APP_NAME_ENV is not set.
+ /// 2. Adds the property/value "Application Name=dab_hosted_Major.Minor.Patch" when the env var DAB_APP_NAME_ENV is set to "dab_hosted".
+ /// (DAB_APP_NAME_ENV is set in hosted scenario or when user sets the value.)
+ /// NOTE: "#pragma warning disable format" is used here to avoid removing intentional, readability promoting spacing in DataRow display names.
+ ///
+ /// connection string provided in the config.
+ /// Updated connection string with Application Name.
+ /// Whether DAB_APP_NAME_ENV is set in environment. (Always present in hosted scenario or if user supplies value.)
+ #pragma warning disable format
+ [DataTestMethod]
+ [DataRow("Data Source=<>;" , "Data Source=<>;Application Name=" , false, DisplayName = "[MSSQL]: DAB adds version 'dab_oss_major_minor_patch' to non-provided connection string property 'Application Name'.")]
+ [DataRow("Data Source=<>;Application Name=CustAppName;" , "Data Source=<>;Application Name=CustAppName," , false, DisplayName = "[MSSQL]: DAB appends version 'dab_oss_major_minor_patch' to user supplied 'Application Name' property.")]
+ [DataRow("Data Source=<>;App=CustAppName;" , "Data Source=<>;Application Name=CustAppName," , false, DisplayName = "[MSSQL]: DAB appends version 'dab_oss_major_minor_patch' to user supplied 'App' property and resolves property to 'Application Name'.")]
+ [DataRow("Data Source=<>;" , "Data Source=<>;Application Name=" , true , DisplayName = "[MSSQL]: DAB adds DAB_APP_NAME_ENV value 'dab_hosted' and version suffix '_major_minor_patch' to non-provided connection string property 'Application Name'.")]
+ [DataRow("Data Source=<>;Application Name=CustAppName;" , "Data Source=<>;Application Name=CustAppName," , true , DisplayName = "[MSSQL]: DAB appends DAB_APP_NAME_ENV value 'dab_hosted' and version suffix '_major_minor_patch' to user supplied 'Application Name' property.")]
+ [DataRow("Data Source=<>;App=CustAppName;" , "Data Source=<>;Application Name=CustAppName," , true , DisplayName = "[MSSQL]: DAB appends version string 'dab_hosted' and version suffix '_major_minor_patch' to user supplied 'App' property and resolves property to 'Application Name'.")]
+ #pragma warning restore format
+ public void MsSqlConnStringSupplementedWithAppNameProperty(
+ string configProvidedConnString,
+ string expectedDabModifiedConnString,
+ bool dabEnvOverride)
+ {
+ // Explicitly set the DAB_APP_NAME_ENV to null to ensure that the DAB_APP_NAME_ENV is not set.
+ if (dabEnvOverride)
+ {
+ Environment.SetEnvironmentVariable(ProductInfo.DAB_APP_NAME_ENV, "dab_hosted");
+ }
+ else
+ {
+ Environment.SetEnvironmentVariable(ProductInfo.DAB_APP_NAME_ENV, null);
+ }
+
+ // Resolve assembly version. Not possible to do in DataRow as DataRows expect compile-time constants.
+ string resolvedAssemblyVersion = ProductInfo.GetDataApiBuilderUserAgent();
+ expectedDabModifiedConnString += resolvedAssemblyVersion;
+
+ RuntimeConfig runtimeConfig = CreateBasicRuntimeConfigWithNoEntity(DatabaseType.MSSQL, configProvidedConnString);
+
+ // Act
+ bool configParsed = RuntimeConfigLoader.TryParseConfig(
+ runtimeConfig.ToJson(),
+ out RuntimeConfig updatedRuntimeConfig,
+ replaceEnvVar: true);
+
+ // Assert
+ Assert.AreEqual(
+ expected: true,
+ actual: configParsed,
+ message: "Runtime config unexpectedly failed parsing.");
+ Assert.AreEqual(
+ expected: expectedDabModifiedConnString,
+ actual: updatedRuntimeConfig.DataSource.ConnectionString,
+ message: "DAB did not properly set the 'Application Name' connection string property.");
+ }
+
+ ///
+ /// Validates that DAB doesn't append nor modify
+ /// - the 'Application Name' or 'App' properties in MySQL database connection strings.
+ /// - the 'Application Name' property in
+ /// PostgreSQL, CosmosDB_PostgreSQl, CosmosDB_NoSQL database connection strings.
+ /// This test validates that this behavior holds true when the DAB_APP_NAME_ENV environment variable
+ /// - is set (dabEnvOverride==true) -> (DAB hosted)
+ /// - is not set (dabEnvOverride==false) -> (DAB OSS).
///
/// database type.
- /// connection string provided in the config.
- /// Updated connection string with Application Name.
- /// If Dab is hosted or OSS.
+ /// connection string provided in the config.
+ /// Updated connection string with Application Name.
+ /// Whether DAB_APP_NAME_ENV is set in environment. (Always present in hosted scenario or if user supplies value.)
+ #pragma warning disable format
[DataTestMethod]
- [DataRow(DatabaseType.MSSQL, "Data Source=<>;", "Data Source=<>;Application Name=dab_oss_1.0.0", false, DisplayName = "[MSSQL]:Adding Application Name property to connectionString with dab_oss app name.")]
- [DataRow(DatabaseType.MySQL, "Something;", "Something;", false, DisplayName = "[MYSQL]:No Change in connectionString without Application name for DAB oss.")]
- [DataRow(DatabaseType.PostgreSQL, "Something;", "Something;", false, DisplayName = "[PGSQL]:No Change in connectionString without Application name for DAB oss.")]
- [DataRow(DatabaseType.CosmosDB_PostgreSQL, "Something;", "Something;", false, DisplayName = "[COSMOSDB_PGSQL]:No Change in connectionString without Application name for DAB oss.")]
- [DataRow(DatabaseType.CosmosDB_NoSQL, "Something;", "Something;", false, DisplayName = "[COSMOSDB_NOSQL]:No Change in connectionString without Application name for DAB oss.")]
- [DataRow(DatabaseType.MSSQL, "Data Source=<>;Application Name=CustAppName;", "Data Source=<>;Application Name=CustAppName,dab_oss_1.0.0", false, DisplayName = "[MSSQL]:Updating connectionString containing customer Application name with dab_oss app name.")]
- [DataRow(DatabaseType.MSSQL, "Data Source=<>;Application Name=CustAppName;User ID=<>", "Data Source=<>;User ID=<>;Application Name=CustAppName,dab_oss_1.0.0", false, DisplayName = "[MSSQL2]:Updating connectionString containing customer Application name with dab_oss app name.")]
- [DataRow(DatabaseType.MySQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", false, DisplayName = "[MYSQL]:No Change in connectionString containing customer Application name for DAB oss.")]
- [DataRow(DatabaseType.PostgreSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", false, DisplayName = "[PGSQL]:No Change in connectionString containing customer Application name for DAB oss.")]
- [DataRow(DatabaseType.CosmosDB_PostgreSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", false, DisplayName = "[COSMOSDB_PGSQL]:No Change in connectionString containing customer Application name for DAB oss.")]
- [DataRow(DatabaseType.CosmosDB_NoSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", false, DisplayName = "[COSMOSDB_NOSQL]:No Change in connectionString containg customer Application name for DAB oss.")]
- [DataRow(DatabaseType.MSSQL, "Data Source=<>;", "Data Source=<>;Application Name=dab_hosted_1.0.0", true, DisplayName = "[MSSQL]:Adding Application Name property to connectionString with dab_hosted app.")]
- [DataRow(DatabaseType.MySQL, "Something;", "Something;", true, DisplayName = "[MYSQL]:No Change in connectionString without Application name for DAB hosted.")]
- [DataRow(DatabaseType.PostgreSQL, "Something;", "Something;", true, DisplayName = "[PGSQL]:No Change in connectionString without Application name for DAB hosted.")]
- [DataRow(DatabaseType.CosmosDB_PostgreSQL, "Something;", "Something;", true, DisplayName = "[COSMOSDB_PGSQL]:No Change in connectionString without Application name for DAB hosted.")]
- [DataRow(DatabaseType.CosmosDB_NoSQL, "Something;", "Something;", true, DisplayName = "[COSMOSDB_NOSQL]:No Change in connectionString without Application name for DAB hosted.")]
- [DataRow(DatabaseType.MSSQL, "Data Source=<>;Application Name=CustAppName;", "Data Source=<>;Application Name=CustAppName,dab_hosted_1.0.0", true, DisplayName = "[MSSQL]:Updating connectionString containing customer Application name with dab_hosted app name.")]
- [DataRow(DatabaseType.MySQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", true, DisplayName = "[MYSQL]:No Change in connectionString containing customer Application name for DAB hosted.")]
- [DataRow(DatabaseType.PostgreSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", true, DisplayName = "[PGSQL]:No Change in connectionString containing customer Application name for DAB hosted.")]
- [DataRow(DatabaseType.CosmosDB_PostgreSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", true, DisplayName = "[COSMOSDB_PGSQL]:No Change in connectionString containing customer Application name for DAB hosted.")]
- [DataRow(DatabaseType.CosmosDB_NoSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", true, DisplayName = "[COSMOSDB_NOSQL]:No Change in connectionString containing customer Application name for DAB hosted.")]
- [DataRow(DatabaseType.MSSQL, "Data Source=<>;App=CustAppName;User ID=<>", "Data Source=<>;User ID=<>;Application Name=CustAppName,dab_oss_1.0.0", false, DisplayName = "[MSSQL]:Updating connectionString containing `App` for customer Application name with dab_oss app name.")]
- [DataRow(DatabaseType.MySQL, "Something1;App=CustAppName;Something2;", "Something1;App=CustAppName;Something2;", false, DisplayName = "[MySQL]:No updates for `App` preoperty in connectionString for DBs other than MSSQL.")]
- [DataRow(DatabaseType.MySQL, "username=dabApp;App=CustAppName;Something2;", "username=dabApp;App=CustAppName;Something2;", false, DisplayName = "[MySQL]:No updates for other properties in connectionString containing `App`.")]
+ [DataRow(DatabaseType.MySQL, "Something;" , "Something;" , false, DisplayName = "[MYSQL|DAB OSS]:No addition of 'Application Name' or 'App' property to connection string.")]
+ [DataRow(DatabaseType.MySQL, "Something;Application Name=CustAppName;" , "Something;Application Name=CustAppName;" , false, DisplayName = "[MYSQL|DAB OSS]:No modification of customer overridden 'Application Name' property.")]
+ [DataRow(DatabaseType.MySQL, "Something1;App=CustAppName;Something2;" , "Something1;App=CustAppName;Something2;" , false, DisplayName = "[MySQL|DAB OSS]:No modification of customer overridden 'App' property.")]
+ [DataRow(DatabaseType.MySQL, "Something;" , "Something;" , true , DisplayName = "[MYSQL|DAB hosted]:No addition of 'Application Name' or 'App' property to connection string.")]
+ [DataRow(DatabaseType.MySQL, "Something;Application Name=CustAppName;" , "Something;Application Name=CustAppName;" , true , DisplayName = "[MYSQL|DAB hosted]:No modification of customer overridden 'Application Name' property.")]
+ [DataRow(DatabaseType.MySQL, "Something1;App=CustAppName;Something2;" , "Something1;App=CustAppName;Something2;" , true, DisplayName = "[MySQL|DAB hosted]:No modification of customer overridden 'App' property.")]
+ [DataRow(DatabaseType.PostgreSQL, "Something;" , "Something;" , false, DisplayName = "[PGSQL|DAB OSS]:No addition of 'Application Name' property to connection string.]")]
+ [DataRow(DatabaseType.PostgreSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", false, DisplayName = "[PGSQL|DAB OSS]:No modification of customer overridden 'Application Name' property.")]
+ [DataRow(DatabaseType.PostgreSQL, "Something;" , "Something;" , true , DisplayName = "[PGSQL|DAB hosted]:No addition of 'Application Name' property to connection string.")]
+ [DataRow(DatabaseType.PostgreSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", true , DisplayName = "[PGSQL|DAB hosted]:No modification of customer overridden 'Application Name' property.")]
+ [DataRow(DatabaseType.CosmosDB_NoSQL, "Something;" , "Something;" , false, DisplayName = "[COSMOSDB_NOSQL|DAB OSS]:No addition of 'Application Name' property to connection string.")]
+ [DataRow(DatabaseType.CosmosDB_NoSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", false, DisplayName = "[COSMOSDB_NOSQL|DAB OSS]:No modification of customer overridden 'Application Name' property.")]
+ [DataRow(DatabaseType.CosmosDB_NoSQL, "Something;" , "Something;" , true , DisplayName = "[COSMOSDB_NOSQL|DAB hosted]:No addition of 'Application Name' property to connection string.")]
+ [DataRow(DatabaseType.CosmosDB_NoSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", true , DisplayName = "[COSMOSDB_NOSQL|DAB hosted]:No modification of customer overridden 'Application Name' property.")]
+ [DataRow(DatabaseType.CosmosDB_PostgreSQL, "Something;" , "Something;" , false, DisplayName = "[COSMOSDB_PGSQL|DAB OSS]:No addition of 'Application Name' property to connection string.")]
+ [DataRow(DatabaseType.CosmosDB_PostgreSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", false, DisplayName = "[COSMOSDB_PGSQL|DAB OSS]:No modification of customer overridden 'Application Name' property.")]
+ [DataRow(DatabaseType.CosmosDB_PostgreSQL, "Something;" , "Something;" , true , DisplayName = "[COSMOSDB_PGSQL|DAB hosted]:No addition of 'Application Name' property to connection string.")]
+ [DataRow(DatabaseType.CosmosDB_PostgreSQL, "Something;Application Name=CustAppName;", "Something;Application Name=CustAppName;", true , DisplayName = "[COSMOSDB_PGSQL|DAB hosted]:No modification of customer overridden 'Application Name' property.")]
+ #pragma warning restore format
public void TestConnectionStringIsCorrectlyUpdatedWithApplicationName(
DatabaseType databaseType,
- string providedConnectionString,
- string expectedUpdatedConnectionString,
- bool isHostedScenario)
+ string configProvidedConnString,
+ string expectedDabModifiedConnString,
+ bool dabEnvOverride)
{
- if (isHostedScenario)
+ // Explicitly set the DAB_APP_NAME_ENV to null to ensure that the DAB_APP_NAME_ENV is not set.
+ if (dabEnvOverride)
{
- Environment.SetEnvironmentVariable(ProductInfo.DAB_APP_NAME_ENV, "dab_hosted_1.0.0");
+ Environment.SetEnvironmentVariable(ProductInfo.DAB_APP_NAME_ENV, "dab_hosted");
}
else
{
Environment.SetEnvironmentVariable(ProductInfo.DAB_APP_NAME_ENV, null);
}
- RuntimeConfig runtimeConfig = CreateBasicRuntimeConfigWithNoEntity(databaseType, providedConnectionString);
+ RuntimeConfig runtimeConfig = CreateBasicRuntimeConfigWithNoEntity(databaseType, configProvidedConnString);
- Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(
+ // Act
+ bool configParsed = RuntimeConfigLoader.TryParseConfig(
runtimeConfig.ToJson(),
out RuntimeConfig updatedRuntimeConfig,
- replaceEnvVar: true));
-
- string actualUpdatedConnectionString = updatedRuntimeConfig.DataSource.ConnectionString;
+ replaceEnvVar: true);
- Assert.AreEqual(actualUpdatedConnectionString, expectedUpdatedConnectionString);
+ // Assert
+ Assert.AreEqual(
+ expected: true,
+ actual: configParsed,
+ message: "Runtime config unexpectedly failed parsing.");
+ Assert.AreEqual(
+ expected: expectedDabModifiedConnString,
+ actual: updatedRuntimeConfig.DataSource.ConnectionString,
+ message: "DAB did not properly set the 'Application Name' connection string property.");
}
[TestMethod("Validates that once the configuration is set, the config controller isn't reachable."), TestCategory(TestCategory.COSMOSDBNOSQL)]
@@ -3196,6 +3262,99 @@ public async Task OpenApi_GlobalEntityRestPath(bool globalRestEnabled, bool expe
}
}
+ ///
+ /// Simulates a GET request to DAB's health check endpoint ('/') and validates the contents of the response.
+ /// The expected format of the response is:
+ /// {
+ /// "status": "Healthy",
+ /// "version": "0.12.0",
+ /// "appName": "dab_oss_0.12.0"
+ /// }
+ /// - the 'version' property format is 'major.minor.patch'
+ ///
+ [TestMethod]
+ [TestCategory(TestCategory.MSSQL)]
+ public async Task HealthEndpoint_ValidateContents()
+ {
+ // Arrange
+ // At least one entity is required in the runtime config for the engine to start.
+ // Even though this entity is not under test, it must be supplied enable successfull
+ // config file creation.
+ Entity requiredEntity = new(
+ Source: new("books", EntitySourceType.Table, null, null),
+ Rest: new(Enabled: false),
+ GraphQL: new("book", "books"),
+ Permissions: new[] { GetMinimalPermissionConfig(AuthorizationResolver.ROLE_ANONYMOUS) },
+ Relationships: null,
+ Mappings: null);
+
+ Dictionary entityMap = new()
+ {
+ { "Book", requiredEntity }
+ };
+
+ CreateCustomConfigFile(globalRestEnabled: true, entityMap);
+
+ string[] args = new[]
+ {
+ $"--ConfigFileName={CUSTOM_CONFIG_FILENAME}"
+ };
+
+ using TestServer server = new(Program.CreateWebHostBuilder(args));
+ using HttpClient client = server.CreateClient();
+
+ // Setup and send GET request to root path.
+ HttpRequestMessage getHealthEndpointContents = new(HttpMethod.Get, $"/");
+
+ // Act - Exercise the health check endpoint code by requesting the health endpoint path '/'.
+ HttpResponseMessage response = await client.SendAsync(getHealthEndpointContents);
+
+ // Assert - Process response body and validate contents.
+ // Validate HTTP return code.
+ string responseBody = await response.Content.ReadAsStringAsync();
+ Dictionary responseProperties = JsonSerializer.Deserialize>(responseBody);
+ Assert.AreEqual(expected: HttpStatusCode.OK, actual: response.StatusCode, message: "Received unexpected HTTP code from health check endpoint.");
+
+ // Validate value of 'status' property in reponse.
+ if (responseProperties.TryGetValue(key: "status", out JsonElement statusValue))
+ {
+ Assert.AreEqual(
+ expected: "Healthy",
+ actual: statusValue.ToString(),
+ message: "Expected endpoint to report 'Healthy'.");
+ }
+ else
+ {
+ Assert.Fail();
+ }
+
+ // Validate value of 'version' property in response.
+ if (responseProperties.TryGetValue(key: DabHealthCheck.DAB_VERSION_KEY, out JsonElement versionValue))
+ {
+ Assert.AreEqual(
+ expected: ProductInfo.GetProductVersion(),
+ actual: versionValue.ToString(),
+ message: "Unexpected or missing version value.");
+ }
+ else
+ {
+ Assert.Fail();
+ }
+
+ // Validate value of 'app-name' property in response.
+ if (responseProperties.TryGetValue(key: DabHealthCheck.DAB_APPNAME_KEY, out JsonElement appNameValue))
+ {
+ Assert.AreEqual(
+ expected: ProductInfo.GetDataApiBuilderUserAgent(),
+ actual: appNameValue.ToString(),
+ message: "Unexpected or missing DAB user agent string.");
+ }
+ else
+ {
+ Assert.Fail();
+ }
+ }
+
///
/// Validates the behavior of the OpenApiDocumentor when the runtime config has entities with
/// REST endpoint enabled and disabled.
diff --git a/src/Service.Tests/CosmosTests/CosmosClientTests.cs b/src/Service.Tests/CosmosTests/CosmosClientTests.cs
index fb061c256c..6934d8a302 100644
--- a/src/Service.Tests/CosmosTests/CosmosClientTests.cs
+++ b/src/Service.Tests/CosmosTests/CosmosClientTests.cs
@@ -20,7 +20,7 @@ public void CosmosClientDefaultUserAgent()
CosmosClientProvider cosmosClientProvider = _application.Services.GetService();
CosmosClient client = cosmosClientProvider.Clients[cosmosClientProvider.RuntimeConfigProvider.GetConfig().DefaultDataSourceName];
// Validate results
- Assert.AreEqual(client.ClientOptions.ApplicationName, ProductInfo.DEFAULT_APP_NAME);
+ Assert.AreEqual(client.ClientOptions.ApplicationName, ProductInfo.DAB_USER_AGENT);
}
[TestMethod]
diff --git a/src/Service/HealthCheck/DabHealthCheck.cs b/src/Service/HealthCheck/DabHealthCheck.cs
new file mode 100644
index 0000000000..038b1d49a6
--- /dev/null
+++ b/src/Service/HealthCheck/DabHealthCheck.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Azure.DataApiBuilder.Product;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+
+namespace Azure.DataApiBuilder.Service.HealthCheck
+{
+ ///
+ /// Health check which returns the DAB engine's version and app name (User Agent string).
+ /// - version: Major.Minor.Patch
+ /// - app-name: dab_oss_Major.Minor.Patch
+ ///
+ internal class DabHealthCheck : IHealthCheck
+ {
+ public const string DAB_VERSION_KEY = "version";
+ public const string DAB_APPNAME_KEY = "app-name";
+
+ ///
+ /// Method to check the health of the DAB engine which is executed by dotnet internals when registered as a health check
+ /// in startup.cs
+ ///
+ /// dotnet provided health check context.
+ /// cancellation token.
+ /// HealthCheckResult with version and appname/useragent string.
+ public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
+ {
+ Dictionary dabVersionMetadata = new()
+ {
+ { DAB_VERSION_KEY, ProductInfo.GetProductVersion() },
+ { DAB_APPNAME_KEY, ProductInfo.GetDataApiBuilderUserAgent() }
+ };
+
+ HealthCheckResult healthCheckResult = HealthCheckResult.Healthy(
+ description: "Healthy",
+ data: dabVersionMetadata);
+
+ return Task.FromResult(healthCheckResult);
+ }
+ }
+}
diff --git a/src/Service/HealthCheck/HealthReportResponseWriter.cs b/src/Service/HealthCheck/HealthReportResponseWriter.cs
new file mode 100644
index 0000000000..fc5da7783b
--- /dev/null
+++ b/src/Service/HealthCheck/HealthReportResponseWriter.cs
@@ -0,0 +1,118 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.IO;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.Extensions.Logging;
+
+namespace Azure.DataApiBuilder.Service.HealthCheck
+{
+ ///
+ /// Creates a JSON response for the health check endpoint using the provided health report.
+ /// If the response has already been created, it will be reused.
+ ///
+ public class HealthReportResponseWriter
+ {
+ // Dependencies
+ private ILogger? _logger;
+
+ // State
+ private byte[]? _responseBytes;
+
+ // Constants
+ private const string JSON_CONTENT_TYPE = "application/json; charset=utf-8";
+
+ public HealthReportResponseWriter(ILogger? logger)
+ {
+ _logger = logger;
+ }
+
+ ///
+ /// Function provided to the health check middleware to write the response.
+ ///
+ /// HttpContext for writing the response.
+ /// Result of health check(s).
+ /// Writes the http response to the http context.
+ public Task WriteResponse(HttpContext context, HealthReport healthReport)
+ {
+
+ context.Response.ContentType = JSON_CONTENT_TYPE;
+
+ if (_responseBytes is null)
+ {
+ _responseBytes = CreateResponse(healthReport);
+ }
+
+ return context.Response.WriteAsync(Encoding.UTF8.GetString(_responseBytes));
+ }
+
+ ///
+ /// Using the provided health report, creates the JSON byte array to be returned and cached.
+ /// Currently, checks for the custom DabHealthCheck result and adds the version and app name to the response.
+ /// The result of the response returned for the health endpoint would be:
+ /// {
+ /// "status": "Healthy",
+ /// "version": "Major.Minor.Patch",
+ /// "appName": "dab_oss_Major.Minor.Patch"
+ /// }
+ ///
+ /// Collection of Health Check results calculated by dotnet HealthCheck endpoint.
+ /// Byte array with JSON response contents.
+ public byte[] CreateResponse(HealthReport healthReport)
+ {
+ JsonWriterOptions options = new() { Indented = true };
+
+ using MemoryStream memoryStream = new();
+ using (Utf8JsonWriter jsonWriter = new(memoryStream, options))
+ {
+ jsonWriter.WriteStartObject();
+ jsonWriter.WriteString("status", healthReport.Status.ToString());
+
+ if (healthReport.Entries.TryGetValue(key: typeof(DabHealthCheck).Name, out HealthReportEntry healthReportEntry))
+ {
+ if (healthReportEntry.Data.TryGetValue(DabHealthCheck.DAB_VERSION_KEY, out object? versionValue) && versionValue is string versionNumber)
+ {
+ jsonWriter.WriteString(DabHealthCheck.DAB_VERSION_KEY, versionNumber);
+ }
+ else
+ {
+ LogTrace("DabHealthCheck did not contain the version number in the HealthReport.");
+ }
+
+ if (healthReportEntry.Data.TryGetValue(DabHealthCheck.DAB_APPNAME_KEY, out object? appNameValue) && appNameValue is string appName)
+ {
+ jsonWriter.WriteString(DabHealthCheck.DAB_APPNAME_KEY, appName);
+ }
+ else
+ {
+ LogTrace("DabHealthCheck did not contain the app name in the HealthReport.");
+ }
+ }
+ else
+ {
+ LogTrace("DabHealthCheck was not found in the HealthReport.");
+ }
+
+ jsonWriter.WriteEndObject();
+ }
+
+ return memoryStream.ToArray();
+ }
+
+ ///
+ /// Logs a trace message if a logger is present and the logger is enabled for trace events.
+ ///
+ /// Message to emit.
+ private void LogTrace(string message)
+ {
+ if (_logger is not null && _logger.IsEnabled(LogLevel.Trace))
+ {
+ _logger.LogTrace(message);
+ }
+ }
+ }
+}
diff --git a/src/Service/Startup.cs b/src/Service/Startup.cs
index 476fcd9736..eb89200d81 100644
--- a/src/Service/Startup.cs
+++ b/src/Service/Startup.cs
@@ -24,6 +24,7 @@
using Azure.DataApiBuilder.Core.Services.OpenAPI;
using Azure.DataApiBuilder.Service.Controllers;
using Azure.DataApiBuilder.Service.Exceptions;
+using Azure.DataApiBuilder.Service.HealthCheck;
using HotChocolate.AspNetCore;
using HotChocolate.Types;
using Microsoft.ApplicationInsights;
@@ -33,6 +34,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
@@ -105,7 +107,8 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton();
services.AddSingleton();
- services.AddHealthChecks();
+ services.AddHealthChecks()
+ .AddCheck("DabHealthCheck");
services.AddSingleton>(implementationFactory: (serviceProvider) =>
{
@@ -139,6 +142,14 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
+
+ // ILogger explicit creation required for logger to use --LogLevel startup argument specified.
+ services.AddSingleton>(implementationFactory: (serviceProvider) =>
+ {
+ ILoggerFactory? loggerFactory = CreateLoggerFactoryForHostedAndNonHostedScenario(serviceProvider);
+ return loggerFactory.CreateLogger();
+ });
services.AddSingleton>(implementationFactory: (serviceProvider) =>
{
@@ -387,7 +398,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, RuntimeC
Enable = false
});
- endpoints.MapHealthChecks("/");
+ endpoints.MapHealthChecks("/", new HealthCheckOptions
+ {
+ ResponseWriter = app.ApplicationServices.GetRequiredService().WriteResponse
+ });
});
}
diff --git a/src/Service/Telemetry/AppInsightsTelemetryInitializer.cs b/src/Service/Telemetry/AppInsightsTelemetryInitializer.cs
index 3531ff8c2a..bd73def78c 100644
--- a/src/Service/Telemetry/AppInsightsTelemetryInitializer.cs
+++ b/src/Service/Telemetry/AppInsightsTelemetryInitializer.cs
@@ -8,7 +8,7 @@ public class AppInsightsTelemetryInitializer : ITelemetryInitializer
{
public static readonly IReadOnlyDictionary GlobalProperties = new Dictionary
{
- { "ProductName", $"{ProductInfo.DEFAULT_APP_NAME}"},
+ { "ProductName", $"{ProductInfo.DAB_USER_AGENT}"},
{ "UserAgent", $"{ProductInfo.GetDataApiBuilderUserAgent()}" }
// Add more custom properties here
};
@@ -19,7 +19,7 @@ public class AppInsightsTelemetryInitializer : ITelemetryInitializer
/// The telemetry object to initialize
public void Initialize(ITelemetry telemetry)
{
- telemetry.Context.Cloud.RoleName = ProductInfo.ROLE_NAME;
+ telemetry.Context.Cloud.RoleName = ProductInfo.CLOUD_ROLE_NAME;
telemetry.Context.Session.Id = Guid.NewGuid().ToString();
telemetry.Context.Component.Version = ProductInfo.GetProductVersion();