Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .csharpierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
**/nuget.config
**/_snapshots/
**/_snapshot/
**/[Nn]u[Gg]et.config
**/*.verified.*
**/*.received.*
31 changes: 15 additions & 16 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,22 @@ generated_code = true
# XML project files
[*.{slnx,csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,nativeproj,locproj}]
indent_size = 2
max_line_length = 200

# Xml build files
[*.builds]
indent_size = 2
max_line_length = 200

# Xml files
[*.{xml,stylecop,resx,ruleset}]
indent_size = 2
max_line_length = 200

# XML config files
[*.{props,targets,ruleset,config,nuspec,vsixmanifest,vsct}]
indent_size = 2
max_line_length = 200

# JSON files
[*.json]
Expand Down Expand Up @@ -86,10 +90,6 @@ insert_final_newline = false
[*.sln]
indent_style = tab

[*.{received,verified}.txt]
insert_final_newline = false
trim_trailing_whitespace = false

[*.{cs,csx,vb,vbx}]
# .NET Code Style Settings
# See https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
Expand Down Expand Up @@ -266,19 +266,18 @@ dotnet_diagnostic.IDE0290.severity = sugges
# [CSharpier] Incompatible rules deactivated
# https://csharpier.com/docs/IntegratingWithLinters#code-analysis-rules
dotnet_diagnostic.IDE0055.severity = none
dotnet_diagnostic.SA1000.severity = none
dotnet_diagnostic.SA1009.severity = none
dotnet_diagnostic.SA1111.severity = none
dotnet_diagnostic.SA1118.severity = none
dotnet_diagnostic.SA1137.severity = none
dotnet_diagnostic.SA1413.severity = none
dotnet_diagnostic.SA1500.severity = none
dotnet_diagnostic.SA1501.severity = none
dotnet_diagnostic.SA1502.severity = none
dotnet_diagnostic.SA1504.severity = none
dotnet_diagnostic.SA1515.severity = none
dotnet_diagnostic.SA1516.severity = none

