Auto stash before merge of "main" and "origin/main"

This commit is contained in:
Janus C. H. Knudsen 2025-02-02 23:13:17 +01:00
parent 21d7128e74
commit 521190475d
41 changed files with 991 additions and 1150 deletions

View file

@ -0,0 +1,119 @@
using Core.Configurations.SmartConfig;
using Core.Configurations;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using Core.Configurations.JsonConfigProvider;
using Autofac;
using System.Data;
using Insight.Database;
using Npgsql;
namespace Tests.ConfigurationTests
{
[TestClass]
public class JsonConfigurationProviderTests : TestFixture
{
[TestMethod]
public void GetSection_ShouldReturnCorrectFeatureSection()
{
// Arrange
var expectedJObject = JObject.Parse(@"{
'Enabled': true,
'RolloutPercentage': 25,
'AllowedUserGroups': ['beta']
}") as JToken;
var builder = new ConfigurationBuilder()
.AddJsonFile("appconfiguration.dev.json")
.Build();
// Act
var section = builder.GetSection("Feature");
// Assert
section.Should().NotBeNull();
section.Value.Should().BeEquivalentTo(expectedJObject);
}
[TestMethod]
public void Get_ShouldReturnCorrectFeatureObject()
{
// Arrange
var expectedFeature = new Feature
{
Enabled = true,
RolloutPercentage = 25,
AllowedUserGroups = new() { "beta" }
};
var builder = new ConfigurationBuilder()
.AddJsonFile("appconfiguration.dev.json")
.Build();
// Act
var actualFeature = builder.GetSection("Feature").ToObject<Feature>();
var actualFeatureObsoleted = builder.GetSection("Feature").Get<Feature>();
// Assert
actualFeature.Should().BeEquivalentTo(expectedFeature);
actualFeatureObsoleted.Should().BeEquivalentTo(expectedFeature);
}
[TestMethod]
public void Get_ShouldReturnCorrectValueAsString()
{
// Arrange
var expectedFeature = "123";
var builder = new ConfigurationBuilder()
.AddJsonFile("appconfiguration.dev.json")
.Build();
// Act
var actualFeature = builder.GetSection("AnotherSetting").Get<string>("Thresholds:High");
// Assert
actualFeature.Should().BeEquivalentTo(expectedFeature);
}
[TestMethod]
public void Get_ShouldReturnCorrectValueAsInt()
{
// Arrange
var expectedFeature = 22;
var builder = new ConfigurationBuilder()
.AddJsonFile("appconfiguration.dev.json")
.Build();
// Act
var actualFeature = builder.GetSection("AnotherSetting:Temperature").Get<int>("Indoor:Max:Limit");
// Assert
actualFeature.Should().Be(expectedFeature);
}
[TestMethod]
public void Get_ShouldReturnCorrectValueAsBool()
{
// Arrange
var expectedFeature = true;
var configRoot = new ConfigurationBuilder()
.AddJsonFile("appconfiguration.dev.json")
.AddSmartConfig()
.Build();
// Act
var actualFeature = configRoot.Get<bool>("Database:UseSSL");
// Assert
actualFeature.Should().Be(expectedFeature);
}
}
public class Feature
{
public bool Enabled { get; set; }
public int RolloutPercentage { get; set; }
public List<string> AllowedUserGroups { get; set; }
}
}

View file

