From 7af2114511774d97955d888a09e868960a4f7aaa Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Tue, 4 Jun 2024 21:27:12 +0800 Subject: [PATCH 01/40] Updating NLog.Targets.Stackify version to 2.2.0 --- Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj b/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj index 742c498..4406a50 100644 --- a/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj +++ b/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj @@ -15,7 +15,7 @@ false false false - 2.2.0-beta1 + 2.2.0 https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet https://stackify.com/wp-content/uploads/2017/02/stk.png From 232118c85525fc5840509f8be0330e1a31225816 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Tue, 4 Jun 2024 21:41:39 +0800 Subject: [PATCH 02/40] Updating NLog.Web.Stackify version to 2.2.0 --- Src/NLog.Web.Stackify/NLog.Web.Stackify.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/NLog.Web.Stackify/NLog.Web.Stackify.csproj b/Src/NLog.Web.Stackify/NLog.Web.Stackify.csproj index 3847e4b..d2db6ba 100644 --- a/Src/NLog.Web.Stackify/NLog.Web.Stackify.csproj +++ b/Src/NLog.Web.Stackify/NLog.Web.Stackify.csproj @@ -15,7 +15,7 @@ false false false - 2.2.0-beta1 + 2.2.0 https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet https://stackify.com/wp-content/uploads/2017/02/stk.png From f91c8b158b62ae911eb5ee2199f5981eda6e9301 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Tue, 4 Jun 2024 22:12:36 +0800 Subject: [PATCH 03/40] Fix nuget stackifylib artifact publish --- azure-pipelines.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5aa5fb7..b51e816 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -119,6 +119,11 @@ stages: command: pack searchPatternPack: Src\StackifyLib\*.csproj; nobuild: true + - task: PublishPipelineArtifact@1 + displayName: Publish Pipeline Artifact [Unsigned] + inputs: + path: $(Build.ArtifactStagingDirectory) + artifactName: Unsigned NuGet Packages - ${{ if eq(parameters['Build StackifyLib Signed'], true) }}: - task: DotNetCoreCLI@2 displayName: Install NuGetKeyVaultSignTool From 159ab42d32d4d8cfade62af52cc2e757e8d61c57 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Tue, 4 Jun 2024 22:18:29 +0800 Subject: [PATCH 04/40] Retain StackifyLib unsigned package --- azure-pipelines.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b51e816..1019614 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -119,11 +119,6 @@ stages: command: pack searchPatternPack: Src\StackifyLib\*.csproj; nobuild: true - - task: PublishPipelineArtifact@1 - displayName: Publish Pipeline Artifact [Unsigned] - inputs: - path: $(Build.ArtifactStagingDirectory) - artifactName: Unsigned NuGet Packages - ${{ if eq(parameters['Build StackifyLib Signed'], true) }}: - task: DotNetCoreCLI@2 displayName: Install NuGetKeyVaultSignTool @@ -142,7 +137,7 @@ stages: enabled: False inputs: targetType: inline - script: "$files = @(Get-ChildItem -Path . -File -Filter *.nupkg)\n\nforeach($file in $files) {\n $BaseFilename = $file.BaseName\n $BaseFilenameSplit = $file.BaseName.Split(\".\")\n $LastVersionDigit = \"\"\n $NewFilenameBase = \"\"\n $PackageVersion = \"\"\n $IsBeta = \"False\"\n if ($BaseFilename.contains(\"beta\")) {\n $IsBeta = \"True\"\n $LastVersionDigit = $file.BaseName.Substring($BaseFilename.length - 6,1)\n }\n else {\n $LastVersionDigit = $file.BaseName.Substring($BaseFilename.length - 1)\n }\n foreach($namePart in $BaseFilenameSplit) {\n if (!$namePart.contains(\"beta\") -and $namePart -notmatch \"^\\d+$\") {\n $NewFilenameBase = $NewFilenameBase + $namePart + \".\"\n } else {\n # check for beta\n if ($namePart.contains(\"beta\")) {\n $PackageVersion = $PackageVersion + $namePart.Substring(0,1)\n } else {\n $PackageVersion = $PackageVersion + $namePart\n }\n # check length to append a .\n if ($namePart -notmatch $LastVersionDigit) {\n $PackageVersion = $PackageVersion + \".\"\n }\n }\n }\n \n # check beta\n $FinalFilenameBase = \"\"\n if ($IsBeta -match \"True\") {\n $FinalFilenameBase = $NewFilenameBase + \"snk.\" + $PackageVersion + \"-beta\"\n } else {\n $FinalFilenameBase = $NewFilenameBase + \"snk.\" + $PackageVersion\n }\n $FinalFilename = $FinalFilenameBase + $file.Extension\n Rename-Item -Path $file -NewName $FinalFilename\n}" + script: "$files = @(Get-ChildItem -Path . -File -Filter *.nupkg)\n\nforeach($file in $files) {\n $BaseFilename = $file.BaseName\n $BaseFilenameSplit = $file.BaseName.Split(\".\")\n $LastVersionDigit = \"\"\n $NewFilenameBase = \"\"\n $PackageVersion = \"\"\n $IsBeta = \"False\"\n if ($BaseFilename.contains(\"beta\")) {\n $IsBeta = \"True\"\n $LastVersionDigit = $file.BaseName.Substring($BaseFilename.length - 6,1)\n }\n else {\n $LastVersionDigit = $file.BaseName.Substring($BaseFilename.length - 1)\n }\n foreach($namePart in $BaseFilenameSplit) {\n if (!$namePart.contains(\"beta\") -and $namePart -notmatch \"^\\d+$\") {\n $NewFilenameBase = $NewFilenameBase + $namePart + \".\"\n } else {\n # check for beta\n if ($namePart.contains(\"beta\")) {\n $PackageVersion = $PackageVersion + $namePart.Substring(0,1)\n } else {\n $PackageVersion = $PackageVersion + $namePart\n }\n # check length to append a .\n if ($namePart -notmatch $LastVersionDigit) {\n $PackageVersion = $PackageVersion + \".\"\n }\n }\n }\n \n # check beta\n $FinalFilenameBase = \"\"\n if ($IsBeta -match \"True\") {\n $FinalFilenameBase = $NewFilenameBase + \"snk.\" + $PackageVersion + \"-beta\"\n } else {\n $FinalFilenameBase = $NewFilenameBase + \"snk.\" + $PackageVersion\n }\n $FinalFilename = $FinalFilenameBase + $file.Extension\n Copy-Item -Path $file -NewName $FinalFilename\n}" workingDirectory: $(Build.ArtifactStagingDirectory) - task: PublishPipelineArtifact@1 displayName: Publish Signed Artifact From 9a0750f660ecbd29c651c3b243690e9d04997b54 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Tue, 4 Jun 2024 22:27:16 +0800 Subject: [PATCH 05/40] Update StackifyLib signed and unsigned --- azure-pipelines.yml | 49 +++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1019614..9c3219e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -66,6 +66,31 @@ stages: ConnectedServiceName: 514ed7d6-3846-4422-8013-af27483dd22c KeyVaultName: keyvault-build-resources RunAsPreJob: true + - task: DotNetCoreCLI@2 + displayName: dotnet restore + inputs: + command: restore + projects: | + Src\StackifyLib\*.csproj + Src\StackifyLib.AspNetCore\*.csproj + Src\StackifyLib.CoreLogger\*.csproj + Src\StackifyLib.log4net\*.csproj + Src\Nlog.Targets.Stackify\*.csproj + Src\NLog.Web.Stackify\*.csproj + Src\StackifyLib.StackifyTraceListener\*.csproj + - ${{ if eq(parameters['Build StackifyLib'], true) }}: + - task: DotNetCoreCLI@2 + displayName: dotnet build + inputs: + projects: | + Src\StackifyLib\*.csproj + arguments: '-c $(BuildConfiguration)' + - task: DotNetCoreCLI@2 + displayName: dotnet pack unsigned stackify lib + inputs: + command: pack + searchPatternPack: Src\StackifyLib\*.csproj; + nobuild: true - ${{ if eq(parameters['Build StackifyLib Signed'], true) }}: - task: DownloadSecureFile@1 name: SNK @@ -94,32 +119,12 @@ stages: (gc $_) -replace ([regex]::Escape("//[assembly: AssemblyKeyFile")), "[assembly: AssemblyKeyFile" | Set-Content $_.FullName -Encoding utf8 } displayName: Uncoment signing attribute - - task: DotNetCoreCLI@2 - displayName: dotnet restore - inputs: - command: restore - projects: | - Src\StackifyLib\*.csproj - Src\StackifyLib.AspNetCore\*.csproj - Src\StackifyLib.CoreLogger\*.csproj - Src\StackifyLib.log4net\*.csproj - Src\Nlog.Targets.Stackify\*.csproj - Src\NLog.Web.Stackify\*.csproj - Src\StackifyLib.StackifyTraceListener\*.csproj - - ${{ if eq(parameters['Build StackifyLib'], true) }}: - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - projects: | - Src\StackifyLib\*.csproj - arguments: '-c $(BuildConfiguration)' - - task: DotNetCoreCLI@2 - displayName: dotnet pack signed stackify lib + displayName: dotnet pack unsigned stackify lib inputs: command: pack searchPatternPack: Src\StackifyLib\*.csproj; nobuild: true - - ${{ if eq(parameters['Build StackifyLib Signed'], true) }}: - task: DotNetCoreCLI@2 displayName: Install NuGetKeyVaultSignTool continueOnError: true @@ -137,7 +142,7 @@ stages: enabled: False inputs: targetType: inline - script: "$files = @(Get-ChildItem -Path . -File -Filter *.nupkg)\n\nforeach($file in $files) {\n $BaseFilename = $file.BaseName\n $BaseFilenameSplit = $file.BaseName.Split(\".\")\n $LastVersionDigit = \"\"\n $NewFilenameBase = \"\"\n $PackageVersion = \"\"\n $IsBeta = \"False\"\n if ($BaseFilename.contains(\"beta\")) {\n $IsBeta = \"True\"\n $LastVersionDigit = $file.BaseName.Substring($BaseFilename.length - 6,1)\n }\n else {\n $LastVersionDigit = $file.BaseName.Substring($BaseFilename.length - 1)\n }\n foreach($namePart in $BaseFilenameSplit) {\n if (!$namePart.contains(\"beta\") -and $namePart -notmatch \"^\\d+$\") {\n $NewFilenameBase = $NewFilenameBase + $namePart + \".\"\n } else {\n # check for beta\n if ($namePart.contains(\"beta\")) {\n $PackageVersion = $PackageVersion + $namePart.Substring(0,1)\n } else {\n $PackageVersion = $PackageVersion + $namePart\n }\n # check length to append a .\n if ($namePart -notmatch $LastVersionDigit) {\n $PackageVersion = $PackageVersion + \".\"\n }\n }\n }\n \n # check beta\n $FinalFilenameBase = \"\"\n if ($IsBeta -match \"True\") {\n $FinalFilenameBase = $NewFilenameBase + \"snk.\" + $PackageVersion + \"-beta\"\n } else {\n $FinalFilenameBase = $NewFilenameBase + \"snk.\" + $PackageVersion\n }\n $FinalFilename = $FinalFilenameBase + $file.Extension\n Copy-Item -Path $file -NewName $FinalFilename\n}" + script: "$files = @(Get-ChildItem -Path . -File -Filter *.nupkg)\n\nforeach($file in $files) {\n $BaseFilename = $file.BaseName\n $BaseFilenameSplit = $file.BaseName.Split(\".\")\n $LastVersionDigit = \"\"\n $NewFilenameBase = \"\"\n $PackageVersion = \"\"\n $IsBeta = \"False\"\n if ($BaseFilename.contains(\"beta\")) {\n $IsBeta = \"True\"\n $LastVersionDigit = $file.BaseName.Substring($BaseFilename.length - 6,1)\n }\n else {\n $LastVersionDigit = $file.BaseName.Substring($BaseFilename.length - 1)\n }\n foreach($namePart in $BaseFilenameSplit) {\n if (!$namePart.contains(\"beta\") -and $namePart -notmatch \"^\\d+$\") {\n $NewFilenameBase = $NewFilenameBase + $namePart + \".\"\n } else {\n # check for beta\n if ($namePart.contains(\"beta\")) {\n $PackageVersion = $PackageVersion + $namePart.Substring(0,1)\n } else {\n $PackageVersion = $PackageVersion + $namePart\n }\n # check length to append a .\n if ($namePart -notmatch $LastVersionDigit) {\n $PackageVersion = $PackageVersion + \".\"\n }\n }\n }\n \n # check beta\n $FinalFilenameBase = \"\"\n if ($IsBeta -match \"True\") {\n $FinalFilenameBase = $NewFilenameBase + \"snk.\" + $PackageVersion + \"-beta\"\n } else {\n $FinalFilenameBase = $NewFilenameBase + \"snk.\" + $PackageVersion\n }\n $FinalFilename = $FinalFilenameBase + $file.Extension\n Rename-Item -Path $file -NewName $FinalFilename\n}" workingDirectory: $(Build.ArtifactStagingDirectory) - task: PublishPipelineArtifact@1 displayName: Publish Signed Artifact From 9c9060ca4dbf68431cbfe9d958e210108ef2003a Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Tue, 4 Jun 2024 22:34:48 +0800 Subject: [PATCH 06/40] Fix pipeline signed and unsigned --- azure-pipelines.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9c3219e..114e690 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -78,19 +78,6 @@ stages: Src\Nlog.Targets.Stackify\*.csproj Src\NLog.Web.Stackify\*.csproj Src\StackifyLib.StackifyTraceListener\*.csproj - - ${{ if eq(parameters['Build StackifyLib'], true) }}: - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - projects: | - Src\StackifyLib\*.csproj - arguments: '-c $(BuildConfiguration)' - - task: DotNetCoreCLI@2 - displayName: dotnet pack unsigned stackify lib - inputs: - command: pack - searchPatternPack: Src\StackifyLib\*.csproj; - nobuild: true - ${{ if eq(parameters['Build StackifyLib Signed'], true) }}: - task: DownloadSecureFile@1 name: SNK @@ -189,6 +176,19 @@ stages: targetType: inline script: > (Get-Content -path Src\StackifyLib\StackifyLib.csproj -Raw) -replace 'StackifyLib.signed','StackifyLib' | Set-Content -Path Src\StackifyLib\StackifyLib.csproj + - ${{ if eq(parameters['Build StackifyLib'], true) }}: + - task: DotNetCoreCLI@2 + displayName: dotnet build + inputs: + projects: | + Src\StackifyLib\*.csproj + arguments: '-c $(BuildConfiguration)' + - task: DotNetCoreCLI@2 + displayName: dotnet pack unsigned stackify lib + inputs: + command: pack + searchPatternPack: Src\StackifyLib\*.csproj; + nobuild: true - ${{ if eq(parameters['Build StackifyLib.AspNetCore'], true) }}: - template: templates/build-and-pack.yml parameters: From 99246181d531937c5ce84f5ee2f4e821d8fca926 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Tue, 4 Jun 2024 22:36:22 +0800 Subject: [PATCH 07/40] Fix block for stackifylib pipeline --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 114e690..3b4cfea 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -176,7 +176,7 @@ stages: targetType: inline script: > (Get-Content -path Src\StackifyLib\StackifyLib.csproj -Raw) -replace 'StackifyLib.signed','StackifyLib' | Set-Content -Path Src\StackifyLib\StackifyLib.csproj - - ${{ if eq(parameters['Build StackifyLib'], true) }}: + - ${{ if eq(parameters['Build StackifyLib'], true) }}: - task: DotNetCoreCLI@2 displayName: dotnet build inputs: From a232c994ab32e0c45c578d978752daf4ebc1f922 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Tue, 4 Jun 2024 22:39:41 +0800 Subject: [PATCH 08/40] Add build for stackify lib signed --- azure-pipelines.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3b4cfea..6ffcddf 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -107,7 +107,13 @@ stages: } displayName: Uncoment signing attribute - task: DotNetCoreCLI@2 - displayName: dotnet pack unsigned stackify lib + displayName: dotnet build + inputs: + projects: | + Src\StackifyLib\*.csproj + arguments: '-c $(BuildConfiguration)' + - task: DotNetCoreCLI@2 + displayName: dotnet pack signed stackify lib inputs: command: pack searchPatternPack: Src\StackifyLib\*.csproj; From 652ea9e7141854c60e87fdd3db82c116d9f85d47 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Thu, 13 Jun 2024 23:41:10 +0800 Subject: [PATCH 09/40] Updating StackifyLib version to 2.2.14 --- Src/StackifyLib/StackifyLib.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 961f4eb..42a9a48 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -13,7 +13,7 @@ false false false - 2.2.14-beta + 2.2.14 StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE From 9f557102acd32d6a546f05733fbaad2a2e45e681 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sat, 29 Jun 2024 02:59:08 +0800 Subject: [PATCH 10/40] DOTNET-178: Initial logic fix for GetDeviceName --- Src/StackifyLib/Models/EnvironmentDetail.cs | 34 +++++++-------- .../StackifyLib.UnitTests/ConfigJSON_Tests.cs | 5 +++ .../JsonSerialization_Tests.cs | 5 +++ .../Models/EnvironmentDetail_Tests.cs | 43 +++++++++++++++++++ .../StackifyLib.UnitTests.csproj | 21 ++++++--- 5 files changed, 85 insertions(+), 23 deletions(-) create mode 100644 test/StackifyLib.UnitTests/Models/EnvironmentDetail_Tests.cs diff --git a/Src/StackifyLib/Models/EnvironmentDetail.cs b/Src/StackifyLib/Models/EnvironmentDetail.cs index f16d4ff..9ed9954 100644 --- a/Src/StackifyLib/Models/EnvironmentDetail.cs +++ b/Src/StackifyLib/Models/EnvironmentDetail.cs @@ -30,9 +30,9 @@ public static EnvironmentDetail Get() return _CachedCopy ?? (_CachedCopy = new EnvironmentDetail()); } #if NETSTANDARD - private static System.Net.Http.HttpClient Client => new System.Net.Http.HttpClient(); + private System.Net.Http.HttpClient Client => new System.Net.Http.HttpClient(); #endif - private static bool registryAccessFailure = false; + private bool registryAccessFailure = false; /// /// Figures out if the server is in azure and if so gets the azure instance name @@ -110,23 +110,23 @@ private void GetAzureInfo() #endif } - private static bool? _isIMDSv1; + private bool? _isIMDSv1; // http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html#d0e30002 private const string EC2InstanceIdUrl = "http://169.254.169.254/latest/meta-data/instance-id"; private const string IMDS_BASE_URL = "http://169.254.169.254/latest"; private const string IMDS_TOKEN_PATH = "/api/token"; private const string IMDS_INSTANCE_ID_PATH = "/meta-data/instance-id"; private const string IMDSV1_BASE_URL = "http://169.254.169.254/latest/meta-data/"; - public static readonly object ec2InstanceLock = new object(); - private static DateTimeOffset? ec2InstanceIdLastUpdate = null; - private static string ec2InstanceId = null; + public static readonly object ec2InstanceLock = new object(); + private DateTimeOffset? ec2InstanceIdLastUpdate = null; + private string ec2InstanceId = string.Empty; /// /// Get the EC2 Instance name if it exists else null /// #if NETFULL - public static string GetDeviceName() + public string GetDeviceName() { var deviceName = Environment.GetEnvironmentVariable("STACKIFY_DEVICE_NAME"); if (!String.IsNullOrEmpty(deviceName)) @@ -139,7 +139,7 @@ public static string GetDeviceName() var isDefaultDeviceNameEc2 = IsEc2MachineName(deviceName); - if (Config.IsEc2 == null || Config.IsEc2 == true || isDefaultDeviceNameEc2) + if ((Config.IsEc2.HasValue && Config.IsEc2 == true) || isDefaultDeviceNameEc2) { var instanceID_task = GetEC2InstanceId(); if (string.IsNullOrWhiteSpace(instanceID_task) == false) @@ -151,7 +151,7 @@ public static string GetDeviceName() return deviceName.Substring(0, deviceName.Length > 60 ? 60 : deviceName.Length); } - public static bool IsIMDSv1() + public bool IsIMDSv1() { if (_isIMDSv1.HasValue) @@ -174,7 +174,7 @@ public static bool IsIMDSv1() } } - public static string GetAccessToken() + public string GetAccessToken() { var url = IMDS_BASE_URL + IMDS_TOKEN_PATH; var httpRequest = (HttpWebRequest)WebRequest.Create(url); @@ -190,7 +190,7 @@ public static string GetAccessToken() } - public static string GetEC2InstanceId() + public string GetEC2InstanceId() { string r = null; @@ -258,7 +258,7 @@ public static string GetEC2InstanceId() return r; } #else - public static string GetDeviceName() + public string GetDeviceName() { var deviceName = Environment.GetEnvironmentVariable("STACKIFY_DEVICE_NAME"); if (!String.IsNullOrEmpty(deviceName)) @@ -271,7 +271,7 @@ public static string GetDeviceName() var isDefaultDeviceNameEc2 = IsEc2MachineName(deviceName); - if (Config.IsEc2 == null || Config.IsEc2 == true || isDefaultDeviceNameEc2) + if ((Config.IsEc2.HasValue && Config.IsEc2 == true) || isDefaultDeviceNameEc2) { var instanceID_task = GetEC2InstanceId(); instanceID_task.Wait(); @@ -284,7 +284,7 @@ public static string GetDeviceName() return deviceName.Substring(0, deviceName.Length > 60 ? 60 : deviceName.Length); } - public static async Task GetAccessTokenAsync() + public async Task GetAccessTokenAsync() { var url = IMDS_BASE_URL + IMDS_TOKEN_PATH; var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Put, url); @@ -294,7 +294,7 @@ public static async Task GetAccessTokenAsync() return await response.Content.ReadAsStringAsync().ConfigureAwait(false); } - public static async Task IsIMDSv1() + public async Task IsIMDSv1() { if (_isIMDSv1.HasValue) { @@ -314,7 +314,7 @@ public static async Task IsIMDSv1() } } - public static async Task GetEC2InstanceId() + public async Task GetEC2InstanceId() { string r = null; try @@ -345,7 +345,7 @@ public static async Task GetEC2InstanceId() } #endif - private static bool IsEc2MachineName(string machineName) + private bool IsEc2MachineName(string machineName) { if (string.IsNullOrWhiteSpace(machineName)) { diff --git a/test/StackifyLib.UnitTests/ConfigJSON_Tests.cs b/test/StackifyLib.UnitTests/ConfigJSON_Tests.cs index 3b4fbce..06d23de 100644 --- a/test/StackifyLib.UnitTests/ConfigJSON_Tests.cs +++ b/test/StackifyLib.UnitTests/ConfigJSON_Tests.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2024 BMC Software, Inc. +// Copyright (c) 2021-2024 Netreo +// Copyright (c) 2019 Stackify +#if NETCORE using System; using System.Collections.Generic; using System.Diagnostics; @@ -125,3 +129,4 @@ private void ResetConfig() } } +#endif \ No newline at end of file diff --git a/test/StackifyLib.UnitTests/JsonSerialization_Tests.cs b/test/StackifyLib.UnitTests/JsonSerialization_Tests.cs index 68ca968..14803d5 100644 --- a/test/StackifyLib.UnitTests/JsonSerialization_Tests.cs +++ b/test/StackifyLib.UnitTests/JsonSerialization_Tests.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2024 BMC Software, Inc. +// Copyright (c) 2021-2024 Netreo +// Copyright (c) 2019 Stackify +#if NETCORE using System; using System.Collections.Generic; using System.Diagnostics; @@ -134,3 +138,4 @@ private static object GetTestObject() } } } +#endif \ No newline at end of file diff --git a/test/StackifyLib.UnitTests/Models/EnvironmentDetail_Tests.cs b/test/StackifyLib.UnitTests/Models/EnvironmentDetail_Tests.cs new file mode 100644 index 0000000..c571ec0 --- /dev/null +++ b/test/StackifyLib.UnitTests/Models/EnvironmentDetail_Tests.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2024 BMC Software, Inc. +// Copyright (c) 2021-2024 Netreo +// Copyright (c) 2019 Stackify +using StackifyLib.Models; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Moq; + +namespace StackifyLib.UnitTests.Models +{ + public class EnvironmentDetail_Tests + { +#if NETCORE + [Fact] + public async Task TestIfConfigEc2IsNull() + { + var envService = new EnvironmentDetail(); + Assert.Equal(Environment.MachineName, envService.GetDeviceName()); + + var type = envService.GetType(); + var property = type.GetField("ec2InstanceId", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.Equal(property.GetValue(envService) as string, string.Empty); + } +#endif +#if NETFULL + [Fact] + public void TestIfConfigEc2IsNull() + { + var envService = new EnvironmentDetail(); + Assert.Equal(Environment.MachineName, envService.GetDeviceName()); + + var type = envService.GetType(); + var property = type.GetField("ec2InstanceId", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.Equal(property.GetValue(envService) as string, string.Empty); + } +#endif + } +} diff --git a/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj b/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj index 4408082..f7cf30a 100644 --- a/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj +++ b/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj @@ -1,16 +1,25 @@  - netcoreapp2.1 + net6.0;net462 + false + true + + NETCORE + + + + NETFULL + + - - - all - runtime; build; native; contentfiles; analyzers - + + + + From 4a970db4f2515ce20aca819d22c8a55614269de1 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sat, 29 Jun 2024 06:06:23 +0800 Subject: [PATCH 11/40] Updating StackifyLib version to 2.2.15-beta --- Src/StackifyLib/StackifyLib.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 42a9a48..5eb441c 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -13,15 +13,15 @@ false false false - 2.2.14 + 2.2.15-beta StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet git https://stackify.com/wp-content/uploads/2017/02/stk.png - 2.2.14.0 - 2.2.14.0 + 2.2.15.0 + 2.2.15.0 Remove default internal file logger false From e3faffc822df41ad47141dc8f0ad581826613e0b Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sat, 29 Jun 2024 06:18:07 +0800 Subject: [PATCH 12/40] Updating StackifyLib version prefix to 2.2.15 --- Src/StackifyLib/StackifyLib.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 5eb441c..8988b14 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -2,7 +2,7 @@ Stackify API - 2.2.14 + 2.2.15 netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib From 42f5d12ef9a252abc655f022096976b3261f4652 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Tue, 2 Jul 2024 15:35:56 +0800 Subject: [PATCH 13/40] DOTNET-179: Update log4net and StackifyLib reference for StackifyLib.log4net --- Src/StackifyLib.log4net/StackifyLib.log4net.csproj | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Src/StackifyLib.log4net/StackifyLib.log4net.csproj b/Src/StackifyLib.log4net/StackifyLib.log4net.csproj index a02870b..c858f37 100644 --- a/Src/StackifyLib.log4net/StackifyLib.log4net.csproj +++ b/Src/StackifyLib.log4net/StackifyLib.log4net.csproj @@ -2,7 +2,8 @@ Stackify Log4Net Appender - 2.0.0 + 2.1.7 + beta netstandard2.0;net40;net45 StackifyLib.log4net StackifyLib.log4net @@ -13,7 +14,7 @@ false false false - 2.1.6 + 2.1.7-beta https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet @@ -22,11 +23,8 @@ - - - - - + + From f2f3f78a50d4417a68bbcc988d934e29ee5c6252 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Wed, 17 Jul 2024 21:14:10 +0800 Subject: [PATCH 14/40] Updating StackifyLib version to 2.2.15 --- Src/StackifyLib/StackifyLib.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 8988b14..7ccfaf4 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -13,7 +13,7 @@ false false false - 2.2.15-beta + 2.2.15 StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE From f89a99ab50155b7d90679e404053cc6177268cb1 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sat, 14 Sep 2024 03:48:41 +0800 Subject: [PATCH 15/40] DOTNET-188: Adjust InnerException string before the '--- End of inner exception stack trace ---' line --- .../StackifyLib.log4net.Tests.csproj | 12 +-- Src/StackifyLib.log4net.Tests/packages.config | 3 +- Src/StackifyLib/Models/ErrorItem.cs | 10 +-- Src/StackifyLibCore.sln | 9 +-- .../Models/ErrorItem_Tests.cs | 78 +++++++++++++++++++ .../StackifyLib.UnitTests.csproj | 2 +- 6 files changed, 94 insertions(+), 20 deletions(-) create mode 100644 test/StackifyLib.UnitTests/Models/ErrorItem_Tests.cs diff --git a/Src/StackifyLib.log4net.Tests/StackifyLib.log4net.Tests.csproj b/Src/StackifyLib.log4net.Tests/StackifyLib.log4net.Tests.csproj index 75acbae..66781be 100644 --- a/Src/StackifyLib.log4net.Tests/StackifyLib.log4net.Tests.csproj +++ b/Src/StackifyLib.log4net.Tests/StackifyLib.log4net.Tests.csproj @@ -33,8 +33,8 @@ 4 - - ..\packages\log4net.2.0.11\lib\net45\log4net.dll + + ..\packages\log4net.2.0.17\lib\net45\log4net.dll ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll @@ -42,9 +42,13 @@ ..\packages\NUnit.3.13.2\lib\net45\nunit.framework.dll + + ..\packages\StackifyLib.2.2.15\lib\net452\StackifyLib.dll + + @@ -65,10 +69,6 @@ {7d98e5e3-a3e7-4613-8bb7-80a60d86a5e8} StackifyLib.log4net - - {03cf7411-65e2-4998-a157-9224eca6199b} - StackifyLib - diff --git a/Src/StackifyLib.log4net.Tests/packages.config b/Src/StackifyLib.log4net.Tests/packages.config index f89ee51..19fcb11 100644 --- a/Src/StackifyLib.log4net.Tests/packages.config +++ b/Src/StackifyLib.log4net.Tests/packages.config @@ -1,6 +1,7 @@  - + + \ No newline at end of file diff --git a/Src/StackifyLib/Models/ErrorItem.cs b/Src/StackifyLib/Models/ErrorItem.cs index b14c7e1..4692b00 100644 --- a/Src/StackifyLib/Models/ErrorItem.cs +++ b/Src/StackifyLib/Models/ErrorItem.cs @@ -298,15 +298,15 @@ public string FramesToString() { StringBuilder sb = new StringBuilder(); - foreach (var item in this.StackTrace) + if (InnerError != null) { - sb.AppendFormat(" at {0}\r\n", item.Method); + sb.Append(InnerError.FramesToString()); + sb.Append("--- End of inner exception stack trace ---\r\n"); } - if (InnerError != null) + foreach (var item in this.StackTrace) { - sb.Append("--- End of inner exception stack trace ---\r\n"); - sb.Append(InnerError.FramesToString()); + sb.AppendFormat(" at {0}\r\n", item.Method); } return sb.ToString(); diff --git a/Src/StackifyLibCore.sln b/Src/StackifyLibCore.sln index a726538..6f15531 100644 --- a/Src/StackifyLibCore.sln +++ b/Src/StackifyLibCore.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30907.101 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35303.130 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StackifyLib", "StackifyLib\StackifyLib.csproj", "{03CF7411-65E2-4998-A157-9224ECA6199B}" EndProject @@ -71,11 +71,9 @@ Global {D0E1AABD-6397-4CC7-9737-4904A4BF5BCD}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0E1AABD-6397-4CC7-9737-4904A4BF5BCD}.Release|Any CPU.Build.0 = Release|Any CPU {2B832B34-BA30-4B5A-8E78-D6DC78AF16FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2B832B34-BA30-4B5A-8E78-D6DC78AF16FB}.Debug|Any CPU.Build.0 = Debug|Any CPU {2B832B34-BA30-4B5A-8E78-D6DC78AF16FB}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B832B34-BA30-4B5A-8E78-D6DC78AF16FB}.Release|Any CPU.Build.0 = Release|Any CPU {BC6FDF2B-E063-4F8B-ABBA-6BD9425D4C64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BC6FDF2B-E063-4F8B-ABBA-6BD9425D4C64}.Debug|Any CPU.Build.0 = Debug|Any CPU {BC6FDF2B-E063-4F8B-ABBA-6BD9425D4C64}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC6FDF2B-E063-4F8B-ABBA-6BD9425D4C64}.Release|Any CPU.Build.0 = Release|Any CPU {CB09D719-F393-413D-B5CD-E0125543AD24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -99,15 +97,12 @@ Global {647F27AD-13D7-40C2-A7BB-97A5D19CE3D5}.Release|Any CPU.ActiveCfg = Release|Any CPU {647F27AD-13D7-40C2-A7BB-97A5D19CE3D5}.Release|Any CPU.Build.0 = Release|Any CPU {ADBD2138-9DE7-4211-AF58-84C7F0F9BEA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ADBD2138-9DE7-4211-AF58-84C7F0F9BEA5}.Debug|Any CPU.Build.0 = Debug|Any CPU {ADBD2138-9DE7-4211-AF58-84C7F0F9BEA5}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADBD2138-9DE7-4211-AF58-84C7F0F9BEA5}.Release|Any CPU.Build.0 = Release|Any CPU {99C81723-20FB-4241-862C-32F7ADDB7A9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {99C81723-20FB-4241-862C-32F7ADDB7A9B}.Debug|Any CPU.Build.0 = Debug|Any CPU {99C81723-20FB-4241-862C-32F7ADDB7A9B}.Release|Any CPU.ActiveCfg = Release|Any CPU {99C81723-20FB-4241-862C-32F7ADDB7A9B}.Release|Any CPU.Build.0 = Release|Any CPU {EC5742DF-C1A8-4A7A-81DC-B6E0A4D3472D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EC5742DF-C1A8-4A7A-81DC-B6E0A4D3472D}.Debug|Any CPU.Build.0 = Debug|Any CPU {EC5742DF-C1A8-4A7A-81DC-B6E0A4D3472D}.Release|Any CPU.ActiveCfg = Release|Any CPU {EC5742DF-C1A8-4A7A-81DC-B6E0A4D3472D}.Release|Any CPU.Build.0 = Release|Any CPU {18F96933-468E-4104-961E-0C68A5EF2B29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU diff --git a/test/StackifyLib.UnitTests/Models/ErrorItem_Tests.cs b/test/StackifyLib.UnitTests/Models/ErrorItem_Tests.cs new file mode 100644 index 0000000..633b046 --- /dev/null +++ b/test/StackifyLib.UnitTests/Models/ErrorItem_Tests.cs @@ -0,0 +1,78 @@ +// Copyright (c) 2024 BMC Software, Inc. +// Copyright (c) 2021-2024 Netreo +// Copyright (c) 2019 Stackify +using StackifyLib.Models; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Moq; + +namespace StackifyLib.UnitTests.Models +{ + public class ErrorItem_Tests + { + + [Fact] + public async Task TestExceptionStringWithInnerException() + { + var expectedString = string.Join( + Environment.NewLine, + "System.Exception: Wrapping the exception to see the differences between outer and inner exceptions. ---> System.Exception: Throwing this exception to see all properties like stacktrace filled", + " at StackifyLib.UnitTests.Models.ExampleService.ThrowInnerException()", + " at StackifyLib.UnitTests.Models.ExampleService.CreateCaughtException()", + "--- End of inner exception stack trace ---", + " at StackifyLib.UnitTests.Models.ExampleService.ThrowOuterException(System.Exception)", + " at StackifyLib.UnitTests.Models.ExampleService.CreateCaughtException()", + " at StackifyLib.UnitTests.Models.ErrorItem_Tests+d__0.MoveNext()", + "" + ); + try + { + //ExampleService service = new ExampleService(); + ExampleService.CreateCaughtException(); + } + catch (Exception ex) + { + //PrintExceptionInfo(ex); + var exceptionString = new ErrorItem(ex).ToString(); + Assert.Contains(expectedString, exceptionString); + } + } + } + + class ExampleService + { + public static Exception CreateCaughtException() + { + try + { + try + { + ThrowInnerException(); + } + catch (Exception exception) + { + ThrowOuterException(exception); + } + } + catch (Exception) + { + throw; + } + // NOTE: never reached, but needed for the compiler. + return null; + } + private static void ThrowOuterException(Exception exception) + { + throw new Exception("Wrapping the exception to see the differences between outer and inner exceptions.", exception); + } + private static void ThrowInnerException() + { + throw new Exception("Throwing this exception to see all properties like stacktrace filled"); + } + } +} diff --git a/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj b/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj index f7cf30a..a4c6e25 100644 --- a/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj +++ b/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj @@ -16,7 +16,7 @@ - + From deb6682261b1ddb398a77e0eaf7ef2902e441c12 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sat, 14 Sep 2024 04:58:36 +0800 Subject: [PATCH 16/40] Update keyvault service name --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6ffcddf..055fcfa 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -63,7 +63,7 @@ stages: - task: AzureKeyVault@2 displayName: 'Azure Key Vault: keyvault-build-resources' inputs: - ConnectedServiceName: 514ed7d6-3846-4422-8013-af27483dd22c + ConnectedServiceName: d5f05656-fdf4-44f0-a545-12ee3fc9ae77 KeyVaultName: keyvault-build-resources RunAsPreJob: true - task: DotNetCoreCLI@2 From 47383851764e5621619c39b4a915b95664ad89c5 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sat, 14 Sep 2024 05:24:28 +0800 Subject: [PATCH 17/40] Update connected service name to BuildServices --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 055fcfa..3751875 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -63,7 +63,7 @@ stages: - task: AzureKeyVault@2 displayName: 'Azure Key Vault: keyvault-build-resources' inputs: - ConnectedServiceName: d5f05656-fdf4-44f0-a545-12ee3fc9ae77 + ConnectedServiceName: BuildServices KeyVaultName: keyvault-build-resources RunAsPreJob: true - task: DotNetCoreCLI@2 From 227b648d3839425ad84bee00f1161edda161c046 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sat, 14 Sep 2024 06:16:35 +0800 Subject: [PATCH 18/40] Update connected service name to Build Service --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3751875..3554134 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -63,7 +63,7 @@ stages: - task: AzureKeyVault@2 displayName: 'Azure Key Vault: keyvault-build-resources' inputs: - ConnectedServiceName: BuildServices + ConnectedServiceName: 'Build Service' KeyVaultName: keyvault-build-resources RunAsPreJob: true - task: DotNetCoreCLI@2 From fe5ede56429e14c863f2229ffda377fd9fa082cc Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sat, 14 Sep 2024 06:21:49 +0800 Subject: [PATCH 19/40] Updating StackifyLib version to 2.2.16-beta --- Src/StackifyLib/StackifyLib.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 7ccfaf4..4fda604 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -2,7 +2,8 @@ Stackify API - 2.2.15 + 2.2.16 + beta netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib @@ -13,7 +14,7 @@ false false false - 2.2.15 + 2.2.16-beta StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE From b88596e25ef5bdb6f9f4a791261194c716bb8f95 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sat, 14 Sep 2024 06:30:29 +0800 Subject: [PATCH 20/40] Updating StackifyLib.log4net version to 2.1.7 --- Src/StackifyLib.log4net/StackifyLib.log4net.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/StackifyLib.log4net/StackifyLib.log4net.csproj b/Src/StackifyLib.log4net/StackifyLib.log4net.csproj index c858f37..3fd6483 100644 --- a/Src/StackifyLib.log4net/StackifyLib.log4net.csproj +++ b/Src/StackifyLib.log4net/StackifyLib.log4net.csproj @@ -3,7 +3,7 @@ Stackify Log4Net Appender 2.1.7 - beta + netstandard2.0;net40;net45 StackifyLib.log4net StackifyLib.log4net @@ -14,7 +14,7 @@ false false false - 2.1.7-beta + 2.1.7 https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet From 57ec215a3a9d701780d7df0e4e31da636e9843f7 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Fri, 11 Oct 2024 02:49:39 +0800 Subject: [PATCH 21/40] Updating StackifyLib version to 2.2.16 --- Src/StackifyLib/StackifyLib.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 4fda604..36c165e 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -3,7 +3,7 @@ Stackify API 2.2.16 - beta + netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib @@ -14,15 +14,15 @@ false false false - 2.2.16-beta + 2.2.16 StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet git https://stackify.com/wp-content/uploads/2017/02/stk.png - 2.2.15.0 - 2.2.15.0 + 2.2.16.0 + 2.2.16.0 Remove default internal file logger false From 034f66599e735e1cdef6a2914ce8d1e10b2c98b8 Mon Sep 17 00:00:00 2001 From: Todd Lair Date: Fri, 10 Jan 2025 16:23:12 -0600 Subject: [PATCH 22/40] Changed the connected service name --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3554134..1126baa 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -63,7 +63,7 @@ stages: - task: AzureKeyVault@2 displayName: 'Azure Key Vault: keyvault-build-resources' inputs: - ConnectedServiceName: 'Build Service' + ConnectedServiceName: 5313674e-7e2b-426b-9eaf-8d3d5bd22e2c KeyVaultName: keyvault-build-resources RunAsPreJob: true - task: DotNetCoreCLI@2 From c57e624a746242ff7d687cea94a448e51318ed2f Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Fri, 16 May 2025 16:34:16 +0100 Subject: [PATCH 23/40] DOTNET-199: Update NLog.Targets.Stackify NLog dependency private to allow 4.x and 5.x --- Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj b/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj index 4406a50..3e217cc 100644 --- a/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj +++ b/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj @@ -2,7 +2,8 @@ NLog.Targets.Stackify - 2.0.0 + 3.0.0 + beta1 netstandard2.0;net40;net45;net461 NLog.Targets.Stackify NLog.Targets.Stackify @@ -15,7 +16,7 @@ false false false - 2.2.0 + 3.0.0-beta1 https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet https://stackify.com/wp-content/uploads/2017/02/stk.png @@ -25,8 +26,8 @@ - - + + From be17c61a0943ee6a12403903f732b38c09aec60f Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Tue, 27 May 2025 19:03:00 +0100 Subject: [PATCH 24/40] DOTNET-199: Add NLog 5.x as dependency for NLog.Targets.Stackify --- Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj | 2 +- samples/CoreConsoleApp/CoreConsoleApp.csproj | 4 ++-- samples/CoreWebApp/CoreWebApp.csproj | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj b/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj index 3e217cc..f694755 100644 --- a/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj +++ b/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj @@ -26,7 +26,7 @@ - + diff --git a/samples/CoreConsoleApp/CoreConsoleApp.csproj b/samples/CoreConsoleApp/CoreConsoleApp.csproj index 70540e5..0196127 100644 --- a/samples/CoreConsoleApp/CoreConsoleApp.csproj +++ b/samples/CoreConsoleApp/CoreConsoleApp.csproj @@ -13,7 +13,6 @@ - @@ -22,7 +21,8 @@ - + + diff --git a/samples/CoreWebApp/CoreWebApp.csproj b/samples/CoreWebApp/CoreWebApp.csproj index 9dc3766..c8ce504 100644 --- a/samples/CoreWebApp/CoreWebApp.csproj +++ b/samples/CoreWebApp/CoreWebApp.csproj @@ -16,7 +16,6 @@ - @@ -31,7 +30,8 @@ - + + From a42035ebf2e3d2c30a625e6a3964901da16f4e73 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Tue, 27 May 2025 19:13:19 +0100 Subject: [PATCH 25/40] Updating NLog.Targets.Stackify version to 3.0.0-beta2 --- Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj b/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj index f694755..7e99cfe 100644 --- a/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj +++ b/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj @@ -3,7 +3,7 @@ NLog.Targets.Stackify 3.0.0 - beta1 + beta2 netstandard2.0;net40;net45;net461 NLog.Targets.Stackify NLog.Targets.Stackify @@ -16,7 +16,7 @@ false false false - 3.0.0-beta1 + 3.0.0-beta2 https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet https://stackify.com/wp-content/uploads/2017/02/stk.png From 43f462adc665788e342d01bb8f5261a59636ea0d Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Wed, 4 Jun 2025 00:27:48 +0100 Subject: [PATCH 26/40] Updating NLog.Targets.Stackify version to 3.0.0 --- Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj b/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj index 7e99cfe..ff29ab3 100644 --- a/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj +++ b/Src/NLog.Targets.Stackify/NLog.Targets.Stackify.csproj @@ -3,7 +3,6 @@ NLog.Targets.Stackify 3.0.0 - beta2 netstandard2.0;net40;net45;net461 NLog.Targets.Stackify NLog.Targets.Stackify @@ -16,7 +15,7 @@ false false false - 3.0.0-beta2 + 3.0.0 https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet https://stackify.com/wp-content/uploads/2017/02/stk.png From cb374cd5133d9aabba9a9ea99fc3f5efc508979e Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Thu, 5 Jun 2025 01:34:47 +0100 Subject: [PATCH 27/40] DOTNET-200: Fix environment name retrieval for azure config instance name --- Src/StackifyLib/Models/AzureConfig.cs | 91 ++++-- Src/StackifyLib/Models/EnvironmentDetail.cs | 6 +- Src/StackifyLib/StackifyLib.csproj | 6 +- Src/StackifyLib/Utils/HttpClient.cs | 9 +- Src/StackifyLib/Utils/ServerConfigHelper.cs | 34 --- .../Models/AzureConfig_Tests.cs | 268 ++++++++++++++++++ 6 files changed, 348 insertions(+), 66 deletions(-) delete mode 100644 Src/StackifyLib/Utils/ServerConfigHelper.cs create mode 100644 test/StackifyLib.UnitTests/Models/AzureConfig_Tests.cs diff --git a/Src/StackifyLib/Models/AzureConfig.cs b/Src/StackifyLib/Models/AzureConfig.cs index 8dad9a0..230d53a 100644 --- a/Src/StackifyLib/Models/AzureConfig.cs +++ b/Src/StackifyLib/Models/AzureConfig.cs @@ -1,10 +1,8 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; +// Copyright (c) 2024-2025 BMC Software, Inc. +// Copyright (c) 2021-2024 Netreo +// Copyright (c) 2019 Stackify +using System; using StackifyLib.Utils; -using System.Linq; #if NETFULL using Microsoft.Win32; #endif @@ -13,22 +11,26 @@ namespace StackifyLib.Models { public class AzureConfig { - private static bool? _inAzure; - private static AzureRoleType _azureRoleType = AzureRoleType.Unknown; - private static string _azureRoleName; - private static string _azureInstanceName; - private static string _entryPoint; + private static AzureConfig _instance = new AzureConfig(); + public static AzureConfig Instance => _instance; - private static readonly DateTime AppStarted = DateTime.UtcNow; + private bool? _inAzure; + private AzureRoleType _azureRoleType = AzureRoleType.Unknown; + private string _azureRoleName; + private string _azureInstanceName; + private string _entryPoint; - private static readonly object Locker = new object(); + private readonly DateTime AppStarted = DateTime.UtcNow; + private readonly object Locker = new object(); + public const string ProductionEnvName = "Production"; - public static string AzureAppWebConfigEnvironment { get; set; } - public static string AzureAppWebConfigApiKey { get; set; } - public static string AzureDriveLetter { get; set; } - public static string AzureInstanceName + public string AzureAppWebConfigEnvironment { get; set; } + public string AzureAppWebConfigApiKey { get; set; } + public string AzureDriveLetter { get; set; } + + public string AzureInstanceName { get { @@ -37,7 +39,7 @@ public static string AzureInstanceName } } - public static bool InAzure + public bool InAzure { get { @@ -62,7 +64,7 @@ public static bool InAzure } } - public static bool IsWebsite + public bool IsWebsite { get { @@ -75,12 +77,24 @@ public static bool IsWebsite } } - private static void EnsureInAzureRan() + private EnvironmentDetail _environmentDetail = null; + + public AzureConfig() + { + + } + + public AzureConfig(EnvironmentDetail environmentDetail) + { + _environmentDetail = environmentDetail; + } + + private void EnsureInAzureRan() { bool ensureTestHasBeenDone = InAzure; } - public static void LoadAzureSettings() + public void LoadAzureSettings() { if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME")) == false && string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID")) == false) { @@ -90,13 +104,13 @@ public static void LoadAzureSettings() var slotId = GetDeploymentSlotId() ?? "0000"; _azureRoleName = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"); - _azureInstanceName = $"{Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME")} {ServerConfigHelper.GetEnvironment()} [{slotId}-{Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID").Left(6)}]"; + _azureInstanceName = $"{Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME")} {GetEnvironment()} [{slotId}-{Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID").Left(6)}]"; return; } } - public static string GetDeploymentSlotId() + public string GetDeploymentSlotId() { try { @@ -113,6 +127,37 @@ public static string GetDeploymentSlotId() return null; } + + public string GetEnvironment() + { + if (IsWebsite) + { + if (_environmentDetail == null) + { + _environmentDetail = EnvironmentDetail.Get(); + } + + var key = _environmentDetail.ConfiguredEnvironmentName; + if (string.IsNullOrEmpty(key)) + { + key = Environment.GetEnvironmentVariable("Stackify.Environment"); + } + + if (string.IsNullOrEmpty(key) == false) + { + return key; + } + + if (string.IsNullOrEmpty(AzureAppWebConfigEnvironment) == false) + { + return AzureAppWebConfigEnvironment; + } + + return ProductionEnvName; + } + + return string.Empty; + } } public enum AzureRoleType : short diff --git a/Src/StackifyLib/Models/EnvironmentDetail.cs b/Src/StackifyLib/Models/EnvironmentDetail.cs index 9ed9954..9fbbd04 100644 --- a/Src/StackifyLib/Models/EnvironmentDetail.cs +++ b/Src/StackifyLib/Models/EnvironmentDetail.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2024 BMC Software, Inc. +// Copyright (c) 2024-2025 BMC Software, Inc. // Copyright (c) 2021-2024 Netreo // Copyright (c) 2019 Stackify @@ -41,9 +41,9 @@ private void GetAzureInfo() { //IsAzureWorkerRole is set when instantiating EnvironmentDetail //Useful in other parts directly referencing AzureInstanceName - if(!IsAzureWorkerRole && AzureConfig.InAzure && AzureConfig.IsWebsite) + if(!IsAzureWorkerRole && AzureConfig.Instance.InAzure && AzureConfig.Instance.IsWebsite) { - AzureInstanceName = AzureConfig.AzureInstanceName; + AzureInstanceName = AzureConfig.Instance.AzureInstanceName; } diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 36c165e..e70855c 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -2,8 +2,8 @@ Stackify API - 2.2.16 - + 2.2.17 + beta1 netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib @@ -14,7 +14,7 @@ false false false - 2.2.16 + 2.2.17-beta1 StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE diff --git a/Src/StackifyLib/Utils/HttpClient.cs b/Src/StackifyLib/Utils/HttpClient.cs index c8619d2..937fdae 100644 --- a/Src/StackifyLib/Utils/HttpClient.cs +++ b/Src/StackifyLib/Utils/HttpClient.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) 2024-2025 BMC Software, Inc. +// Copyright (c) 2021-2024 Netreo +// Copyright (c) 2019 Stackify +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -313,9 +316,9 @@ public bool IdentifyApp() } //Applicable only for Azure AppService - if(AzureConfig.InAzure && AzureConfig.IsWebsite) + if(AzureConfig.Instance.InAzure && AzureConfig.Instance.IsWebsite) { - env.DeviceName = AzureConfig.AzureInstanceName; + env.DeviceName = AzureConfig.Instance.AzureInstanceName; } string jsonData = JsonConvert.SerializeObject(env, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); diff --git a/Src/StackifyLib/Utils/ServerConfigHelper.cs b/Src/StackifyLib/Utils/ServerConfigHelper.cs deleted file mode 100644 index 67834c6..0000000 --- a/Src/StackifyLib/Utils/ServerConfigHelper.cs +++ /dev/null @@ -1,34 +0,0 @@ -using StackifyLib.Models; -using System; -using System.Collections.Generic; -using System.Text; - -namespace StackifyLib.Utils -{ - public class ServerConfigHelper - { - public const string ProductionEnvName = "Production"; - - public static string GetEnvironment() - { - if (AzureConfig.IsWebsite) - { - var key = Environment.GetEnvironmentVariable("Stackify.Environment"); - - if (string.IsNullOrEmpty(key) == false) - { - return key; - } - - if (string.IsNullOrEmpty(AzureConfig.AzureAppWebConfigEnvironment) == false) - { - return AzureConfig.AzureAppWebConfigEnvironment; - } - - return ProductionEnvName; - } - - return string.Empty; - } - } -} diff --git a/test/StackifyLib.UnitTests/Models/AzureConfig_Tests.cs b/test/StackifyLib.UnitTests/Models/AzureConfig_Tests.cs new file mode 100644 index 0000000..10b44d3 --- /dev/null +++ b/test/StackifyLib.UnitTests/Models/AzureConfig_Tests.cs @@ -0,0 +1,268 @@ +// Copyright (c) 2024-2025 BMC Software, Inc. +using StackifyLib.Models; +using System; +using System.Collections.Generic; +using Xunit; +using Moq; +#if NETCORE || NETCOREX +using Microsoft.Extensions.Configuration; +#endif + +namespace StackifyLib.UnitTests.Models +{ + public class AzureConfig_Tests + { + private readonly MockRepository _mockRepository; + private readonly Dictionary _originalEnvironment = new Dictionary(); + + public AzureConfig_Tests() + { + _mockRepository = new MockRepository(MockBehavior.Strict); + BackupEnvironmentVariables(); + } + + public void Dispose() + { + RestoreEnvironmentVariables(); + } + + private void BackupEnvironmentVariables() + { + _originalEnvironment["WEBSITE_SITE_NAME"] = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"); + _originalEnvironment["WEBSITE_INSTANCE_ID"] = Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"); + _originalEnvironment["WEBSITE_IIS_SITE_NAME"] = Environment.GetEnvironmentVariable("WEBSITE_IIS_SITE_NAME"); + _originalEnvironment["Stackify.Environment"] = Environment.GetEnvironmentVariable("Stackify.Environment"); + } + + private void RestoreEnvironmentVariables() + { + foreach (var kvp in _originalEnvironment) + { + Environment.SetEnvironmentVariable(kvp.Key, kvp.Value); + } + } + + private void ResetAzureConfig() + { + var instanceField = typeof(AzureConfig).GetField("_instance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + instanceField.SetValue(null, new AzureConfig()); +#if NETCORE || NETCOREX + Config.SetConfiguration(null); +#endif + } + + private void SetEnvironmentVariables(string siteName, string instanceId, string iisSiteName = null) + { + Environment.SetEnvironmentVariable("WEBSITE_SITE_NAME", siteName); + Environment.SetEnvironmentVariable("WEBSITE_INSTANCE_ID", instanceId); + Environment.SetEnvironmentVariable("WEBSITE_IIS_SITE_NAME", iisSiteName); + } + + [Fact] + public void InAzure_ReturnsTrue_WhenAzureEnvironmentDetected() + { + SetEnvironmentVariables("my-site", "instance-id"); + ResetAzureConfig(); + + var result = new AzureConfig(new EnvironmentDetail()).InAzure; + Assert.True(result); + } + + [Fact] + public void InAzure_ReturnsFalse_WhenNotInAzure() + { + SetEnvironmentVariables(null, null); + ResetAzureConfig(); + + var result = new AzureConfig(new EnvironmentDetail()).InAzure; + Assert.False(result); + } + + [Fact] + public void IsWebsite_ReturnsTrue_WhenWebAppDetected() + { + SetEnvironmentVariables("my-site", "instance-id"); + ResetAzureConfig(); + + var result = new AzureConfig(new EnvironmentDetail()).IsWebsite; + Assert.True(result); + } + + [Fact] + public void AzureInstanceName_CorrectFormat_WhenInWebApp() + { + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site Production [1234-full-i]", instanceName); + } + + [Fact] + public void GetDeploymentSlotId_ReturnsLast4Chars_WhenSlotPresent() + { + SetEnvironmentVariables(null, null, "site-name__abcd"); + ResetAzureConfig(); + + var slotId = new AzureConfig(new EnvironmentDetail()).GetDeploymentSlotId(); + Assert.Equal("abcd", slotId); + } + + [Fact] + public void GetDeploymentSlotId_ReturnsNull_WhenNoSlotPresent() + { + SetEnvironmentVariables(null, null, "site-name"); + ResetAzureConfig(); + + var slotId = new AzureConfig(new EnvironmentDetail()).GetDeploymentSlotId(); + Assert.Null(slotId); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void GetEnvironment_ReturnsConfiguredEnvironment_WhenSet() + { + SetEnvironmentVariables("site", "instance"); + Environment.SetEnvironmentVariable("Stackify.Environment", "Staging"); + ResetAzureConfig(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("Staging", env); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void AzureInstanceName_CorrectFormat_WhenEnvStagingTestSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", "StagingTest"); + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site StagingTest [1234-full-i]", instanceName); + } + + [Fact] + public void GetEnvironment_ReturnsWebConfigValue_WhenNoEnvVarSet() + { + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + + var env = new AzureConfig(new EnvironmentDetail()) + { + AzureAppWebConfigEnvironment = "Test" + }.GetEnvironment(); + Assert.Equal("Test", env); + } + + [Fact] + public void GetEnvironment_ReturnsProduction_WhenNoConfigurationSet() + { + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + Config.LoadSettings(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("Production", env); + } + + [Fact] + public void GetEnvironment_ReturnsEmptyString_WhenNotWebsite() + { + SetEnvironmentVariables(null, null); // Not in Azure + ResetAzureConfig(); + + var env = new AzureConfig().GetEnvironment(); + Assert.Equal(string.Empty, env); + } + +#if NETFULL + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void GetEnvironment_ReturnsTest_WhenAppSettingsSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", ""); + System.Configuration.ConfigurationManager.AppSettings["Stackify.Environment"] = "Test"; + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("Test", env); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void AzureInstanceName_CorrectFormat_WhenAppSettingsEnvStagingAppSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", ""); + System.Configuration.ConfigurationManager.AppSettings["Stackify.Environment"] = "StagingApp"; + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site StagingApp [1234-full-i]", instanceName); + } +#endif + +#if NETCORE || NETCOREX + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void GetEnvironment_ReturnsTestCore_WhenIConfigurationSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", ""); + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + + var iconfig = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Stackify:Environment"] = "TestCore" + }) + .Build(); + Config.SetConfiguration(iconfig); + Config.LoadSettings(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("TestCore", env); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void AzureInstanceName_CorrectFormat_WhenIConfigurationEnvStagingConfigSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", ""); + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var iconfig = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Stackify:Environment"] = "StagingConfig" + }) + .Build(); + Config.SetConfiguration(iconfig); + Config.LoadSettings(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site StagingConfig [1234-full-i]", instanceName); + } +#endif + + [Fact] + public void AzureRoleType_SetToWebApp_WhenWebsiteDetected() + { + // Arrange + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + + // Act + bool _ = AzureConfig.Instance.InAzure; // Force initialization + var roleType = GetPrivateField("_azureRoleType"); + + // Assert + Assert.Equal(AzureRoleType.WebApp, roleType); + } + + // Helper to access private fields + private T GetPrivateField(string fieldName) + { + var field = typeof(AzureConfig).GetField(fieldName, + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + return (T)field.GetValue(AzureConfig.Instance); + } + } +} From 38400dc3dc688ee50f55c6b90e468f7b50cfb130 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Thu, 5 Jun 2025 23:27:08 +0100 Subject: [PATCH 28/40] DOTNET-200: Check Stackify.Environment first then default to normal config env and add tests --- Src/StackifyLib/Models/AzureConfig.cs | 9 +-- .../Models/AzureConfig_Tests.cs | 77 +++++++++++++++---- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/Src/StackifyLib/Models/AzureConfig.cs b/Src/StackifyLib/Models/AzureConfig.cs index 230d53a..7f1eefa 100644 --- a/Src/StackifyLib/Models/AzureConfig.cs +++ b/Src/StackifyLib/Models/AzureConfig.cs @@ -137,10 +137,10 @@ public string GetEnvironment() _environmentDetail = EnvironmentDetail.Get(); } - var key = _environmentDetail.ConfiguredEnvironmentName; + var key = Environment.GetEnvironmentVariable("Stackify.Environment"); if (string.IsNullOrEmpty(key)) { - key = Environment.GetEnvironmentVariable("Stackify.Environment"); + key = _environmentDetail.ConfiguredEnvironmentName; } if (string.IsNullOrEmpty(key) == false) @@ -148,11 +148,6 @@ public string GetEnvironment() return key; } - if (string.IsNullOrEmpty(AzureAppWebConfigEnvironment) == false) - { - return AzureAppWebConfigEnvironment; - } - return ProductionEnvName; } diff --git a/test/StackifyLib.UnitTests/Models/AzureConfig_Tests.cs b/test/StackifyLib.UnitTests/Models/AzureConfig_Tests.cs index 10b44d3..7ef334e 100644 --- a/test/StackifyLib.UnitTests/Models/AzureConfig_Tests.cs +++ b/test/StackifyLib.UnitTests/Models/AzureConfig_Tests.cs @@ -140,19 +140,6 @@ public void AzureInstanceName_CorrectFormat_WhenEnvStagingTestSet() Assert.Equal("my-site StagingTest [1234-full-i]", instanceName); } - [Fact] - public void GetEnvironment_ReturnsWebConfigValue_WhenNoEnvVarSet() - { - SetEnvironmentVariables("site", "instance"); - ResetAzureConfig(); - - var env = new AzureConfig(new EnvironmentDetail()) - { - AzureAppWebConfigEnvironment = "Test" - }.GetEnvironment(); - Assert.Equal("Test", env); - } - [Fact] public void GetEnvironment_ReturnsProduction_WhenNoConfigurationSet() { @@ -187,6 +174,18 @@ public void GetEnvironment_ReturnsTest_WhenAppSettingsSet() Assert.Equal("Test", env); } + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void GetEnvironment_ReturnsTest_WhenEnvSetFirst_AppSettingsSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", "StagingFirstApp"); + System.Configuration.ConfigurationManager.AppSettings["Stackify.Environment"] = "Test"; + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("StagingFirstApp", env); + } + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] public void AzureInstanceName_CorrectFormat_WhenAppSettingsEnvStagingAppSet() { @@ -198,6 +197,18 @@ public void AzureInstanceName_CorrectFormat_WhenAppSettingsEnvStagingAppSet() var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; Assert.Equal("my-site StagingApp [1234-full-i]", instanceName); } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void AzureInstanceName_CorrectFormat_WhenEnvSetFirst_AppSettings() + { + Environment.SetEnvironmentVariable("Stackify.Environment", "StagingFirst"); + System.Configuration.ConfigurationManager.AppSettings["Stackify.Environment"] = "StagingApp"; + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site StagingFirst [1234-full-i]", instanceName); + } #endif #if NETCORE || NETCOREX @@ -221,6 +232,46 @@ public void GetEnvironment_ReturnsTestCore_WhenIConfigurationSet() Assert.Equal("TestCore", env); } + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void GetEnvironment_ReturnsTestCore_WhenEnvFirst_IConfigurationSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", "TestCoreFirst"); + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + + var iconfig = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Stackify:Environment"] = "TestCore" + }) + .Build(); + Config.SetConfiguration(iconfig); + Config.LoadSettings(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("TestCoreFirst", env); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void AzureInstanceName_CorrectFormat_WhenEnvSetFirst_IConfigurationEnvStagingConfigSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", "StagingConfigFirst"); + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var iconfig = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Stackify:Environment"] = "StagingConfig" + }) + .Build(); + Config.SetConfiguration(iconfig); + Config.LoadSettings(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site StagingConfigFirst [1234-full-i]", instanceName); + } + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] public void AzureInstanceName_CorrectFormat_WhenIConfigurationEnvStagingConfigSet() { From 3cd224f22a064b8f7db9c39f5a570189c1822c43 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Fri, 6 Jun 2025 01:11:43 +0100 Subject: [PATCH 29/40] DOTNET-200: Update assembly version to 2.2.17 --- Src/StackifyLib/StackifyLib.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index e70855c..10eb0d0 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -21,8 +21,8 @@ https://github.com/stackify/stackify-api-dotnet git https://stackify.com/wp-content/uploads/2017/02/stk.png - 2.2.16.0 - 2.2.16.0 + 2.2.17.0 + 2.2.17.0 Remove default internal file logger false From 02f0149887ac2df28e7562aa70ee690983b5a521 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Mon, 21 Jul 2025 13:37:32 +0100 Subject: [PATCH 30/40] Updating StackifyLib version to 2.2.17-beta --- Src/StackifyLib/StackifyLib.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 10eb0d0..4f53677 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -3,7 +3,7 @@ Stackify API 2.2.17 - beta1 + beta netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib @@ -14,7 +14,7 @@ false false false - 2.2.17-beta1 + 2.2.17-beta StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE From a0641bd3c82569437ea16ea42dbac354add9da55 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Thu, 11 Sep 2025 01:49:49 +0100 Subject: [PATCH 31/40] Updating StackifyLib version to 2.2.17 --- Src/StackifyLib/StackifyLib.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 4f53677..986cd6b 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -3,7 +3,7 @@ Stackify API 2.2.17 - beta + netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib @@ -14,7 +14,7 @@ false false false - 2.2.17-beta + 2.2.17 StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE From 993ebfcdf5a7f04a6f96dcc14e4b386a2e68ce51 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sun, 14 Sep 2025 00:27:19 +0100 Subject: [PATCH 32/40] DOTNET-202: Initial proxy support (custom and default) and HttpClient.CustomRequestModifier --- README.md | 19 + Src/StackifyLib/Config.cs | 10 + Src/StackifyLib/StackifyLib.csproj | 11 +- Src/StackifyLib/Utils/HttpClient.cs | 90 +- .../StackifyLib.UnitTests.csproj | 3 +- .../Utils/HttpClient_Tests.cs | 860 ++++++++++++++++++ 6 files changed, 956 insertions(+), 37 deletions(-) create mode 100644 test/StackifyLib.UnitTests/Utils/HttpClient_Tests.cs diff --git a/README.md b/README.md index 0309ea1..ce51bd5 100644 --- a/README.md +++ b/README.md @@ -65,16 +65,35 @@ StackifyLib.Logger.GlobalEnvironment = ""; By default the library will use the WebRequest.DefaultWebProxy. If you want to set a specific proxy server only for StackifyLib, you can do so in code OR via config. +For proxy using custom credentials: ```xml ``` +For proxy using default credentials: +```xml + + + +``` + +For proxy that needs to be tailor fit ```csharp StackifyLib.Utils.HttpClient.CustomWebProxy = new WebProxy(); ``` +In case it need some web request intervention, a delegate has been provided to customize the http request +```csharp +StackifyLib.Utils.HttpClient.CustomRequestModifier = (request) => +{ + // Examples: + request.PreAuthenticate = true; // If proxy needs pre-authentication + request.Timeout = 30000; +}; +``` + If you are having problems you can get logging out of the framework by hooking in to its custom logging. ```csharp diff --git a/Src/StackifyLib/Config.cs b/Src/StackifyLib/Config.cs index 23e0c6f..f8322c4 100644 --- a/Src/StackifyLib/Config.cs +++ b/Src/StackifyLib/Config.cs @@ -166,6 +166,13 @@ public static void LoadSettings() { RumKey = rumKey; } + + ProxyServer = Get("Stackify.ProxyServer", string.Empty); + var proxyUseDefaultCredentials = Get("Stackify.ProxyUseDefaultCredentials", string.Empty); + if (string.IsNullOrWhiteSpace(proxyUseDefaultCredentials) == false) + { + ProxyUseDefaultCredentials = proxyUseDefaultCredentials.Equals(bool.TrueString, StringComparison.CurrentCultureIgnoreCase); + } } catch (Exception ex) { @@ -217,6 +224,9 @@ public static void LoadSettings() public static string RumKey { get; set; } + public static string ProxyServer { get; set; } + public static bool? ProxyUseDefaultCredentials { get; set; } = false; + /// /// Attempts to fetch a setting value given the key. diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 986cd6b..06b04e0 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -2,8 +2,8 @@ Stackify API - 2.2.17 - + 2.2.18 + beta netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib @@ -14,15 +14,15 @@ false false false - 2.2.17 + 2.2.18-beta StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet git https://stackify.com/wp-content/uploads/2017/02/stk.png - 2.2.17.0 - 2.2.17.0 + 2.2.18.0 + 2.2.18.0 Remove default internal file logger false @@ -57,5 +57,4 @@ NETFULL - diff --git a/Src/StackifyLib/Utils/HttpClient.cs b/Src/StackifyLib/Utils/HttpClient.cs index 937fdae..1f376a9 100644 --- a/Src/StackifyLib/Utils/HttpClient.cs +++ b/Src/StackifyLib/Utils/HttpClient.cs @@ -31,7 +31,9 @@ public class AppIdentityInfo public class HttpClient { - public static IWebProxy CustomWebProxy = null; + public static IWebProxy CustomWebProxy = null; + public static Action CustomRequestModifier = null; + public static bool IsUnitTest = false; public string BaseAPIUrl { get; private set; } @@ -121,39 +123,31 @@ public HttpClient(string apiKey, string apiUrl) BaseAPIUrl += "/"; } -#if NETFULL public static void LoadWebProxyConfig() { try { - string val = Config.Get("Stackify.ProxyServer"); - + string val = Config.ProxyServer; if (!string.IsNullOrEmpty(val)) { - StackifyAPILogger.Log("Setting proxy server based on override config", true); - var uri = new Uri(val); - - var proxy = new WebProxy(uri, false); + WebProxy proxy = new WebProxy(uri); if (!string.IsNullOrEmpty(uri.UserInfo) && uri.UserInfo.Contains(":")) { string[] pieces = uri.UserInfo.Split(':'); - - proxy.Credentials = new NetworkCredential(pieces[0], pieces[1]); + proxy = new WebProxy(uri) + { + Credentials = new NetworkCredential(pieces[0], pieces[1]) + }; } else { - - string settingUseDefault = Config.Get("Stackify.ProxyUseDefaultCredentials"); - - bool useDefault; - - if (!string.IsNullOrEmpty(settingUseDefault) && bool.TryParse(settingUseDefault, out useDefault)) + bool? settingUseDefault = Config.ProxyUseDefaultCredentials; + if (settingUseDefault.HasValue && settingUseDefault.Value) { - //will make it use the user of the running windows service - proxy.UseDefaultCredentials = useDefault; + proxy.UseDefaultCredentials = true; } } CustomWebProxy = proxy; @@ -164,7 +158,6 @@ public static void LoadWebProxyConfig() StackifyAPILogger.Log("Error setting default web proxy " + ex.Message, true); } } -#endif /// /// This method does some throttling when errors happen to control when it should try again. Error backoff logic @@ -614,23 +607,40 @@ private HttpWebRequest BuildJsonRequest(string url, string jsonData, bool compre request.Headers[HttpRequestHeader.UserAgent] = "StackifyLib-" + _version; #endif - request.Headers["X-Stackify-Key"] = this.APIKey; + request.Headers.Add("X-Stackify-Key", this.APIKey); request.ContentType = "application/json"; - //if (HttpClient.CustomWebProxy != null) - //{ + if (CustomWebProxy != null) + { + request.Proxy = CustomWebProxy; + } - // request.Proxy = HttpClient.CustomWebProxy; - //} + if (CustomRequestModifier != null) + { + try + { + CustomRequestModifier(request); + } + catch (Exception ex) + { + StackifyAPILogger.Log($"Failed to set CustomRequestModifier - Message: {ex.Message}", true); + } + } if (!string.IsNullOrEmpty(jsonData) && compress) { request.Method = "POST"; - request.Headers[HttpRequestHeader.ContentEncoding] = "gzip"; + request.Headers.Add(HttpRequestHeader.ContentEncoding, "gzip"); + + if (IsUnitTest) + { + return request; + } byte[] payload = Encoding.UTF8.GetBytes(jsonData); #if NETFULL + request.ContentLength = payload.Length; using (Stream postStream = request.GetRequestStream()) #else using (Stream postStream = request.GetRequestStreamAsync().GetAwaiter().GetResult()) @@ -647,8 +657,12 @@ private HttpWebRequest BuildJsonRequest(string url, string jsonData, bool compre { request.Method = "POST"; - byte[] payload = Encoding.UTF8.GetBytes(jsonData); + if (IsUnitTest) + { + return request; + } + byte[] payload = Encoding.UTF8.GetBytes(jsonData); #if NETFULL request.ContentLength= payload.Length; using (Stream stream = request.GetRequestStream()) @@ -697,14 +711,30 @@ private HttpWebRequest BuildPOSTRequest(string url, string postdata) request.Headers[HttpRequestHeader.ContentLength] = "0"; #endif - //if (HttpClient.CustomWebProxy != null) - //{ - // request.Proxy = HttpClient.CustomWebProxy; - //} + if (HttpClient.CustomWebProxy != null) + { + request.Proxy = HttpClient.CustomWebProxy; + } + if (CustomRequestModifier != null) + { + try + { + CustomRequestModifier(request); + } + catch (Exception ex) + { + StackifyAPILogger.Log($"Failed to set CustomRequestModifier - Message: {ex.Message}", true); + } + } request.Method = "POST"; + if (IsUnitTest) + { + return request; + } + if (!String.IsNullOrEmpty(postdata)) { byte[] payload = Encoding.UTF8.GetBytes(postdata); diff --git a/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj b/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj index a4c6e25..7d03e51 100644 --- a/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj +++ b/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj @@ -4,7 +4,9 @@ net6.0;net462 false true + true + NETCORE @@ -13,7 +15,6 @@ NETFULL - diff --git a/test/StackifyLib.UnitTests/Utils/HttpClient_Tests.cs b/test/StackifyLib.UnitTests/Utils/HttpClient_Tests.cs new file mode 100644 index 0000000..102160c --- /dev/null +++ b/test/StackifyLib.UnitTests/Utils/HttpClient_Tests.cs @@ -0,0 +1,860 @@ +// Copyright (c) 2024-2025 BMC Software, Inc. +// Copyright (c) 2021-2024 Netreo +// Copyright (c) 2019 Stackify +using Moq; +using StackifyLib.Models; +using StackifyLib.Utils; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Reflection; +using System.Security.Policy; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace StackifyLib.UnitTests.Utils +{ + public class HttpClient_Tests : IDisposable + { + private readonly HttpClient _httpClient; + private IWebProxy _originalProxy; + private Action _originalRequestModifier; + + public HttpClient_Tests() + { + // Store original values to restore after tests + _originalProxy = HttpClient.CustomWebProxy; + _originalRequestModifier = HttpClient.CustomRequestModifier; + + _httpClient = new HttpClient("test-api-key", "https://test.api.com/"); + HttpClient.IsUnitTest = true; + } + + public void Dispose() + { + // Restore original values + HttpClient.CustomWebProxy = _originalProxy; + HttpClient.CustomRequestModifier = _originalRequestModifier; + HttpClient.IsUnitTest = false; + } + + [Fact] + public void Constructor_SetsBaseAPIUrl_WithTrailingSlash() + { + var client = new HttpClient("test-key", "https://api.test.com"); + + Assert.Equal("https://api.test.com/", client.BaseAPIUrl); + } + + [Fact] + public void Constructor_PreservesTrailingSlash_WhenAlreadyPresent() + { + var client = new HttpClient("test-key", "https://api.test.com/"); + + Assert.Equal("https://api.test.com/", client.BaseAPIUrl); + } + + [Fact] + public void CustomWebProxy_CanBeSetAndRetrieved() + { + var testProxy = new WebProxy("http://proxy.test.com:8080"); + + HttpClient.CustomWebProxy = testProxy; + + Assert.Equal(testProxy, HttpClient.CustomWebProxy); + } + + [Fact] + public void CustomRequestModifier_CanBeSetAndRetrieved() + { + var testModifier = new Action(req => req.Timeout = 30000); + + HttpClient.CustomRequestModifier = testModifier; + + Assert.Equal(testModifier, HttpClient.CustomRequestModifier); + } + + [Fact] + public void CustomRequestModifier_ModifiesRequest() + { + bool modifierCalled = false; + string capturedUserAgent = null; + + HttpClient.CustomRequestModifier = (request) => + { + modifierCalled = true; + capturedUserAgent = request.Headers["User-Agent"]; + request.Timeout = 30000; + }; + + // Use reflection to test the BuildJsonRequest method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var finalRequest = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{}", false }); + + Assert.True(modifierCalled); + Assert.Equal(30000, finalRequest.Timeout); + Assert.NotNull(capturedUserAgent); + } + + [Fact] + public void CustomRequestModifier_ModifiesPOSTRequest() + { + bool modifierCalled = false; + + HttpClient.CustomRequestModifier = (request) => + { + modifierCalled = true; + request.Timeout = 45000; + }; + + // Use reflection to test the BuildPOSTRequest method + var method = typeof(HttpClient).GetMethod("BuildPOSTRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var finalRequest = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "data=test" }); + + Assert.True(modifierCalled); + Assert.Equal(45000, finalRequest.Timeout); + } + + [Fact] + public void BuildJsonRequest_SetsCustomWebProxy_WhenProvided() + { + var testProxy = new WebProxy("http://proxy1.test.com:8080"); + HttpClient.CustomWebProxy = testProxy; + + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{}", false }); + + Assert.Equal(testProxy, request.Proxy); + } + + [Fact] + public void BuildPOSTRequest_SetsCustomWebProxy_WhenProvided() + { + var testProxy = new WebProxy("http://proxy.test.com:8080"); + HttpClient.CustomWebProxy = testProxy; + + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildPOSTRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "data=test" }); + + Assert.Equal(testProxy, request.Proxy); + } + + [Fact] + public void BuildJsonRequest_DoesNotSetProxy_WhenCustomWebProxyIsNull() + { + HttpClient.CustomWebProxy = null; + + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{}", false }); + + // When CustomWebProxy is null, the request should use default proxy settings + Assert.NotEqual(HttpClient.CustomWebProxy, request.Proxy); + } + + [Fact] + public void BuildJsonRequest_SetsCorrectHeaders() + { + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{\"test\": \"data\"}", false }); + + Assert.Equal("application/json", request.ContentType); + Assert.Equal("test-api-key", request.Headers["X-Stackify-Key"]); + Assert.Equal("POST", request.Method); +#if NETFULL + Assert.Contains("StackifyLib-", request.UserAgent); +#else + Assert.Contains("StackifyLib-", request.Headers["User-Agent"]); +#endif + } + + [Fact] + public void BuildJsonRequest_WithCompression_SetsCorrectHeaders() + { + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{\"test\": \"data\"}", true }); + + Assert.Equal("application/json", request.ContentType); + Assert.Equal("gzip", request.Headers["Content-Encoding"]); + Assert.Equal("POST", request.Method); + } + + [Fact] + public void BuildJsonRequest_WithEmptyJsonData_UsesGETMethod() + { + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "", false }); + + Assert.Equal("GET", request.Method); + } + + [Fact] + public void BuildPOSTRequest_SetsCorrectHeaders() + { + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildPOSTRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "data=test" }); + + Assert.Equal("application/x-www-form-urlencoded", request.ContentType); + Assert.Equal("test-api-key", request.Headers["X-Stackify-Key"]); + Assert.Equal("POST", request.Method); +#if NETFULL + Assert.Contains("StackifyLib-", request.UserAgent); +#else + Assert.Contains("StackifyLib-", request.Headers["User-Agent"]); +#endif + } + + [Fact] + public void ProxyAndCustomModifier_WorkTogether() + { + var testProxy = new WebProxy("http://proxy.test.com:8080"); + HttpClient.CustomWebProxy = testProxy; + + bool modifierCalled = false; + HttpClient.CustomRequestModifier = (request) => + { + modifierCalled = true; + request.Timeout = 60000; + }; + + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var finalRequest = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{}", false }); + + Assert.Equal(testProxy, finalRequest.Proxy); + Assert.True(modifierCalled); + Assert.Equal(60000, finalRequest.Timeout); + } + + [Fact] + public void CustomRequestModifier_DoesNotThrow_WhenNull() + { + HttpClient.CustomRequestModifier = null; + + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + // Should not throw exception when CustomRequestModifier is null + var exception = Record.Exception(() => + method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{}", false }) + ); + + Assert.Null(exception); + } + +#if NETFULL + [Fact] + public void LoadWebProxyConfig_SetsCustomWebProxy_FromConfig() + { + // This test would need to mock the Config.ProxyServer property + // For now, we'll test that the method exists and can be called + var method = typeof(HttpClient).GetMethod("LoadWebProxyConfig", BindingFlags.Public | BindingFlags.Static); + Assert.NotNull(method); + + // Should not throw exception + var exception = Record.Exception(() => method.Invoke(null, null)); + Assert.Null(exception); + } +#endif + + [Theory] + [InlineData("http://proxy.example.com:8080")] + [InlineData("https://secure-proxy.example.com:443", true)] + [InlineData("http://user:pass@proxy.example.com:3128")] + public void CustomWebProxy_AcceptsDifferentProxyUrls(string proxyUrl, bool throwsException = false) + { + var testProxy = new WebProxy(proxyUrl); + HttpClient.CustomWebProxy = testProxy; + + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + if (throwsException) + { +#if NETFULL + Assert.Throws(() => (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{}", false })); +#else + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{}", false }); + Assert.Equal(testProxy, request.Proxy); +#endif + } + else + { + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{}", false }); + Assert.Equal(testProxy, request.Proxy); + } + } + + [Fact] + public void CustomRequestModifier_CanModifyMultipleProperties() + { + string customHeaderValue = "CustomValue123"; + + HttpClient.CustomRequestModifier = (request) => + { + request.Timeout = 120000; + request.Headers.Add("X-Custom-Header", customHeaderValue); + request.KeepAlive = false; + }; + + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var finalRequest = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{}", false }); + + Assert.Equal(120000, finalRequest.Timeout); + Assert.Equal(customHeaderValue, finalRequest.Headers["X-Custom-Header"]); + Assert.False(finalRequest.KeepAlive); + } + + [Fact] + public void ProxyConfiguration_RespectsOrder_ProxyThenCustomModifier() + { + var testProxy = new WebProxy("http://proxy.test.com:8080"); + HttpClient.CustomWebProxy = testProxy; + + // Custom modifier that verifies proxy was set first + bool proxyWasSetBeforeModifier = false; + HttpClient.CustomRequestModifier = (request) => + { + proxyWasSetBeforeModifier = (request.Proxy == testProxy); + request.Timeout = 25000; + }; + + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var finalRequest = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{}", false }); + + Assert.True(proxyWasSetBeforeModifier, "Proxy should be set before CustomRequestModifier is called"); + Assert.Equal(testProxy, finalRequest.Proxy); + Assert.Equal(25000, finalRequest.Timeout); + } + + [Fact] + public void CustomRequestModifier_CanOverrideProxySettings() + { + var initialProxy = new WebProxy("http://initial-proxy.test.com:8080"); + var overrideProxy = new WebProxy("http://override-proxy.test.com:3128"); + + HttpClient.CustomWebProxy = initialProxy; + + HttpClient.CustomRequestModifier = (request) => + { + // Override the proxy set by the framework + request.Proxy = overrideProxy; + }; + + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildJsonRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var finalRequest = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "{}", false }); + + Assert.Equal(overrideProxy, finalRequest.Proxy); + Assert.NotEqual(initialProxy, finalRequest.Proxy); + } + + [Fact] + public void BuildPOSTRequest_WithoutProxy_DoesNotSetProxy() + { + HttpClient.CustomWebProxy = null; + + // Use reflection to access private method + + HttpWebRequest requestDefault = (HttpWebRequest)WebRequest.Create("http://test.com"); + var method = typeof(HttpClient).GetMethod("BuildPOSTRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", "data=test" }); + Assert.Equivalent(request.Proxy, requestDefault.Proxy); + } + + [Theory] + [InlineData("http://proxy.test.com:8080", "")] + [InlineData("http://proxy.test.com:8080", "data=test")] + [InlineData("https://secure-proxy.test.com:443", "param1=value1¶m2=value2", true)] + public void BuildPOSTRequest_WithDifferentProxyAndData_SetsProxyCorrectly(string proxyUrl, string postData, bool throwsException = false) + { + var testProxy = new WebProxy(proxyUrl); + HttpClient.CustomWebProxy = testProxy; + + // Use reflection to access private method + var method = typeof(HttpClient).GetMethod("BuildPOSTRequest", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(method); + + + if (throwsException) + { +#if NETFULL + Assert.Throws(() => (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", postData })); +#else + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", postData }); + Assert.Equal(testProxy, request.Proxy); + Assert.Equal("POST", request.Method); + Assert.Equal("application/x-www-form-urlencoded", request.ContentType); +#endif + } + else + { + var request = (HttpWebRequest)method.Invoke(_httpClient, new object[] { "https://test.api.com/test", postData }); + Assert.Equal(testProxy, request.Proxy); + Assert.Equal("POST", request.Method); + Assert.Equal("application/x-www-form-urlencoded", request.ContentType); + } + + } + + [Fact] + public void APIKey_Property_ReturnsCorrectValue() + { + var client = new HttpClient("test-api-key", "https://test.api.com/"); + + // Access the APIKey property + Assert.Equal("test-api-key", client.APIKey); + } + + [Fact] + public void StackifyWebResponse_IsClientError_ReturnsCorrectValues() + { + var response = new HttpClient.StackifyWebResponse(); + + // Test 4xx status codes (client errors) + response.StatusCode = HttpStatusCode.BadRequest; + Assert.True(response.IsClientError()); + + response.StatusCode = HttpStatusCode.Unauthorized; + Assert.True(response.IsClientError()); + + response.StatusCode = HttpStatusCode.NotFound; + Assert.True(response.IsClientError()); + + // Test non-4xx status codes + response.StatusCode = HttpStatusCode.OK; + Assert.False(response.IsClientError()); + + response.StatusCode = HttpStatusCode.InternalServerError; + Assert.False(response.IsClientError()); + + response.StatusCode = HttpStatusCode.BadGateway; + Assert.False(response.IsClientError()); + } + + [Fact] + public void LoadWebProxyConfig_SetsUseDefaultCredentials_WhenConfigIsTrue() + { + // Arrange - Mock the Config.Get method to return true for UseDefaultCredentials + var originalProxyServer = Config.ProxyServer; + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + + try + { + // Set up a proxy server without credentials in URL + proxyServerProperty.SetValue(null, "http://proxy.test.com:8080"); + + // We'll test this by calling LoadWebProxyConfig and checking the result + HttpClient.LoadWebProxyConfig(); + + var customProxy = HttpClient.CustomWebProxy as WebProxy; + Assert.NotNull(customProxy); + + // Note: We can't easily test UseDefaultCredentials without mocking Config.Get + // This test verifies the method runs without error when proxy is configured + Assert.Equal("proxy.test.com", customProxy.Address.Host); + Assert.Equal(8080, customProxy.Address.Port); + } + finally + { + // Restore original state + proxyServerProperty.SetValue(null, originalProxyServer); + HttpClient.CustomWebProxy = null; + } + } + + [Fact] + public void LoadWebProxyConfig_SetsCredentials_WhenProxyHasUserInfo() + { + // Arrange + var originalProxyServer = Config.ProxyServer; + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + + try + { + // Set up a proxy server with credentials in URL + proxyServerProperty.SetValue(null, "http://testuser:testpass@proxy.test.com:8080"); + + // Act + HttpClient.LoadWebProxyConfig(); + + // Assert + var customProxy = HttpClient.CustomWebProxy as WebProxy; + Assert.NotNull(customProxy); + Assert.Equal("proxy.test.com", customProxy.Address.Host); + Assert.Equal(8080, customProxy.Address.Port); + + // Verify credentials were set + Assert.NotNull(customProxy.Credentials); + var networkCredential = customProxy.Credentials as NetworkCredential; + Assert.NotNull(networkCredential); + Assert.Equal("testuser", networkCredential.UserName); + Assert.Equal("testpass", networkCredential.Password); + } + finally + { + // Restore original state + proxyServerProperty.SetValue(null, originalProxyServer); + HttpClient.CustomWebProxy = null; + } + } + + [Fact] + public void LoadWebProxyConfig_DoesNotSetProxy_WhenProxyServerIsEmpty() + { + // Arrange + var originalProxyServer = Config.ProxyServer; + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + + try + { + // Set empty proxy server + proxyServerProperty.SetValue(null, string.Empty); + + // Act + HttpClient.LoadWebProxyConfig(); + + // Assert - CustomWebProxy should remain null + Assert.Null(HttpClient.CustomWebProxy); + } + finally + { + // Restore original state + proxyServerProperty.SetValue(null, originalProxyServer); + HttpClient.CustomWebProxy = null; + } + } + + [Fact] + public void LoadWebProxyConfig_HandlesException_WhenInvalidProxyUrl() + { + // Arrange + var originalProxyServer = Config.ProxyServer; + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + + try + { + // Set invalid proxy server URL + proxyServerProperty.SetValue(null, "invalid-url-format"); + + // Act - should not throw exception + var exception = Record.Exception(() => HttpClient.LoadWebProxyConfig()); + + // Assert - should handle exception gracefully + Assert.Null(exception); + // CustomWebProxy might be null or unchanged depending on the exception handling + } + finally + { + // Restore original state + proxyServerProperty.SetValue(null, originalProxyServer); + HttpClient.CustomWebProxy = null; + } + } + + [Theory] + [InlineData("http://proxy.test.com:8080")] + [InlineData("https://secure-proxy.test.com:443")] + [InlineData("http://proxy.company.com:3128")] + public void LoadWebProxyConfig_SetsCorrectProxyAddress_ForValidUrls(string proxyUrl) + { + // Arrange + var originalProxyServer = Config.ProxyServer; + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + + try + { + // Set proxy server + proxyServerProperty.SetValue(null, proxyUrl); + + // Act + HttpClient.LoadWebProxyConfig(); + + // Assert + var customProxy = HttpClient.CustomWebProxy as WebProxy; + Assert.NotNull(customProxy); + + var expectedUri = new Uri(proxyUrl); + Assert.Equal(expectedUri.Host, customProxy.Address.Host); + Assert.Equal(expectedUri.Port, customProxy.Address.Port); + Assert.Equal(expectedUri.Scheme, customProxy.Address.Scheme); + } + finally + { + // Restore original state + proxyServerProperty.SetValue(null, originalProxyServer); + HttpClient.CustomWebProxy = null; + } + } + [Fact] + public void LoadWebProxyConfig_WithProxyUseDefaultCredentials_True() + { + // Arrange - Save original values + var originalProxyServer = Config.ProxyServer; + var originalProxyUseDefaultCredentials = Config.ProxyUseDefaultCredentials; + + try + { + // Set up Config values + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + var proxyUseDefaultCredentialsProperty = configType.GetProperty("ProxyUseDefaultCredentials", BindingFlags.Public | BindingFlags.Static); + + proxyServerProperty.SetValue(null, "http://corporate-proxy.test.com:8080"); + proxyUseDefaultCredentialsProperty.SetValue(null, true); + + // Act + HttpClient.LoadWebProxyConfig(); + + // Assert + var customProxy = HttpClient.CustomWebProxy as WebProxy; + Assert.NotNull(customProxy); + Assert.Equal("corporate-proxy.test.com", customProxy.Address.Host); + Assert.Equal(8080, customProxy.Address.Port); + Assert.True(customProxy.UseDefaultCredentials); + Assert.NotNull(customProxy.Credentials); // No explicit credentials when using default + Assert.Empty((customProxy.Credentials as NetworkCredential).UserName); + } + finally + { + // Restore original values + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + var proxyUseDefaultCredentialsProperty = configType.GetProperty("ProxyUseDefaultCredentials", BindingFlags.Public | BindingFlags.Static); + + proxyServerProperty.SetValue(null, originalProxyServer); + proxyUseDefaultCredentialsProperty.SetValue(null, originalProxyUseDefaultCredentials); + HttpClient.CustomWebProxy = null; + } + } + + [Fact] + public void LoadWebProxyConfig_WithProxyUseDefaultCredentials_False() + { + // Arrange - Save original values + var originalProxyServer = Config.ProxyServer; + var originalProxyUseDefaultCredentials = Config.ProxyUseDefaultCredentials; + + try + { + // Set up Config values + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + var proxyUseDefaultCredentialsProperty = configType.GetProperty("ProxyUseDefaultCredentials", BindingFlags.Public | BindingFlags.Static); + + proxyServerProperty.SetValue(null, "http://corporate-proxy.test.com:8080"); + proxyUseDefaultCredentialsProperty.SetValue(null, false); + + // Act + HttpClient.LoadWebProxyConfig(); + + // Assert + var customProxy = HttpClient.CustomWebProxy as WebProxy; + Assert.NotNull(customProxy); + Assert.Equal("corporate-proxy.test.com", customProxy.Address.Host); + Assert.Equal(8080, customProxy.Address.Port); + Assert.False(customProxy.UseDefaultCredentials); + Assert.Null(customProxy.Credentials); + } + finally + { + // Restore original values + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + var proxyUseDefaultCredentialsProperty = configType.GetProperty("ProxyUseDefaultCredentials", BindingFlags.Public | BindingFlags.Static); + + proxyServerProperty.SetValue(null, originalProxyServer); + proxyUseDefaultCredentialsProperty.SetValue(null, originalProxyUseDefaultCredentials); + HttpClient.CustomWebProxy = null; + } + } + + [Fact] + public void LoadWebProxyConfig_WithProxyUseDefaultCredentials_Null() + { + // Arrange - Save original values + var originalProxyServer = Config.ProxyServer; + var originalProxyUseDefaultCredentials = Config.ProxyUseDefaultCredentials; + + try + { + // Set up Config values + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + var proxyUseDefaultCredentialsProperty = configType.GetProperty("ProxyUseDefaultCredentials", BindingFlags.Public | BindingFlags.Static); + + proxyServerProperty.SetValue(null, "http://corporate-proxy.test.com:8080"); + proxyUseDefaultCredentialsProperty.SetValue(null, null); // Explicitly set to null + + // Act + HttpClient.LoadWebProxyConfig(); + + // Assert + var customProxy = HttpClient.CustomWebProxy as WebProxy; + Assert.NotNull(customProxy); + Assert.Equal("corporate-proxy.test.com", customProxy.Address.Host); + Assert.Equal(8080, customProxy.Address.Port); + Assert.False(customProxy.UseDefaultCredentials); // Should remain false when null + Assert.Null(customProxy.Credentials); + } + finally + { + // Restore original values + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + var proxyUseDefaultCredentialsProperty = configType.GetProperty("ProxyUseDefaultCredentials", BindingFlags.Public | BindingFlags.Static); + + proxyServerProperty.SetValue(null, originalProxyServer); + proxyUseDefaultCredentialsProperty.SetValue(null, originalProxyUseDefaultCredentials); + HttpClient.CustomWebProxy = null; + } + } + + [Fact] + public void LoadWebProxyConfig_WithEmbeddedCredentials_IgnoresProxyUseDefaultCredentials() + { + // Arrange - Save original values + var originalProxyServer = Config.ProxyServer; + var originalProxyUseDefaultCredentials = Config.ProxyUseDefaultCredentials; + + try + { + // Set up Config values - proxy with embedded credentials + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + var proxyUseDefaultCredentialsProperty = configType.GetProperty("ProxyUseDefaultCredentials", BindingFlags.Public | BindingFlags.Static); + + proxyServerProperty.SetValue(null, "http://testuser:testpass@corporate-proxy.test.com:8080"); + proxyUseDefaultCredentialsProperty.SetValue(null, true); // This should be ignored + + // Act + HttpClient.LoadWebProxyConfig(); + + // Assert - Embedded credentials take precedence over UseDefaultCredentials + var customProxy = HttpClient.CustomWebProxy as WebProxy; + Assert.NotNull(customProxy); + Assert.Equal("corporate-proxy.test.com", customProxy.Address.Host); + Assert.Equal(8080, customProxy.Address.Port); + Assert.False(customProxy.UseDefaultCredentials); // Should be false when explicit credentials are used + + // Verify explicit credentials were set + Assert.NotNull(customProxy.Credentials); + var networkCredential = customProxy.Credentials as NetworkCredential; + Assert.NotNull(networkCredential); + Assert.Equal("testuser", networkCredential.UserName); + Assert.Equal("testpass", networkCredential.Password); + } + finally + { + // Restore original values + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + var proxyUseDefaultCredentialsProperty = configType.GetProperty("ProxyUseDefaultCredentials", BindingFlags.Public | BindingFlags.Static); + + proxyServerProperty.SetValue(null, originalProxyServer); + proxyUseDefaultCredentialsProperty.SetValue(null, originalProxyUseDefaultCredentials); + HttpClient.CustomWebProxy = null; + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + [InlineData(null)] + public void LoadWebProxyConfig_ProxyUseDefaultCredentials_VariousValues(bool? configValue) + { + // Arrange - Save original values + var originalProxyServer = Config.ProxyServer; + var originalProxyUseDefaultCredentials = Config.ProxyUseDefaultCredentials; + + try + { + // Set up Config values + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + var proxyUseDefaultCredentialsProperty = configType.GetProperty("ProxyUseDefaultCredentials", BindingFlags.Public | BindingFlags.Static); + + proxyServerProperty.SetValue(null, "http://test-proxy.company.com:3128"); + proxyUseDefaultCredentialsProperty.SetValue(null, configValue); + + // Act + HttpClient.LoadWebProxyConfig(); + + // Assert + var customProxy = HttpClient.CustomWebProxy as WebProxy; + Assert.NotNull(customProxy); + Assert.Equal("test-proxy.company.com", customProxy.Address.Host); + Assert.Equal(3128, customProxy.Address.Port); + + // UseDefaultCredentials should only be true when configValue is true + bool expectedUseDefault = configValue.HasValue && configValue.Value; + Assert.Equal(expectedUseDefault, customProxy.UseDefaultCredentials); + if (configValue.HasValue && configValue.Value) + { + Assert.NotNull(customProxy.Credentials); + Assert.Empty((customProxy.Credentials as NetworkCredential).UserName); + } + else + { + Assert.Null(customProxy.Credentials); + } + } + finally + { + // Restore original values + var configType = typeof(Config); + var proxyServerProperty = configType.GetProperty("ProxyServer", BindingFlags.Public | BindingFlags.Static); + var proxyUseDefaultCredentialsProperty = configType.GetProperty("ProxyUseDefaultCredentials", BindingFlags.Public | BindingFlags.Static); + + proxyServerProperty.SetValue(null, originalProxyServer); + proxyUseDefaultCredentialsProperty.SetValue(null, originalProxyUseDefaultCredentials); + HttpClient.CustomWebProxy = null; + } + } + } +} From b5c8164a8f3de6d06f12694c0390bd042dca1e4e Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sun, 14 Sep 2025 00:33:25 +0100 Subject: [PATCH 33/40] DOTNET-202: Cleanup some csproj settings --- Src/StackifyLib/StackifyLib.csproj | 11 ++++++----- .../StackifyLib.UnitTests.csproj | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 06b04e0..986cd6b 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -2,8 +2,8 @@ Stackify API - 2.2.18 - beta + 2.2.17 + netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib @@ -14,15 +14,15 @@ false false false - 2.2.18-beta + 2.2.17 StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet git https://stackify.com/wp-content/uploads/2017/02/stk.png - 2.2.18.0 - 2.2.18.0 + 2.2.17.0 + 2.2.17.0 Remove default internal file logger false @@ -57,4 +57,5 @@ NETFULL + diff --git a/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj b/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj index 7d03e51..d9c07c3 100644 --- a/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj +++ b/test/StackifyLib.UnitTests/StackifyLib.UnitTests.csproj @@ -4,7 +4,6 @@ net6.0;net462 false true - true From 6626f318b7930d3e399343f543498ccc1e269c5b Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Sun, 14 Sep 2025 00:34:41 +0100 Subject: [PATCH 34/40] DOTNET-202: Update readme values --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce51bd5..e041892 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ For proxy using custom credentials: For proxy using default credentials: ```xml - + ``` From 3d9146b5d11f1c8ede675e374d73fbcd3cfc2a76 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Mon, 15 Sep 2025 20:01:12 +0100 Subject: [PATCH 35/40] DOTNET-202: Fix .NET Core proxy support --- Src/StackifyLib/Utils/HttpClient.cs | 2 - .../Utils/HttpClient_Loading_Tests.cs | 55 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 test/StackifyLib.UnitTests/Utils/HttpClient_Loading_Tests.cs diff --git a/Src/StackifyLib/Utils/HttpClient.cs b/Src/StackifyLib/Utils/HttpClient.cs index 1f376a9..01fa943 100644 --- a/Src/StackifyLib/Utils/HttpClient.cs +++ b/Src/StackifyLib/Utils/HttpClient.cs @@ -85,9 +85,7 @@ public bool IsClientError() static HttpClient() { -#if NETFULL LoadWebProxyConfig(); -#endif } public HttpClient(string apiKey, string apiUrl) diff --git a/test/StackifyLib.UnitTests/Utils/HttpClient_Loading_Tests.cs b/test/StackifyLib.UnitTests/Utils/HttpClient_Loading_Tests.cs new file mode 100644 index 0000000..4a3316d --- /dev/null +++ b/test/StackifyLib.UnitTests/Utils/HttpClient_Loading_Tests.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2024-2025 BMC Software, Inc. +// Copyright (c) 2021-2024 Netreo +// Copyright (c) 2019 Stackify +using Moq; +using StackifyLib.Models; +using StackifyLib.Utils; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Reflection; +using System.Security.Policy; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace StackifyLib.UnitTests.Utils +{ + public class HttpClient_Loading_Tests : IDisposable + { + private readonly HttpClient _httpClient; + + public HttpClient_Loading_Tests() + { + // Store original values to restore after tests + Config.ProxyServer = "http://testuser:testpass@docker-hyperv.local:8881"; + _httpClient = new HttpClient("test-api-key", "https://test.api.com/"); + HttpClient.IsUnitTest = true; + } + + public void Dispose() + { + HttpClient.IsUnitTest = false; + } + + [Fact(Skip = "This test should be run explicitly (due to static loading nature of HttpClient)")] + public void LoadWebProxyConfig_DefaultBehavior() + { + // Arrange - Mock the Config.Get method to return true for UseDefaultCredentials + try + { + var customProxy = HttpClient.CustomWebProxy as WebProxy; + Assert.NotNull(customProxy); + + // Note: We can't easily test UseDefaultCredentials without mocking Config.Get + // This test verifies the method runs without error when proxy is configured + Assert.Equal("docker-hyperv.local", customProxy.Address.Host); + Assert.Equal(8881, customProxy.Address.Port); + } + finally + { + } + } + } +} From b1e1da73eff55004bcff40985dd2ed3bcfcdea3d Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Mon, 15 Sep 2025 20:10:20 +0100 Subject: [PATCH 36/40] Updating StackifyLib version to 2.2.18-beta --- Src/StackifyLib/StackifyLib.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 986cd6b..a7a0c36 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -2,8 +2,8 @@ Stackify API - 2.2.17 - + 2.2.18 + beta netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib @@ -14,15 +14,15 @@ false false false - 2.2.17 + 2.2.18-beta StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet git https://stackify.com/wp-content/uploads/2017/02/stk.png - 2.2.17.0 - 2.2.17.0 + 2.2.18.0 + 2.2.18.0 Remove default internal file logger false From e6291e235ceb55e249644eebcdb85ba911e0e8e1 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Mon, 22 Sep 2025 01:28:40 +0100 Subject: [PATCH 37/40] DOTNET-202: Revert content-length on GZip post --- Src/StackifyLib/Utils/HttpClient.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Src/StackifyLib/Utils/HttpClient.cs b/Src/StackifyLib/Utils/HttpClient.cs index 01fa943..729c6ac 100644 --- a/Src/StackifyLib/Utils/HttpClient.cs +++ b/Src/StackifyLib/Utils/HttpClient.cs @@ -638,7 +638,6 @@ private HttpWebRequest BuildJsonRequest(string url, string jsonData, bool compre byte[] payload = Encoding.UTF8.GetBytes(jsonData); #if NETFULL - request.ContentLength = payload.Length; using (Stream postStream = request.GetRequestStream()) #else using (Stream postStream = request.GetRequestStreamAsync().GetAwaiter().GetResult()) From b900e4f71939361214f7d06e4543ce618a68f03a Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Mon, 22 Sep 2025 01:50:53 +0100 Subject: [PATCH 38/40] Updating StackifyLib version to 2.2.18-beta2 --- Src/StackifyLib/StackifyLib.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index a7a0c36..556924a 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -3,7 +3,7 @@ Stackify API 2.2.18 - beta + beta2 netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib @@ -14,7 +14,7 @@ false false false - 2.2.18-beta + 2.2.18-beta2 StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE From 164d31f36e5242180eff470870b7f0dcb6c3c6e5 Mon Sep 17 00:00:00 2001 From: Michael Mantos Date: Mon, 20 Oct 2025 14:13:11 +0100 Subject: [PATCH 39/40] Updating StackifyLib version to 2.2.18 --- Src/StackifyLib/StackifyLib.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 556924a..68a1a16 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -3,7 +3,7 @@ Stackify API 2.2.18 - beta2 + netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib @@ -14,7 +14,7 @@ false false false - 2.2.18-beta2 + 2.2.18 StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE From dc6436c06eeeb4e3f9fbf8eae2fd6135df5b2ec9 Mon Sep 17 00:00:00 2001 From: Todd Lair Date: Mon, 3 Nov 2025 16:52:38 -0600 Subject: [PATCH 40/40] Removing Veracode scanning from build pipeline --- azure-pipelines.yml | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1126baa..1299979 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,14 +16,6 @@ pool: # name: Azure Pipelines parameters: - - name: RUN_VERACODE_SCAN - displayName: 'Upload to Veracode' - type: boolean - default: true - - name: RUN_VERACODE_SCA - displayName: 'Execute Veracode SCA' - type: boolean - default: true - name: 'Build StackifyLib' type: boolean default: true @@ -254,23 +246,4 @@ stages: $version = "$version".Trim() echo "##vso[task.setvariable variable=version]$version" echo "StackifyLib."$version".nupkg" - workingDirectory: $(Build.SourcesDirectory) - - task: Veracode@3 - displayName: 'Upload and scan: $(Build.ArtifactStagingDirectory)/StackifyLib.$(version).nupkg' - condition: and(succeeded(), eq('${{ parameters.RUN_VERACODE_SCAN }}', 'true')) - enabled: True - inputs: - AnalysisService: 51003f89-58ab-463c-8e20-41484888d9c7 - veracodeAppProfile: Retrace .Net StackifyLib - version: AZ-Devops-Build-$(build.buildNumber) - filepath: $(Build.ArtifactStagingDirectory)/StackifyLib.$(version).nupkg - - - job: SCA - pool: - vmImage: 'windows-latest' - steps: - - task: PowerShell@2 - inputs: - targetType: 'inline' - script: $Env:SRCCLR_API_TOKEN="$(SRCCLR_API_TOKEN)"; Set-ExecutionPolicy AllSigned -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://download.srcclr.com/ci.ps1')); srcclr scan --allow-dirty .; - condition: and(succeeded(), eq('${{ parameters.RUN_VERACODE_SCA }}', 'true')) \ No newline at end of file + workingDirectory: $(Build.SourcesDirectory) \ No newline at end of file