# Support for NetEvolve.Arguments Methods
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1062#null-check-validation-methods
dotnet_code_quality.CA1062.null_check_validation_methods = M:NetEvolve.Arguments.Argument.ThrowIfNull(System.Object,System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNull(System.Void*,System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNullOrEmpty(System.String,System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNullOrEmpty``1(System.Collections.Generic.IEnumerable{``0},System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNullOrWhiteSpace(System.String,System.String)

# Disable all style rules for generated code
[*.{received,verified}.*]
generated_code = true
# Disable all style rules for migrations
dotnet_analyzer_diagnostic.severity = none

[**/Migrations/*.{cs,csx,vb,vbx}]
generated_code = true
# Disable all style rules for migrations
dotnet_analyzer_diagnostic.severity = none
1 change: 0 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@
<RepositoryUrl>https://github.com/dailydevops/http.correlation.git</RepositoryUrl>
<PackageReleaseNotes>$(PackageProjectUrl)/releases</PackageReleaseNotes>
<PackageTags>http;tracking;correlation;request id;</PackageTags>
<RootNamespace>NetEvolve.Http.Correlation</RootNamespace>
</PropertyGroup>
</Project>
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<GlobalPackageReference Include="SonarAnalyzer.CSharp" Version="10.21.0.135717" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Core" Version="2.51.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.5.2" />
Expand Down
5 changes: 3 additions & 2 deletions Http.Correlation.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@
<File Path="Directory.Solution.props" />
<File Path="GitVersion.yml" />
<File Path="LICENSE" />
<File Path="new-project.ps1" />
<File Path="nuget.config" />
<File Path="README.md" />
<File Path="update-solution.ps1" />
</Folder>
<Folder Name="/src/">
<Project Path="src/NetEvolve.Http.Correlation.Abstractions/NetEvolve.Http.Correlation.Abstractions.csproj" />
<Project Path="src/NetEvolve.Http.Correlation.AspNetCore/NetEvolve.Http.Correlation.AspNetCore.csproj" />
<Project Path="src/NetEvolve.Http.Correlation.Azure.Functions/NetEvolve.Http.Correlation.Azure.Functions.csproj" Id="639963ff-323b-4f12-804e-c1b97a2f1cf2" />
<Project Path="src/NetEvolve.Http.Correlation.HttpClient/NetEvolve.Http.Correlation.HttpClient.csproj" />
<Project Path="src/NetEvolve.Http.Correlation.TestGenerator/NetEvolve.Http.Correlation.TestGenerator.csproj" />
<Project Path="src/NetEvolve.Http.Correlation.Ulid/NetEvolve.Http.Correlation.Ulid.csproj" />
Expand All @@ -26,6 +25,8 @@
<Project Path="tests/NetEvolve.Http.Correlation.Abstractions.Tests.Unit/NetEvolve.Http.Correlation.Abstractions.Tests.Unit.csproj" />
<Project Path="tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Integration/NetEvolve.Http.Correlation.AspNetCore.Tests.Integration.csproj" />
<Project Path="tests/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit/NetEvolve.Http.Correlation.AspNetCore.Tests.Unit.csproj" />
<Project Path="tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration/NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration.csproj" />
<Project Path="tests/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit/NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit.csproj" />
<Project Path="tests/NetEvolve.Http.Correlation.HttpClient.Tests.Unit/NetEvolve.Http.Correlation.HttpClient.Tests.Unit.csproj" />
<Project Path="tests/NetEvolve.Http.Correlation.TestGenerator.Tests.Unit/NetEvolve.Http.Correlation.TestGenerator.Tests.Unit.csproj" />
<Project Path="tests/NetEvolve.Http.Correlation.Ulid.Tests.Unit/NetEvolve.Http.Correlation.Ulid.Tests.Unit.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface IHttpCorrelationAccessor
/// <summary>
/// Gets the current Correlation Id from the <see cref="HttpContext"/>.
/// </summary>
string CorrelationId { get; }
string CorrelationId { get; internal set; }

/// <summary>
/// Gets the header name, which is used.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(_TargetFrameworks)</TargetFrameworks>
<RootNamespace>NetEvolve.Http.Correlation</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<Description>Abstractions for the uniform use of Http.Correlation. Based on the primary Http header `X-Correlation-ID` as well as the alternative Http header `X-Request-ID`.</Description>
Expand All @@ -9,6 +10,10 @@
<ItemGroup>
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.AspNetCore" />
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.AspNetCore.Tests.Integration" />
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.AspNetCore.Tests.Unit" />
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.Azure.Functions" />
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration" />
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit" />
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.HttpClient" />
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.HttpClient.Tests.Unit" />
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.TestGenerator" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace NetEvolve.Http.Correlation;
namespace NetEvolve.Http.Correlation.AspNetCore;

using System;
using Microsoft.AspNetCore.Builder;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace NetEvolve.Http.Correlation;
namespace NetEvolve.Http.Correlation.AspNetCore;

using Microsoft.AspNetCore.Http;
using NetEvolve.Http.Correlation.Abstractions;
Expand All @@ -7,12 +7,17 @@
internal sealed class HttpCorrelationAccessor : IHttpCorrelationAccessor
{
private readonly IHttpContextAccessor _httpContextAccessor;
private string? _correlationId;

public HttpCorrelationAccessor(IHttpContextAccessor httpContextAccessor) =>
_httpContextAccessor = httpContextAccessor;

/// <inheritdoc />
public string CorrelationId => _httpContextAccessor.HttpContext!.TraceIdentifier;
public string CorrelationId
{
get => _correlationId ??= _httpContextAccessor.HttpContext!.TraceIdentifier;
set => _correlationId = value;
}

/// <inheritdoc />
public string HeaderName { get; set; } = CorrelationConstants.HeaderName1;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace NetEvolve.Http.Correlation;
namespace NetEvolve.Http.Correlation.AspNetCore;

using System;
using System.Collections.Generic;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
</PropertyGroup>
<PropertyGroup>
<Description>Implementation of AspNetCore middleware to use Http.Correlation. Based on the primary Http header `X-Correlation-ID` as well as the alternative Http header `X-Request-ID`.</Description>
<PackageTags>$(PackageTags);aspnetcore</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\NetEvolve.Http.Correlation.Abstractions\NetEvolve.Http.Correlation.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.AspNetCore.Tests.Unit" />
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.AspNetCore.Tests.Integration" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace NetEvolve.Http.Correlation;
namespace NetEvolve.Http.Correlation.AspNetCore;

using System;
using Microsoft.Extensions.DependencyInjection;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace NetEvolve.Http.Correlation.Azure.Functions;

using Microsoft.Azure.Functions.Worker;
using NetEvolve.Http.Correlation.Abstractions;

internal sealed class FunctionsCorrelationAccessor : IHttpCorrelationAccessor
{
private string? _correlationId;

public FunctionContext Context { get; set; } = default!;

public string CorrelationId
{
get => _correlationId ??= Context.InvocationId;
set => _correlationId = value;
}

public string HeaderName { get; set; } = string.Empty;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
namespace NetEvolve.Http.Correlation.Azure.Functions;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.Functions.Worker.Middleware;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NetEvolve.Http.Correlation.Abstractions;
using static NetEvolve.Http.Correlation.CorrelationConstants;

internal sealed class FunctionsCorrelationMiddleware : IFunctionsWorkerMiddleware
{
private readonly ILogger<FunctionsCorrelationMiddleware> _logger;

public FunctionsCorrelationMiddleware(ILogger<FunctionsCorrelationMiddleware> logger) => _logger = logger;

public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
ArgumentNullException.ThrowIfNull(context);

var accessor = context.InstanceServices.GetRequiredService<FunctionsCorrelationAccessor>();
accessor.Context = context;

var httpRequestData = await context.GetHttpRequestDataAsync().ConfigureAwait(false);

if (httpRequestData is null)
{
await next(context).ConfigureAwait(false);
return;
}

if (!GetIdFromHeader(httpRequestData, out var correlationId, out var usedHeaderName))
{
correlationId = GeneratedId(context);
}

accessor.CorrelationId = correlationId;
accessor.HeaderName = usedHeaderName;

var scopeProperties = new Dictionary<string, object>(StringComparer.Ordinal)
{
{ usedHeaderName, correlationId },
};

using (_logger.BeginScope(scopeProperties))
{
await next(context).ConfigureAwait(false);

// Add correlation ID to response headers after function execution
var httpResponseData = context.GetInvocationResult().Value as HttpResponseData;
if (httpResponseData is not null && !httpResponseData.Headers.Contains(usedHeaderName))
{
httpResponseData.Headers.Add(usedHeaderName, correlationId);
}
}
}

private static string GeneratedId(FunctionContext context) =>
context.InstanceServices.GetService<IHttpCorrelationIdProvider>()?.GenerateId() ?? context.InvocationId;

private static bool GetIdFromHeader(
HttpRequestData httpRequestData,
out string correlationId,
out string usedHeaderName
)
{
usedHeaderName = HeaderName1;
correlationId = string.Empty;

if (httpRequestData.Headers.TryGetValues(HeaderName1, out var headerValues1))
{
var firstValue = headerValues1.FirstOrDefault();
if (!string.IsNullOrWhiteSpace(firstValue))
{
correlationId = firstValue;
return true;
}
}

if (httpRequestData.Headers.TryGetValues(HeaderName2, out var headerValues2))
{
var firstValue = headerValues2.FirstOrDefault();
if (!string.IsNullOrWhiteSpace(firstValue))
{
correlationId = firstValue;
usedHeaderName = HeaderName2;
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace NetEvolve.Http.Correlation.Azure.Functions;

using System;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NetEvolve.Http.Correlation.Abstractions;

/// <summary>
/// <see cref="IFunctionsWorkerApplicationBuilder"/> Extensions for <see cref="FunctionsCorrelationMiddleware"/>.
/// </summary>
public static class FunctionsWorkerApplicationBuilderExtensions
{
/// <summary>
/// Adds the <see cref="FunctionsCorrelationMiddleware"/> to the Azure Functions worker middleware pipeline.
/// </summary>
/// <param name="builder">The <see cref="IFunctionsWorkerApplicationBuilder"/> instance.</param>
/// <returns>The <see cref="IFunctionsWorkerApplicationBuilder"/> instance.</returns>
public static IFunctionsWorkerApplicationBuilder UseHttpCorrelation(this IFunctionsWorkerApplicationBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder);

using (var scopedServices = builder.Services.BuildServiceProvider().CreateScope())
{
if (scopedServices.ServiceProvider.GetService<IHttpCorrelationAccessor>() is null)
{
throw new InvalidOperationException(
$"The required services for this function were not found. Please run `services.{nameof(ServiceCollectionExtensions.AddHttpCorrelation)}()` in advance."
);
}
}

return builder.UseMiddleware<FunctionsCorrelationMiddleware>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(_TargetFrameworks)</TargetFrameworks>
</PropertyGroup>
<PropertyGroup>
<Description>Implementation of Azure.Functions middleware to use Http.Correlation. Based on the primary Http header `X-Correlation-ID` as well as the alternative Http header `X-Request-ID`.</Description>
<PackageTags>$(PackageTags);azure;functions</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\NetEvolve.Http.Correlation.Abstractions\NetEvolve.Http.Correlation.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker.Core" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.Azure.Functions.Tests.Unit" />
<InternalsVisibleTo Include="NetEvolve.Http.Correlation.Azure.Functions.Tests.Integration" />
</ItemGroup>
</Project>
Loading
Loading