Skip to content

Conversation

@simplicbe
Copy link
Member

@simplicbe simplicbe commented Dec 16, 2025

Summary by CodeRabbit

Release Notes

  • New Features
    • Added support for custom headers prefixed with "oxs-" that are automatically captured from HTTP requests and propagated through message publishing and sending operations.
    • Custom headers are now preserved throughout the request context for use across the system.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

Walkthrough

The PR adds support for propagating custom OxS-prefixed headers (oxs-*) through HTTP requests, message broker contexts, and MassTransit operations. A new OxSHeaders property is added to IRequestContext and its implementations, with header extraction and forwarding logic in multiple filter and middleware components.

Changes

Cohort / File(s) Summary
Core interfaces and constants
src/Simplic.OxS/IRequestContext.cs, src/Simplic.OxS/Constants.cs
Added new public property OxSHeaders: IDictionary<string, string> to IRequestContext interface and new public constant OxSHeaderPrefix = "oxs-" for standardizing OXS header names.
RequestContext implementations
src/Simplic.OxS.Server/Implementations/RequestContext.cs, src/Simplic.OxS.GrpcSample.Client/Program.cs
Added public property OxSHeaders initialized to empty dictionary in both RequestContext class and MockRequestContext test class.
Message broker header propagation
src/Simplic.OxS.MessageBroker/Filter/ConsumeContextFilter.cs, src/Simplic.OxS.MessageBroker/Filter/PublishUserHeaderFilter.cs, src/Simplic.OxS.MessageBroker/Filter/SendUserHeaderFilter.cs
Added logic to extract OxS-prefixed headers from ConsumeContext and propagate them through MassTransit publish and send contexts, augmenting existing header handling.
Server-side header handling
src/Simplic.OxS.Server/Filter/RequestContextActionFilter.cs, src/Simplic.OxS.Server/Filter/RequestContextHubFilter.cs, src/Simplic.OxS.Server/Middleware/HttpRequestInterceptor.cs
Added header extraction logic: RequestContextActionFilter filters and initializes OxSHeaders, RequestContextHubFilter introduces HTTP header reading with helper methods (GetFromHeader, GetStringFromHeader, SetOxSHeaders), and HttpRequestInterceptor populates OxSHeaders from request headers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

  • Header consistency: Verify case-insensitive OxS prefix matching is applied consistently across all filter and middleware files.
  • Null safety: Ensure null-checking on OxSHeaders is present in all propagation paths (PublishUserHeaderFilter, SendUserHeaderFilter).
  • Helper methods in RequestContextHubFilter: Review new support methods (GetFromHeader, GetStringFromHeader, SetOxSHeaders) for correct GUID parsing and header value handling.
  • Header value preservation: Confirm no unintended transformations of header values during copying across layers.

Poem

🐰 Hop, hop, headers flow,
Through filters and middleware's glow,
OxS prefixes dance with care,
Custom data everywhere!
Now context carries what we need,
A chain of headers, yes indeed!

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and overly broad. 'Add request context' does not clearly describe the specific change—adding OxS header handling and propagation across multiple components. Use a more specific title such as 'Add OxSHeaders support to request context' or 'Implement custom header propagation across request context' to clearly convey the primary change.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch f_custom_request_context_header

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
src/Simplic.OxS.Server/Middleware/HttpRequestInterceptor.cs (1)

19-26: Consider using .FirstOrDefault() for StringValues conversion.

Line 24 assigns header.Value (a StringValues) directly to the string dictionary. While this works via implicit conversion, it concatenates multiple values with commas. For consistency with other header handling in the codebase and to avoid unexpected behavior, use .FirstOrDefault() or .ToString().

Apply this diff:

-                    requestContext.OxSHeaders[header.Key] = header.Value;
+                    requestContext.OxSHeaders[header.Key] = header.Value.FirstOrDefault() ?? string.Empty;
src/Simplic.OxS.Server/Filter/RequestContextHubFilter.cs (4)

28-30: Remove unused variables.

The variables userIdFromHeader and organizationIdFromHeader are declared but never used. Lines 40-44 populate UserId and OrganizationId from JWT claims instead, making these header reads unnecessary.

Apply this diff:

-                // Get other headers if needed
-                var userIdFromHeader = GetFromHeader(httpContext, Constants.HttpHeaderUserIdKey);
-                var organizationIdFromHeader = GetFromHeader(httpContext, Constants.HttpHeaderOrganizationIdKey);
-                

