Skip to content

Conversation

@itssimon
Copy link
Member

@itssimon itssimon commented Jun 26, 2025

Summary by CodeRabbit

  • New Features

    • Added support for masking specific fields in JSON request and response bodies when logging.
    • Enhanced configurability for specifying which body fields to mask.
  • Refactor

    • Centralized and modularized masking logic for requests and responses.
    • Deferred masking and serialization until log entries are written to file for improved separation of concerns.
  • Tests

    • Expanded test coverage to validate exclusion and masking features, including header, query parameter, and body field masking.
    • Improved test structure with helper methods for reading and verifying logged entries.

@itssimon itssimon self-assigned this Jun 26, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jun 26, 2025

Walkthrough

The changes introduce a new data transfer object for request logging, centralize and enhance masking logic for request and response data, and defer serialization until log writing. Configuration now supports specifying JSON body field masking patterns. Test coverage is expanded to verify exclusion and masking behavior, with new helper methods and more granular assertions.

Changes

File(s) Change Summary
src/main/java/io/apitally/common/RequestLogger.java Refactored to store pending log entries as objects, defer masking/serialization to file write, and centralize masking logic (headers, query params, JSON body fields) in new private methods.
src/main/java/io/apitally/common/RequestLoggingConfig.java Added support for specifying JSON body field masking patterns via a new field and getter/setter methods.
src/main/java/io/apitally/common/dto/RequestLogItem.java Introduced new class to encapsulate request, response, and exception data for logging, with UUID and JSON serialization annotations.
src/test/java/io/apitally/common/RequestLoggerTest.java Expanded and refactored tests to cover exclusion and masking features, added helper for reading logged items, and improved assertions for logged content and masking correctness.

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@codecov
Copy link

codecov bot commented Jun 26, 2025

Codecov Report

Attention: Patch coverage is 84.04255% with 15 lines in your changes missing coverage. Please review.

Project coverage is 78.08%. Comparing base (020b3b6) to head (e134436).
Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...rc/main/java/io/apitally/common/RequestLogger.java 81.25% 5 Missing and 10 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main      #22      +/-   ##
============================================
+ Coverage     75.44%   78.08%   +2.63%     
- Complexity      281      313      +32     
============================================
  Files            30       31       +1     
  Lines          1006     1054      +48     
  Branches        128      133       +5     
============================================
+ Hits            759      823      +64     
+ Misses          167      157      -10     
+ Partials         80       74       -6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@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: 1

🔭 Outside diff range comments (1)
src/main/java/io/apitally/common/RequestLogger.java (1)

464-479: Fix compilation error in skipEmptyValues method.

Same issue with properties() method here.