@ -1,139 +1,78 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Moq;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;
namespace Tests.ConfigurationTests
using Tests;
using Core.Configurations.SmartConfig;
using Core.Configurations.Common;
namespace Tests.ConfigurationTests;
[TestClass]
public class ConfigurationTests : TestFixture
{
[TestClass]
public class KeyValueJsonHandlingTests : TestFixture
{
[TestInitialize]
public void Init()
{
}
[TestMethod]
public void FunMixedTypesTest()
{
var pairs = new List<KeyValuePair<string, object>>
{
new("developer:coffeeLevel", 9001.5),
new("developer:bugsFixed", 42),
new("developer:awake", false),
new("compiler:errors:semicolon:0", "Missing ;"),
new("compiler:errors:semicolon:1", "Found ; but why?"),
new("computer:ram:status", "Out of memory"),
new("computer:cpu:temperature", 99.9),
new("friday:beer:count", 6),
new("friday:code:working", true),
new("friday:bugs:count", int.MaxValue),
new("real:object", JObject.Parse(@"{
""beer"": {
""count"": 6
},
""code"": {
""working"": true
},
""bugs"": {
""count"": 2147483647
}
}"))
};
[TestMethod]
public void ConfigurationSettingsTest()
{
var pairs = new List<KeyValuePair<string, JToken>>
{
new("Debug", true),
// Database konfiguration
new("Database:ConnectionString", "Server=db.example.com;Port=5432"),
new("Database:Timeout", 30),
new("Database:UseSSL", true),
var result = KeyValueToJson.Convert(pairs);
// Logging konfiguration med JObject
new("Logging:FileOptions", JObject.Parse(@"{
'Path': '/var/logs/app.log',
'MaxSizeMB': 100,
'RetentionDays': 7
}")),
var expected = JObject.Parse(@"{
'developer': {
'coffeeLevel': 9001.5,
'bugsFixed': 42,
'awake': false
},
'compiler': {
'errors': { 'semicolon': ['Missing ;', 'Found ; but why?'] }
},
'computer': {
'ram': { 'status': 'Out of memory' },
'cpu': { 'temperature': 99.9 }
},
'friday': {
'beer': { 'count': 6 },
'code': { 'working': true },
'bugs': { 'count': 2147483647 }
// Feature flags med kompleks konfiguration
new("Features:Experimental", JObject.Parse(@"{
'Enabled': true,
'RolloutPercentage': 25,
'AllowedUserGroups': ['beta']
}")),
// API endpoints med array
new("API:Endpoints", "/api/users"),
new("API:Endpoints", "/api/products")
};
var result = KeyValueToJson.Convert(pairs);
var expected = JObject.Parse(@"{
'Debug' : true,
'Database': {
'ConnectionString': 'Server=db.example.com;Port=5432',
'Timeout': 30,
'UseSSL': true
},
'Logging': {
'FileOptions': {
'Path': '/var/logs/app.log',
'MaxSizeMB': 100,
'RetentionDays': 7
}
}");
},
'Features': {
'Experimental': {
'Enabled': true,
'RolloutPercentage': 25,
'AllowedUserGroups': ['beta']
}
},
'API': {
'Endpoints': ['/api/users', '/api/products']
}
}");
Assert.IsTrue(JToken.DeepEquals(expected, result));
}
}
public static class KeyValueToJson
{
public static JObject Convert(List<KeyValuePair<string, object>> pairs)
{
var root = new JObject();
foreach (var pair in pairs)
{
var keys = pair.Key.Split(':');
var current = root;
// Gennemgå hierarkiet og opret underobjekter, hvis de ikke eksisterer
for (int i = 0; i < keys.Length - 1; i++)
{
var key = keys[i];
if (current[key] == null)
{
current[key] = new JObject();
}
current = (JObject)current[key];
}
// Håndter den sidste nøgle og tilføj værdien
var lastKey = keys[keys.Length - 1];
var value = ConvertValue(pair.Value);
// Hvis den sidste nøgle allerede eksisterer, tilføj til en liste
if (current[lastKey] != null)
{
// Hvis den allerede er en liste, tilføj til listen
if (current[lastKey].Type == JTokenType.Array)
{
((JArray)current[lastKey]).Add(value);
}
// Hvis den ikke er en liste, konverter til en liste
else
{
var existingValue = current[lastKey];
current[lastKey] = new JArray { existingValue, value };
}
}
// Ellers tilføj som en enkelt værdi
else
{
current[lastKey] = value;
}
}
return root;
}
private static JToken ConvertValue(object value)
{
// Konverter værdien til det korrekte JToken-format
return value switch
{
int i => new JValue(i),
double d => new JValue(d),
bool b => new JValue(b),
string s => new JValue(s),
_ => new JValue(value.ToString()) // Fallback for andre typer
};
}
}
}
Assert.IsTrue(JToken.DeepEquals(expected, result));
}
}

View file

@ -0,0 +1,102 @@
using Core.Configurations.SmartConfig;
using Core.Configurations;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using Core.Configurations.JsonConfigProvider;
using Autofac;
using System.Data;
using Insight.Database;
using Npgsql;
namespace Tests.ConfigurationTests
{
[TestClass]
public class SmartConfigProviderTests : TestFixture
{
[TestMethod]
public void Get_ShouldReturnCorrectValueAsBool()
{
// Arrange
var expectedFeature = true;
var config = new ConfigurationBuilder()
.AddJsonFile("appconfiguration.dev.json")
.AddSmartConfig()
.Build();
// Act
var actualFeature = config.Get<bool>("Database:UseSSL");
// Assert
actualFeature.Should().Be(expectedFeature);
}
[TestMethod]
public void Get_ShouldReturnCorrectValueWhenSelectingIntoValueRowInConfigTable()
{
// Arrange
var expectedFeature = 100;
var builder = new ConfigurationBuilder()
.AddJsonFile("appconfiguration.dev.json")
.AddSmartConfig()
.Build();
// Act
var actualFeature = builder.GetSection("Logging:FileOptions").Get<int>("MaxSizeMB");
var withoutSectionThisAlsoWorks = builder.Get<int>("Logging:FileOptions:MaxSizeMB");
// Assert
actualFeature.Should().Be(expectedFeature);
actualFeature.Should().Be(withoutSectionThisAlsoWorks);
}
[TestMethod]
public void GetPostgresSearchPath()
{
//ALTER USER sathumper SET search_path TO ptmain, public;
var conn = Container.Resolve<IDbConnection>();
var result = conn.QuerySql("SHOW search_path;");
using (var connw = new NpgsqlConnection(conn.ConnectionString + ";Password=3911"))
{
connw.Open();
using (var cmd = new NpgsqlCommand("SHOW search_path; SELECT current_user;", connw))
{
using (var reader = cmd.ExecuteReader())
{
reader.Read();
var r1 = $"Search path: {reader.GetString(0)}";
reader.NextResult();
reader.Read();
var r2 = $"Current schema: {reader.GetString(0)}";
}
}
}
}
[TestMethod]
public void TryGetActiveConfigurations()
{
const string sql = @"
SELECT id, ""key"", value, label, content_type,
valid_from, expires_at, created_at, modified_at, etag
FROM app_configuration
WHERE CURRENT_TIMESTAMP BETWEEN valid_from AND expires_at
OR (valid_from IS NULL AND expires_at IS NULL)";
var conn = Container.Resolve<IDbConnection>();
var result = conn.QuerySql(sql);
}
}
}

50
Tests/PostgresTests.cs Normal file
View file

@ -0,0 +1,50 @@
using Autofac;
using System.Data;
using Insight.Database;
namespace Tests
{
[TestClass]
public class PostgresTests : TestFixture
{
[TestMethod]
public void TestDefaultConnection()
{
var conn = Container.Resolve<IDbConnection>();
//https://www.reddit.com/r/dotnet/comments/6wdoyn/how_to_properly_register_dapper_on_net_core_2_di/
//https://www.code4it.dev/blog/postgres-crud-dapper/
//https://stackoverflow.com/questions/69169247/how-to-create-idbconnection-factory-using-autofac-for-dapper
conn.ExecuteSql("SELECT 1 as p");
}
[TestMethod]
public void TryTenantSetupService()
{
var conn = Container.Resolve<IDbConnection>();
}
[TestMethod]
public async Task TryDbSetup()
{
var conn = Container.Resolve<IDbConnection>();
var dbSetup = new Database.IdentitySystem.DbSetup(conn);
await dbSetup.CreateSystem("swp");
}
[TestMethod]
public void SetupPostgresql_LISTEN()
{
//var builder = new ConfigurationBuilder()
// .AddPostgresConfiguration(options =>
// {
// options.ConnectionString = "Host=192.168.1.57;Database=ptdb01;Username=postgres;Password=3911";
// options.Channel = "config_changes";
// options.ConfigurationQuery = @"select * from dev.app_configuration";
// });
}
}
}

View file

@ -1,173 +0,0 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Tests;
using Microsoft.Extensions.Configuration;
using Core.Configurations.SmartConfiguration;
namespace Configuration.Core.Tests;
[TestClass]
public class ConfigurationTests : TestFixture
{
private Mock<IConfigurationRepository> _mockRepo;
private KeyValueConfigurationBuilder _builder;
[TestInitialize]
public void Init()
{
_mockRepo = new Mock<IConfigurationRepository>();
_builder = new KeyValueConfigurationBuilder(_mockRepo.Object);
}
[TestMethod]
public async Task LoadConfiguration_WithValidData_BuildsCorrectly()
{
// Arrange
var configurations = new List<AppConfiguration>
{
new AppConfiguration
{
Key = "Email:Templates:Welcome",
Value = @"{""subject"":""Welcome"",""sender"":""test@company.com""}",
ValidFrom = DateTime.UtcNow.AddDays(-1),
ExpiresAt = DateTime.UtcNow.AddDays(1)
}
};
_mockRepo.Setup(r => r.GetActiveConfigurations())
.ReturnsAsync(configurations);
// Act
await _builder.LoadConfiguration();
var config = _builder.Build();
// Assert
Assert.IsNotNull(config);
Assert.AreEqual("Welcome", config["Email:Templates:Welcome:subject"]);
}
[TestMethod]
public async Task LoadConfiguration_WithExpiredData_NotIncluded()
{
// Arrange
var configurations = new List<AppConfiguration>
{
new AppConfiguration
{
Key = "Test:Key",
Value = @"{""value"":""test""}",
ValidFrom = DateTime.UtcNow.AddDays(1),
ExpiresAt = DateTime.UtcNow.AddDays(2)
}
};
_mockRepo.Setup(r => r.GetActiveConfigurations())
.ReturnsAsync(new List<AppConfiguration>());
// Act
await _builder.LoadConfiguration();
var config = _builder.Build();
// Assert
Assert.IsNull(config["Test:Key:value"]);
}
[TestMethod]
public void AddKeyValue_WithNestedKeys_BuildsCorrectHierarchy()
{
// Arrange
var key = "Level1:Level2:Level3";
var value = @"{""setting"":""value""}";
// Act
_builder.AddKeyValue(key, value);
var config = _builder.Build();
// Assert
Assert.AreEqual("value", config["Level1:Level2:Level3:setting"]);
}
[TestMethod]
public async Task GetSection_ReturnsCorrectSubSection()
{
// Arrange
var configurations = new List<AppConfiguration>
{
new AppConfiguration
{
Key = "Parent:Child",
Value = @"{""setting"":""value""}",
ValidFrom = DateTime.UtcNow.AddDays(-1)
}
};
_mockRepo.Setup(r => r.GetActiveConfigurations())
.ReturnsAsync(configurations);
// Act
await _builder.LoadConfiguration();
var config = _builder.Build();
var section = config.GetSection("Parent");
// Assert
Assert.AreEqual("value", section["Child:setting"]);
}
[TestMethod]
public void Configuration_ShouldMapToTypedClass()
{
// Arrange
var configData = new JObject
{
["Email"] = new JObject
{
["Templates"] = new JObject
{
["Welcome"] = new JObject
{
["subject"] = "Welcome to our service",
["template"] = "welcome-template.html",
["sender"] = "noreply@test.com",
["settings"] = new JObject
{
["isEnabled"] = true,
["retryCount"] = 3
}
}
}
}
};
Microsoft.Extensions.Configuration.IConfiguration configuration = null;// new JsonConfiguration(configData, new Microsoft.Extensions.Configuration.ConfigurationReloadToken());
// Act
var welcomeConfig = configuration.GetSection("Email:Templates:Welcome").Get<WelcomeEmailConfig>();
// Assert
Assert.IsNotNull(welcomeConfig);
Assert.AreEqual("Welcome to our service", welcomeConfig.Subject);
Assert.AreEqual("welcome-template.html", welcomeConfig.Template);
Assert.AreEqual("noreply@test.com", welcomeConfig.Sender);
Assert.IsTrue(welcomeConfig.Settings.IsEnabled);
Assert.AreEqual(3, welcomeConfig.Settings.RetryCount);
}
public class WelcomeEmailConfig
{
public string Subject { get; set; }
public string Template { get; set; }
public string Sender { get; set; }
public EmailSettings Settings { get; set; }
}
public class EmailSettings
{
public bool IsEnabled { get; set; }
public int RetryCount { get; set; }
}
}

View file

@ -1,97 +1,84 @@
using System;
using System.Diagnostics;
using Autofac;
using Core.Configurations;
using Core.ModuleRegistry;
using Microsoft.ApplicationInsights;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Core.Configurations.JsonConfigProvider;
namespace Tests
{
/// <summary>
/// Act as base class for tests. Avoids duplication of test setup code
/// </summary>
[TestClass]
public abstract partial class TestFixture
{
protected IContainer Container { get; private set; }
protected ContainerBuilder ContainerBuilder { get; private set; }
/// <summary>
/// Act as base class for tests. Avoids duplication of test setup code
/// </summary>
[TestClass]
public abstract partial class TestFixture
{
protected IContainer Container { get; private set; }
protected ContainerBuilder ContainerBuilder { get; private set; }
public virtual IConfigurationRoot Configuration()
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appconfiguration.dev.json")
.Build();
return configuration;
}
/// <summary>
/// Should not be overriden. Rather override PreArrangeAll to setup data needed for a test class.
/// Override PrebuildContainer with a method that does nothing to prevent early build of IOC container
/// </summary>
[TestInitialize]
public void Setup()
{
CreateContainerBuilder();
Container = ContainerBuilder.Build();
Insight.Database.Providers.PostgreSQL.PostgreSQLInsightDbProvider.RegisterProvider();
}
[AssemblyInitialize]
public static void AssemblySetup(TestContext tc)
{
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
protected virtual void CreateContainerBuilder()
{
IConfigurationRoot configuration = Configuration();
var envConfiguration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
var builder = new ContainerBuilder();
builder.RegisterInstance(new LoggerFactory())
.As<ILoggerFactory>();
}
builder.RegisterGeneric(typeof(Logger<>))
.As(typeof(ILogger<>))
.SingleInstance();
public virtual IConfigurationRoot Configuration()
{
builder.RegisterModule(new Core.ModuleRegistry.DbPostgreSqlModule
{
ConnectionString = configuration.GetConnectionString("DefaultConnection")
});
IConfigurationBuilder configBuilder = Core.Configurations.AzureConfigurationManager.AppConfigBuilder("appsettings.dev.json");
IConfigurationRoot configuration = configBuilder.Build();
return configuration;
}
/// <summary>
/// Should not be overriden. Rather override PreArrangeAll to setup data needed for a test class.
/// Override PrebuildContainer with a method that does nothing to prevent early build of IOC container
/// </summary>
[TestInitialize]
public void Setup()
{
CreateContainerBuilder();
Container = ContainerBuilder.Build();
Insight.Database.Providers.PostgreSQL.PostgreSQLInsightDbProvider.RegisterProvider();
}
protected virtual void CreateContainerBuilder()
{
IConfigurationRoot configuration = Configuration();
var builder = new ContainerBuilder();
builder.RegisterInstance(new LoggerFactory())
.As<ILoggerFactory>();
builder.RegisterGeneric(typeof(Logger<>))
.As(typeof(ILogger<>))
.SingleInstance();
builder.RegisterModule(new Core.ModuleRegistry.DbPostgreSqlModule
{
ConnectionString = configuration.GetConnectionString("ptdb")
});
builder.RegisterModule(new Core.ModuleRegistry.TelemetryModule
{
TelemetryConfig = configuration.GetSection("ApplicationInsights").Get<Core.ModuleRegistry.TelemetryConfig>()
});
builder.RegisterModule(new Core.ModuleRegistry.TelemetryModule
{
TelemetryConfig = configuration.GetSection("ApplicationInsights").ToObject<Core.ModuleRegistry.TelemetryConfig>()
});
ContainerBuilder = builder;
}
ContainerBuilder = builder;
}
[TestCleanup]
public void CleanUp()
{
Trace.Flush();
var telemetryClient = Container.Resolve<TelemetryClient>();
telemetryClient.Flush();
[TestCleanup]
public void CleanUp()
{
Trace.Flush();
var telemetryClient = Container.Resolve<TelemetryClient>();
telemetryClient.Flush();
if (Container != null)
{
Container.Dispose();
Container = null;
}
}
if (Container != null)
{
Container.Dispose();
Container = null;
}
}
}
}
}

View file

@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="FluentAssertions" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="moq" Version="4.20.72" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
@ -26,7 +27,7 @@
</ItemGroup>
<ItemGroup>
<None Update="appsettings.dev.json">
<None Update="appconfiguration.dev.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

View file

@ -1,56 +0,0 @@
using Autofac;
using System.Data;
using Insight.Database;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Extensions.Configuration;
using Core.Configurations.PostgresqlConfigurationBuilder;
namespace Tests
{
[TestClass]
public class UnitTest1 : TestFixture
{
[TestMethod]
public void TestDefaultConnection()
{
var conn = Container.Resolve<IDbConnection>();
//https://www.reddit.com/r/dotnet/comments/6wdoyn/how_to_properly_register_dapper_on_net_core_2_di/
//https://www.code4it.dev/blog/postgres-crud-dapper/
//https://stackoverflow.com/questions/69169247/how-to-create-idbconnection-factory-using-autofac-for-dapper
conn.ExecuteSql("SELECT 1 as p");
//var sql = "SELECT * FROM swp.foo";
//var customers = conn.Query(sql, commandType:CommandType.Text);
}
[TestMethod]
public void TryTenantSetupService()
{
var conn = Container.Resolve<IDbConnection>();
}
[TestMethod]
public async Task TryDbSetup()
{
var conn = Container.Resolve<IDbConnection>();
var dbSetup = new Database.AppConfigurationSystem.ConfigurationDatabaseSetup(conn);
//await dbSetup..CreateDatabaseWithSchema("swp");
}
[TestMethod]
public void SetupPostgresql_LISTEN()
{
var builder = new ConfigurationBuilder()
.AddPostgresConfiguration(options =>
{
options.ConnectionString = "Host=192.168.1.57;Database=ptdb01;Username=postgres;Password=3911";
options.Channel = "config_changes";
options.ConfigurationQuery = @"select * from dev.app_configuration";
});
}
}
}

View file

@ -0,0 +1,32 @@
{
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Host=192.168.1.57;Port=5432;Database=ptdb01;User Id=sathumper;Password=3911;"
},
"ApplicationInsights": {
"ConnectionString": "InstrumentationKey=6d2e76ee-5343-4691-a5e3-81add43cb584;IngestionEndpoint=https://northeurope-0.in.applicationinsights.azure.com/"
},
"Feature": {
"Enabled": true,
"RolloutPercentage": 25,
"AllowedUserGroups": [ "beta" ]
},
"AnotherSetting": {
"Thresholds": {
"High": "123",
"Low": "-1"
},
"Temperature": {
"Indoor": {
"Max": { "Limit": 22 },
"Min": { "Limit": 18 }
},
"Outdoor": {
"Max": { "Limit": 12 },
"Min": { "Limit": 9 }
}
}
}
}

View file

@ -1,9 +0,0 @@
{
"AllowedHosts": "*",
"ConnectionStrings": {
"ptdb": "Host=192.168.1.57;Port=5432;Database=ptdb01;User Id=postgres;Password=3911;"
},
"ApplicationInsights": {
"ConnectionString": "InstrumentationKey=6d2e76ee-5343-4691-a5e3-81add43cb584;IngestionEndpoint=https://northeurope-0.in.applicationinsights.azure.com/"
}
}