55-66: Consider extracting duplicated helper to a shared utility class.

The GetFromHeader method is duplicated in RequestContextActionFilter.cs (lines 80-91). Consider extracting this and other common header parsing utilities to a shared class to reduce duplication and improve maintainability.


74-82: Remove unused method.

The GetStringFromHeader method is defined but never called in the codebase. Remove it unless it's needed for future planned work.

Apply this diff:

-        /// <summary>
-        /// Get a string value from the http header by its key
-        /// </summary>
-        /// <param name="httpContext"></param>
-        /// <param name="key"></param>
-        /// <returns></returns>
-        private string? GetStringFromHeader(HttpContext httpContext, string key)
-        {
-            if (httpContext.Request.Headers.TryGetValue(key, out StringValues headerValues))
-            {
-                return headerValues.FirstOrDefault();
-            }
-
-            return null;
-        }
-

89-100: Consider extracting OxS header extraction to a shared utility.

The OxS header extraction logic is duplicated in HttpRequestInterceptor.cs (lines 19-26). Consider extracting this to a shared utility method to reduce duplication and ensure consistent behavior across components.

Note: This implementation correctly uses .FirstOrDefault() for StringValues conversion (line 97), which is more explicit than the direct assignment in HttpRequestInterceptor.cs.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cdc487a and a495a5e.

📒 Files selected for processing (10)
  • src/Simplic.OxS.GrpcSample.Client/Program.cs (1 hunks)
  • src/Simplic.OxS.MessageBroker/Filter/ConsumeContextFilter.cs (1 hunks)
  • src/Simplic.OxS.MessageBroker/Filter/PublishUserHeaderFilter.cs (1 hunks)
  • src/Simplic.OxS.MessageBroker/Filter/SendUserHeaderFilter.cs (1 hunks)
  • src/Simplic.OxS.Server/Filter/RequestContextActionFilter.cs (2 hunks)
  • src/Simplic.OxS.Server/Filter/RequestContextHubFilter.cs (3 hunks)
  • src/Simplic.OxS.Server/Implementations/RequestContext.cs (1 hunks)
  • src/Simplic.OxS.Server/Middleware/HttpRequestInterceptor.cs (1 hunks)
  • src/Simplic.OxS/Constants.cs (1 hunks)
  • src/Simplic.OxS/IRequestContext.cs (1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-11-26T09:38:34.885Z
Learnt from: CR
Repo: simplic/simplic-oxs-accounting PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-26T09:38:34.885Z
Learning: Applies to **/*.cs : Never include OrganizationId in response objects; client already knows it

Applied to files:

  • src/Simplic.OxS.GrpcSample.Client/Program.cs
📚 Learning: 2025-08-26T05:52:00.195Z
Learnt from: CR
Repo: simplic/simplic-oxs-storage-management PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-26T05:52:00.195Z
Learning: Applies to {Program.cs,Startup.cs,**/Controllers/**/*.cs,**/Authorization/**/*.cs} : Use JWT Bearer authentication; support OAuth 2.0/OpenID Connect; role-based and policy-based authorization; integrate with Microsoft Entra ID; apply consistent security to controllers and minimal APIs

Applied to files:

  • src/Simplic.OxS.Server/Middleware/HttpRequestInterceptor.cs
  • src/Simplic.OxS.Server/Filter/RequestContextActionFilter.cs
📚 Learning: 2025-08-26T05:52:00.195Z
Learnt from: CR
Repo: simplic/simplic-oxs-storage-management PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-26T05:52:00.195Z
Learning: Applies to {Program.cs,**/Middleware/**/*.cs,**/Models/**/*.cs,**/Validators/**/*.cs,**/Filters/**/*.cs} : Use Data Annotations and FluentValidation; customize validation pipeline responses; implement global exception handling middleware; produce consistent error responses using RFC 7807 Problem Details

Applied to files:

  • src/Simplic.OxS.Server/Filter/RequestContextActionFilter.cs
📚 Learning: 2025-11-26T09:38:34.885Z
Learnt from: CR
Repo: simplic/simplic-oxs-accounting PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-26T09:38:34.885Z
Learning: Use built-in ASP.NET Core features when possible before introducing third-party dependencies