Apply this fix:

 private ObjectNode skipEmptyValues(ObjectNode node) {
     ObjectNode result = objectMapper.createObjectNode();
-    node.properties().forEach(entry -> {
+    node.fields().forEachRemaining(entry -> {
         if (entry.getValue().isNull()) {
             return;
         }
         if (entry.getValue().isArray() && entry.getValue().size() == 0) {
             return;
         }
         if (entry.getValue().isTextual() && entry.getValue().asText().isEmpty()) {
             return;
         }
         result.set(entry.getKey(), entry.getValue());
     });
     return result;
 }
🧹 Nitpick comments (2)
src/test/java/io/apitally/common/RequestLoggerTest.java (1)

361-428: Excellent test coverage for JSON body field masking.

The test thoroughly validates the masking behavior with nested objects and arrays. Good attention to detail in verifying both masked and non-masked fields.

Consider extracting the large JSON strings to constants or a test fixture file for better readability:

+    private static final String TEST_REQUEST_BODY_JSON = "{\"username\":\"john_doe\",\"password\":\"secret123\"...}";
+    private static final String TEST_RESPONSE_BODY_JSON = "{\"status\":\"success\",\"secret\":\"response_secret\"...}";
+
     @Test
     void testMaskBodyFields() {
         // ... setup code ...
-        String requestBodyJson = "{\"username\":\"john_doe\",\"password\":\"secret123\",\"token\":\"abc123\",\"custom\":\"xyz789\",\"user_id\":42,\"api_key\":123,\"normal_field\":\"value\",\"nested\":{\"password\":\"nested_secret\",\"count\":5,\"deeper\":{\"auth\":\"deep_token\"}},\"array\":[{\"password\":\"array_secret\",\"id\":1},{\"normal\":\"text\",\"token\":\"array_token\"}]}";
-        String responseBodyJson = "{\"status\":\"success\",\"secret\":\"response_secret\",\"data\":{\"pwd\":\"response_pwd\"}}";
+        String requestBodyJson = TEST_REQUEST_BODY_JSON;
+        String responseBodyJson = TEST_RESPONSE_BODY_JSON;
src/main/java/io/apitally/common/RequestLogger.java (1)

70-78: Good addition of default body field masking patterns.

The default patterns cover common sensitive field names including passwords, tokens, authentication fields, and financial data.

Consider adding a few more common sensitive field patterns:

 private static final List<String> MASK_BODY_FIELD_PATTERNS = Arrays.asList(
         "password",
         "pwd",
         "token",
         "secret",
         "auth",
         "card[-_ ]?number",
         "ccv",
-        "ssn");
+        "ssn",
+        "api[-_ ]?key",
+        "private[-_ ]?key",
+        "access[-_ ]?token",
+        "refresh[-_ ]?token");

Also applies to: 111-112, 387-390

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6e1c9c and 0948ea8.

📒 Files selected for processing (4)
  • src/main/java/io/apitally/common/RequestLogger.java (11 hunks)
  • src/main/java/io/apitally/common/RequestLoggingConfig.java (2 hunks)
  • src/main/java/io/apitally/common/dto/RequestLogItem.java (1 hunks)
  • src/test/java/io/apitally/common/RequestLoggerTest.java (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/io/apitally/common/RequestLogger.java (4)
src/main/java/io/apitally/common/dto/ExceptionDto.java (1)
  • ExceptionDto (3-25)
src/main/java/io/apitally/common/dto/Header.java (1)
  • Header (3-19)
src/main/java/io/apitally/common/dto/Request.java (1)
  • Request (3-43)
src/main/java/io/apitally/common/dto/RequestLogItem.java (1)
  • RequestLogItem (7-39)
🪛 GitHub Actions: Tests
src/main/java/io/apitally/common/RequestLogger.java

[error] 433-433: Compilation error: cannot find symbol method 'properties()' in variable 'objectNode' of type com.fasterxml.jackson.databind.node.ObjectNode


[error] 466-466: Compilation error: cannot find symbol method 'properties()' in variable 'node' of type com.fasterxml.jackson.databind.node.ObjectNode

🔇 Additional comments (5)
src/main/java/io/apitally/common/RequestLoggingConfig.java (1)

16-16: LGTM! Clean implementation of body field masking configuration.

The new bodyFieldMaskPatterns field and its accessor methods follow the established pattern used for other masking configurations in this class.

Also applies to: 92-98

src/main/java/io/apitally/common/dto/RequestLogItem.java (1)

1-39: LGTM! Well-structured DTO for request logging.

The implementation follows best practices:

  • Immutable fields with proper encapsulation
  • UUID generation for unique identification
  • Correct use of Jackson annotations for JSON serialization
src/test/java/io/apitally/common/RequestLoggerTest.java (2)

45-107: Good refactoring of the end-to-end test.

The test now properly validates the structure of logged data and uses the helper method for file reading. The assertions are comprehensive and check all relevant fields.


430-452: Clean helper method implementation.

The helper method properly handles file operations and JSON parsing.

src/main/java/io/apitally/common/RequestLogger.java (1)

178-244: Well-structured masking implementation.

The applyMasking method effectively centralizes all masking logic with proper handling of:

  • User-provided masking callbacks
  • Size limits with clear messages
  • JSON body field masking
  • Header and query parameter masking

The deferred masking approach improves performance by avoiding unnecessary processing for requests that may be dropped from the queue.

Copy link

@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: 0

♻️ Duplicate comments (1)
src/main/java/io/apitally/common/RequestLogger.java (1)

433-433: Compilation error fix confirmed.

The use of fields().forEachRemaining() correctly addresses the previous compilation error. The ObjectNode API provides fields() method that returns an Iterator of Map.Entry objects.

🧹 Nitpick comments (3)
src/main/java/io/apitally/common/RequestLogger.java (3)

70-78: Consider making body field masking patterns configurable.

The default patterns are comprehensive and security-focused. However, consider adding these as defaults to the configuration class to allow runtime customization while maintaining backward compatibility.

-    private static final List<String> MASK_BODY_FIELD_PATTERNS = Arrays.asList(
-            "password",
-            "pwd",
-            "token",
-            "secret",
-            "auth",
-            "card[-_ ]?number",
-            "ccv",
-            "ssn");
+    private static final List<String> DEFAULT_MASK_BODY_FIELD_PATTERNS = Arrays.asList(
+            "password",
+            "pwd", 
+            "token",
+            "secret",
+            "auth",
+            "card[-_ ]?number",
+            "ccv",
+            "ssn");

Then update the pattern compilation to use DEFAULT_MASK_BODY_FIELD_PATTERNS as the default list.


189-191: Consider extracting duplicate body size validation.

The body size checking logic is duplicated for both request and response bodies. Consider extracting this into a helper method for better maintainability.

+    private void validateAndTruncateBodySize(RequestResponseBase requestOrResponse) {
+        if (requestOrResponse.getBody() != null && requestOrResponse.getBody().length > MAX_BODY_SIZE) {
+            requestOrResponse.setBody(BODY_TOO_LARGE);
+        }
+    }

     if (request.getBody() != null) {
         // Apply user-provided masking callback for request body
         if (config.getCallbacks() != null) {
             byte[] maskedBody = config.getCallbacks().maskRequestBody(request);
             request.setBody(maskedBody != null ? maskedBody : BODY_MASKED);
         }

-        if (request.getBody().length > MAX_BODY_SIZE) {
-            request.setBody(BODY_TOO_LARGE);
-        }
+        validateAndTruncateBodySize(request);

Also applies to: 207-209


238-242: Handle URL reconstruction exceptions more explicitly.

While the malformed URL catch is good, consider logging the exception for debugging purposes, especially in development environments.

         } catch (MalformedURLException e) {
-            // Keep original URL if malformed
+            logger.debug("Failed to reconstruct URL for query param masking: {}", e.getMessage());
+            // Keep original URL if malformed
         }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0948ea8 and e134436.

📒 Files selected for processing (1)
  • src/main/java/io/apitally/common/RequestLogger.java (10 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/io/apitally/common/RequestLogger.java (4)
src/main/java/io/apitally/common/dto/ExceptionDto.java (1)
  • ExceptionDto (3-25)
src/main/java/io/apitally/common/dto/Header.java (1)
  • Header (3-19)
src/main/java/io/apitally/common/dto/Request.java (1)
  • Request (3-43)
src/main/java/io/apitally/common/dto/RequestLogItem.java (1)
  • RequestLogItem (7-39)
🪛 GitHub Check: codecov/patch
src/main/java/io/apitally/common/RequestLogger.java

[warning] 190-190: src/main/java/io/apitally/common/RequestLogger.java#L190
Added line #L190 was not covered by tests


[warning] 208-208: src/main/java/io/apitally/common/RequestLogger.java#L208
Added line #L208 was not covered by tests


[warning] 240-240: src/main/java/io/apitally/common/RequestLogger.java#L240
Added line #L240 was not covered by tests


[warning] 425-426: src/main/java/io/apitally/common/RequestLogger.java#L425-L426
Added lines #L425 - L426 were not covered by tests

🔇 Additional comments (8)
src/main/java/io/apitally/common/RequestLogger.java (8)

44-44: LGTM: Improved JSON content type detection.

The regex pattern provides more flexible JSON content type detection compared to exact string matching.


148-153: LGTM: Improved exclusion logic structure.

The early return pattern for exclusion checks is clean and efficient. The callback-based exclusion check provides good extensibility.


162-168: LGTM: Clean separation of concerns.

Creating the RequestLogItem upfront and deferring masking to applyMasking() improves the architecture by separating logging from masking concerns.


178-244: Excellent centralization of masking logic.

The applyMasking() method successfully centralizes all masking operations, improving maintainability. The logic flow is clear: user callbacks → size limits → field-level masking → headers → query params.


419-443: LGTM: Robust JSON masking implementation.

The JSON masking logic correctly handles the compilation error from the past review by using fields().forEachRemaining(). The recursive approach properly handles nested objects and arrays, and the exception handling gracefully falls back to the original body.


451-454: LGTM: Focused JSON content type detection.

The dedicated method with regex pattern matching provides more accurate JSON detection compared to the broader hasSupportedContentType() method.


255-268: LGTM: Clean serialization with proper object composition.

The writeToFile method now properly applies masking before serialization and uses Jackson's ObjectNode for clean JSON structure composition. The skipEmptyValues() call helps reduce log size.


111-112: ```shell
#!/bin/bash

Search for the getBodyFieldMaskPatterns method in the codebase

rg -n "getBodyFieldMaskPatterns" -C3 .


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@itssimon itssimon merged commit cb65793 into main Jun 26, 2025
16 checks passed
@itssimon itssimon deleted the body-field-masking branch June 26, 2025 05:37
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