Applied to files:

  • src/Simplic.OxS.Server/Filter/RequestContextActionFilter.cs
  • src/Simplic.OxS.Server/Filter/RequestContextHubFilter.cs
📚 Learning: 2025-08-26T05:52:00.195Z
Learnt from: CR
Repo: simplic/simplic-oxs-storage-management PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-26T05:52:00.195Z
Learning: Applies to {Program.cs,Startup.cs,**/Controllers/**/*.cs} : Enforce HTTP method semantics: GET (no body, return body, safe/idempotent/cacheable), POST (create, return resource+location), PUT (replace, idempotent), PATCH (partial update, idempotent), DELETE (may have body, idempotent)

Applied to files:

  • src/Simplic.OxS.Server/Filter/RequestContextActionFilter.cs
🧬 Code graph analysis (3)
src/Simplic.OxS.Server/Filter/RequestContextActionFilter.cs (2)
src/Simplic.OxS/Constants.cs (1)
  • Constants (6-39)
src/Simplic.OxS.Server/Constants.cs (1)
  • Constants (6-37)
src/Simplic.OxS.Server/Filter/RequestContextHubFilter.cs (3)
src/Simplic.OxS.Server/Middleware/HttpRequestInterceptor.cs (3)
  • ValueTask (10-50)
  • Guid (56-67)
  • Guid (73-84)
src/Simplic.OxS/Constants.cs (1)
  • Constants (6-39)
src/Simplic.OxS.Server/Filter/RequestContextActionFilter.cs (3)
  • Guid (81-92)
  • Guid (98-109)
  • Guid (115-126)
src/Simplic.OxS.MessageBroker/Filter/ConsumeContextFilter.cs (1)
src/Simplic.OxS/Constants.cs (1)
  • Constants (6-39)
🔇 Additional comments (7)
src/Simplic.OxS/IRequestContext.cs (1)

22-27: LGTM! Interface extension for custom header propagation.

The addition of OxSHeaders to the interface is well-documented and appropriate for propagating custom OxS-prefixed headers through the request pipeline. The property type IDictionary<string, string> aligns with typical header storage patterns.

src/Simplic.OxS.MessageBroker/Filter/ConsumeContextFilter.cs (1)

40-47: LGTM! Correct header extraction from ConsumeContext.

The implementation correctly filters and populates OxS-prefixed headers from the MassTransit ConsumeContext. The case-insensitive comparison and null-safe value handling are appropriate.

src/Simplic.OxS.MessageBroker/Filter/PublishUserHeaderFilter.cs (1)

28-31: LGTM! Header propagation to publish context.

The implementation correctly forwards OxSHeaders from the request context into the MassTransit publish headers with proper null checking.

src/Simplic.OxS/Constants.cs (1)

32-38: LGTM! Well-defined header prefix constant.

The OxSHeaderPrefix constant centralizes the "oxs-" prefix and is well-documented. This promotes consistency across the codebase when handling OxS-specific headers.

src/Simplic.OxS.MessageBroker/Filter/SendUserHeaderFilter.cs (1)

31-34: LGTM! Header propagation to send context.

The implementation correctly forwards OxSHeaders from the request context into the MassTransit send headers with proper null checking, consistent with the publish filter.

src/Simplic.OxS.GrpcSample.Client/Program.cs (1)

151-151: LGTM! Mock implementation updated correctly.

The MockRequestContext has been properly updated to implement the new OxSHeaders interface member with an appropriate default value.

src/Simplic.OxS.Server/Implementations/RequestContext.cs (1)

23-28: LGTM! Clean implementation with sensible defaults.

The OxSHeaders property is properly implemented with a default empty dictionary, which prevents null reference issues. The documentation clearly explains the purpose with a helpful example.

Comment on lines +53 to +65

// Set request header if none are set yet
if (requestContext.OxSHeaders == null || requestContext.OxSHeaders.Any() == false)
{
requestContext.OxSHeaders = new Dictionary<string, string>();
foreach (var header in requestContext.OxSHeaders)
{
if (header.Key.StartsWith(Simplic.OxS.Constants.OxSHeaderPrefix, StringComparison.OrdinalIgnoreCase))
{
requestContext.OxSHeaders[header.Key] = header.Value;
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix critical logic error - iterating over empty dictionary instead of HTTP headers.

Lines 58-64 iterate over the newly created empty requestContext.OxSHeaders dictionary instead of the actual HTTP request headers from executionContext.HttpContext.Request.Headers. This means OxS headers will never be populated from incoming HTTP requests.

Apply this diff to fix the logic:

 // Set request header if none are set yet
 if (requestContext.OxSHeaders == null || requestContext.OxSHeaders.Any() == false)
 {
     requestContext.OxSHeaders = new Dictionary<string, string>();
-    foreach (var header in requestContext.OxSHeaders)
+    foreach (var header in executionContext.HttpContext.Request.Headers)
     {
         if (header.Key.StartsWith(Simplic.OxS.Constants.OxSHeaderPrefix, StringComparison.OrdinalIgnoreCase))
         {
             requestContext.OxSHeaders[header.Key] = header.Value;
         }
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Set request header if none are set yet
if (requestContext.OxSHeaders == null || requestContext.OxSHeaders.Any() == false)
{
requestContext.OxSHeaders = new Dictionary<string, string>();
foreach (var header in requestContext.OxSHeaders)
{
if (header.Key.StartsWith(Simplic.OxS.Constants.OxSHeaderPrefix, StringComparison.OrdinalIgnoreCase))
{
requestContext.OxSHeaders[header.Key] = header.Value;
}
}
}
// Set request header if none are set yet
if (requestContext.OxSHeaders == null || requestContext.OxSHeaders.Any() == false)
{
requestContext.OxSHeaders = new Dictionary<string, string>();
foreach (var header in executionContext.HttpContext.Request.Headers)
{
if (header.Key.StartsWith(Simplic.OxS.Constants.OxSHeaderPrefix, StringComparison.OrdinalIgnoreCase))
{
requestContext.OxSHeaders[header.Key] = header.Value;
}
}
}
🤖 Prompt for AI Agents
In src/Simplic.OxS.Server/Filter/RequestContextActionFilter.cs around lines 53
to 65, the current code creates a new empty requestContext.OxSHeaders and then
(incorrectly) iterates that empty dictionary instead of the incoming HTTP
request headers, so OxS headers are never populated; change the loop to
enumerate executionContext.HttpContext.Request.Headers, and for each header
whose key starts with Simplic.OxS.Constants.OxSHeaderPrefix (use
StringComparison.OrdinalIgnoreCase) add or set
requestContext.OxSHeaders[header.Key] = header.Value (convert header.Value to a
string or join multiple values) after ensuring requestContext.OxSHeaders is
instantiated when null or empty.

Comment on lines +31 to +35
if (context.Request.Headers.TryGetValue(Constants.HttpHeaderCorrelationIdKey, out StringValues correlationIds))
{
correlationId = correlationIds.FirstOrDefault(k => k == Constants.HttpHeaderCorrelationIdKey) ??
Guid.NewGuid().ToString();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix incorrect correlation ID extraction logic.

Line 33 has a logic error. The lambda parameter k represents a header value from the StringValues collection, not a key. Comparing k == Constants.HttpHeaderCorrelationIdKey will never match and always fall back to generating a new GUID, defeating the purpose of reading the header.

Apply this diff:

-            correlationId = correlationIds.FirstOrDefault(k => k == Constants.HttpHeaderCorrelationIdKey) ??
-                                                Guid.NewGuid().ToString();
+            correlationId = correlationIds.FirstOrDefault() ?? Guid.NewGuid().ToString();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (context.Request.Headers.TryGetValue(Constants.HttpHeaderCorrelationIdKey, out StringValues correlationIds))
{
correlationId = correlationIds.FirstOrDefault(k => k == Constants.HttpHeaderCorrelationIdKey) ??
Guid.NewGuid().ToString();
}
if (context.Request.Headers.TryGetValue(Constants.HttpHeaderCorrelationIdKey, out StringValues correlationIds))
{
correlationId = correlationIds.FirstOrDefault() ?? Guid.NewGuid().ToString();
}
🤖 Prompt for AI Agents
In src/Simplic.OxS.Server/Middleware/HttpRequestInterceptor.cs around lines 31
to 35, the code incorrectly compares header values to the header name; replace
the predicate so you extract the first header value (e.g.,
correlationIds.FirstOrDefault()) and fall back to Guid.NewGuid().ToString() if
empty or null; ensure you trim/check for empty string before using it so an
actual header value is used rather than always generating a new GUID.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants