Skip to content

setBody#2462

Merged
rrayst merged 7 commits into
masterfrom
set-body-interceptor
Dec 19, 2025
Merged

setBody#2462
rrayst merged 7 commits into
masterfrom
set-body-interceptor

Conversation

@predic8
Copy link
Copy Markdown
Member

@predic8 predic8 commented Dec 15, 2025

Summary by CodeRabbit

  • New Features

    • Set HTTP request/response bodies using configurable template expressions.
  • Bug Fixes

    • Error responses now record the failed expression under the key "expression" for clearer diagnostics.
  • Refactor

    • Consolidated dollar-style template parsing into a shared parser context for consistent templating behavior.
  • Tests

    • Added unit tests validating body-setting behavior across expression scenarios.

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

- Introduced `SetBodyInterceptor` to set HTTP message body content using static strings or dynamic expressions.
- Added `DollarTemplateParserContext` for SpEL-based template parsing.
- Refactored `TemplateExchangeExpression` and related classes to use the centralized `DollarTemplateParserContext`.
- Improved error handling in `AbstractSetterInterceptor` with clearer logging and internal responses.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 15, 2025

Walkthrough

Adds a new SetBodyInterceptor that evaluates expressions to set HTTP message bodies, extracts a shared DollarTemplateParserContext for SpEL templates, updates TemplateExchangeExpression imports/usage, and renames an error detail key from "value" to "expression" in AbstractSetterInterceptor.

Changes

Cohort / File(s) Summary
New interceptor
core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java
New class that evaluates a configured expression and writes the resulting UTF‑8 string into the HTTP message body for request and response flows; builds ProblemDetails and aborts the exchange on evaluation failure.
Abstract setter change
core/src/main/java/com/predic8/membrane/core/interceptor/lang/AbstractSetterInterceptor.java
Error detail key renamed from "value" to "expression" in internal error responses; removed an empty Javadoc block.
Dollar template parser extraction
core/src/main/java/com/predic8/membrane/core/lang/spel/DollarTemplateParserContext.java, core/src/main/java/com/predic8/membrane/core/lang/spel/SpELExchangeExpression.java
Introduced standalone DollarTemplateParserContext singleton and removed the former inner DollarBracketTemplateParserContext from SpELExchangeExpression.
Template expression usage
core/src/main/java/com/predic8/membrane/core/lang/TemplateExchangeExpression.java
Switched to use the shared DOLLAR_TEMPLATE_PARSER_CONTEXT for SpEL-based template construction and adjusted imports.
ExchangeExpression comment
core/src/main/java/com/predic8/membrane/core/lang/ExchangeExpression.java
Added an inline comment in the SPEL case; no behavioral change.
Tests
core/src/test/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptorTest.java
New unit tests validating SetBodyInterceptor behavior for request/response and various expression results.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Exchange
    participant SetBodyInterceptor
    participant ExpressionEngine
    participant Message
    participant ProblemDetails

    Note over Client,SetBodyInterceptor: SetBodyInterceptor evaluation and apply flow
    Client->>Exchange: send request / receive response
    Exchange->>SetBodyInterceptor: handleRequest(handleResponse)(exc)
    SetBodyInterceptor->>ExpressionEngine: evaluate(expression, exchange)
    alt evaluation succeeds
        ExpressionEngine-->>SetBodyInterceptor: evaluatedString
        SetBodyInterceptor->>Message: setBody(evaluatedString as UTF-8)
        SetBodyInterceptor->>Exchange: continue()
    else evaluation fails
        ExpressionEngine-->>SetBodyInterceptor: exception
        SetBodyInterceptor->>ProblemDetails: build(detailKey="expression", cause)
        SetBodyInterceptor->>Exchange: abortWith(ProblemDetails)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect SetBodyInterceptor: expression evaluation, UTF‑8 conversion, request vs response handling, and error/abort behavior.
  • Verify ProblemDetails construction and the renamed error detail key ("expression") for consistency.
  • Confirm DollarTemplateParserContext singleton usage and TemplateExchangeExpression import changes preserve prior SpEL templating behavior.

Possibly related PRs

Suggested reviewers

  • christiangoerdes
  • rrayst

Poem

🐰 I hopped through templates and found a shiny key,
Bodies set from expressions — quick as can be,
Dollar parser gathered, no more tucked-away nest,
Errors now whisper "expression" — clearer than the rest,
I twirl with joy and burrow into testing glee! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'setBody' is concise and directly describes the main change: adding a new SetBodyInterceptor class to set HTTP message bodies via expressions.
✨ 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 set-body-interceptor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (3)
core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java (2)

37-54: Consider tightening response characteristics and log level for evaluation failures

The core path is correct and the ProblemDetails usage is consistent, but two small improvements might help:

  • Since you always serialize the evaluated value as UTF‑8, consider explicitly aligning or setting the Content-Type/charset of the message body when appropriate so downstream components and clients don’t see a header/encoding mismatch.
  • The log entry for failed evaluations is currently at info level; depending on expected frequency, this might be better suited to warn (for unexpected failures) or debug (if failures can be user‑driven and noisy).
    These are behavioral/operational tweaks only; the current logic is otherwise sound.

56-79: Config API and descriptions are clear; optionally guard for verbose or null expressions

The @MCAttribute setter and getDisplayName are straightforward and consistent. One optional tweak: if expressions can be very long or unset, you might want getShortDescription() to either truncate the expression or fall back to a static description to avoid surfacing "null" or overly verbose text in UIs/logs.

core/src/main/java/com/predic8/membrane/core/lang/spel/DollarTemplateParserContext.java (1)

3-3: Consider using a specific import instead of wildcard.

Wildcard imports can obscure which classes are actually used and may lead to naming conflicts. Consider importing only the specific class needed.

Apply this diff:

-import org.springframework.expression.common.*;
+import org.springframework.expression.common.TemplateParserContext;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 90ae79a and 5f4f3e8.

📒 Files selected for processing (5)
  • core/src/main/java/com/predic8/membrane/core/interceptor/lang/AbstractSetterInterceptor.java (1 hunks)
  • core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java (1 hunks)
  • core/src/main/java/com/predic8/membrane/core/lang/TemplateExchangeExpression.java (2 hunks)
  • core/src/main/java/com/predic8/membrane/core/lang/spel/DollarTemplateParserContext.java (1 hunks)
  • core/src/main/java/com/predic8/membrane/core/lang/spel/SpELExchangeExpression.java (0 hunks)
💤 Files with no reviewable changes (1)
  • core/src/main/java/com/predic8/membrane/core/lang/spel/SpELExchangeExpression.java
🧰 Additional context used
🧬 Code graph analysis (2)
core/src/main/java/com/predic8/membrane/core/lang/TemplateExchangeExpression.java (1)
core/src/main/java/com/predic8/membrane/core/lang/spel/DollarTemplateParserContext.java (1)
  • DollarTemplateParserContext (5-13)
core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java (2)
core/src/main/java/com/predic8/membrane/core/exceptions/ProblemDetails.java (1)
  • ProblemDetails (36-414)
core/src/main/java/com/predic8/membrane/core/util/ExceptionUtil.java (1)
  • ExceptionUtil (16-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java)
🔇 Additional comments (3)
core/src/main/java/com/predic8/membrane/core/interceptor/lang/AbstractSetterInterceptor.java (1)

62-62: LGTM! Improved error detail naming.

The change from "value" to "expression" makes the error detail key more descriptive and consistent with the variable name and surrounding context (e.g., line 55's error message also references "expression").

core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java (1)

22-35: Request/response wiring and reuse via handleInternal look solid

Using handleRequest/handleResponse as thin wrappers around a shared handleInternal keeps behavior consistent across flows and avoids duplication. No issues from a correctness or design perspective here.

core/src/main/java/com/predic8/membrane/core/lang/spel/DollarTemplateParserContext.java (1)

5-11: Refactoring verified and complete.

The singleton implementation is correct and thread-safe. All references to the old DollarBracketTemplateParserContext inner class have been successfully removed, and the new DOLLAR_TEMPLATE_PARSER_CONTEXT instance is properly imported and used in TemplateExchangeExpression.newInstance(). The refactoring improves code organization by centralizing parser context configuration. The ExchangeExpression.expression() factory method intentionally passes null to support plain SpEL expressions without templating, serving a different use case than TemplateExchangeExpression.

…ngeExpression`

- Clarified UTF-8 encoding in `SetBodyInterceptor` Javadoc and added corresponding comment in method logic.
- Added context on null `parserContext` in `SpELExchangeExpression` construction.
Copy link
Copy Markdown
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 (2)
core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java (2)

47-47: Consider logging exceptions at WARN or ERROR level instead of INFO.

Logging exceptions at INFO level may cause them to be missed in production environments where INFO logging is often disabled. Since this represents an error condition that aborts the exchange, WARN or ERROR would be more appropriate.

-            log.info(message);
+            log.error(message);

65-68: Consider adding validation for the expression value.

The setValue method does not validate whether the provided expression is null or empty. While this might be intentional or handled by the parent class, consider adding validation to fail fast during configuration rather than at runtime.

 @MCAttribute
 public void setValue(String value) {
+    if (value == null || value.trim().isEmpty()) {
+        throw new IllegalArgumentException("Expression cannot be null or empty");
+    }
     this.expression = value;
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f4f3e8 and f42bad3.

📒 Files selected for processing (2)
  • core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java (1 hunks)
  • core/src/main/java/com/predic8/membrane/core/lang/ExchangeExpression.java (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • core/src/main/java/com/predic8/membrane/core/lang/ExchangeExpression.java
🧰 Additional context used
🧬 Code graph analysis (1)
core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java (2)
core/src/main/java/com/predic8/membrane/core/exceptions/ProblemDetails.java (1)
  • ProblemDetails (36-414)
core/src/main/java/com/predic8/membrane/core/util/ExceptionUtil.java (1)
  • ExceptionUtil (16-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java)

@membrane-ci-server
Copy link
Copy Markdown

This pull request needs "/ok-to-test" from an authorized committer.

…tests

- Updated `SetBodyInterceptor` to handle cases where expression evaluation returns null, setting the body to "null" instead.
- Introduced `SetBodyInterceptorTest` to verify null handling, expression evaluation, and response body modifications.
Copy link
Copy Markdown
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: 0

♻️ Duplicate comments (1)
core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java (1)

48-59: Consider allowing stacktraces for easier debugging in non‑production.

Currently, the ProblemDetails builder unconditionally calls .stacktrace(false), which suppresses stacktraces even when getRouter().isProduction() is false. This can make expression failures harder to debug during development, given that ProblemDetails already supports conditional exposure of details based on the production flag (see ProblemDetails in core). Based on relevant code snippets, this suppression may be redundant in non‑production.

You might consider either:

  • Removing .stacktrace(false) and relying on ProblemDetails’ default behavior, or
  • Making stacktrace inclusion conditional on a debug/dev flag.
🧹 Nitpick comments (1)
core/src/test/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptorTest.java (1)

22-52: Good coverage of primary behaviors; consider optional extra cases.

The tests nicely cover literal values, simple path templating, and status-derived output on the request flow. If you want to harden this further, you could optionally add:

  • A handleResponse-based test to ensure the interceptor behaves identically on the response path.
  • A failure-case test (e.g., invalid expression) asserting that a ProblemDetails response is produced and the exchange is aborted.

These are non-blocking and can be deferred.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f42bad3 and 8d23a3d.

📒 Files selected for processing (2)
  • core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java (1 hunks)
  • core/src/test/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptorTest.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java (2)
core/src/main/java/com/predic8/membrane/core/exceptions/ProblemDetails.java (1)
  • ProblemDetails (36-414)
core/src/main/java/com/predic8/membrane/core/util/ExceptionUtil.java (1)
  • ExceptionUtil (16-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java)
🔇 Additional comments (1)
core/src/main/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptor.java (1)

39-48: Core interception logic and null handling look solid.

Delegating both flows to handleInternal keeps behavior consistent, and the explicit result variable plus null-to-"null" mapping avoids NPEs when evaluating the expression. UTF‑8 encoding is appropriate given the configuration story.

No changes needed here.

…ogic

- Updated test method to use `handleResponse` instead of `handleRequest` in `SetBodyInterceptorTest`.
- Adjusted assertions to verify response body content instead of request.
Copy link
Copy Markdown
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d23a3d and 2953566.

📒 Files selected for processing (1)
  • core/src/test/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptorTest.java (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java)
🔇 Additional comments (5)
core/src/test/java/com/predic8/membrane/core/interceptor/lang/SetBodyInterceptorTest.java (5)

1-11: LGTM!

Imports are appropriate and well-organized for testing the SetBodyInterceptor with JUnit 5.


19-28: LGTM!

The setup method correctly initializes test fixtures with appropriate test data. The Exchange is properly constructed with both request and response for comprehensive testing.


30-36: LGTM!

The test correctly verifies that a literal "null" string value is set as the request body. The initialization, invocation, and assertion sequence is appropriate.


38-44: LGTM!

The test correctly verifies that the expression ${path} is evaluated and resolved to the request path "/foo". Expression evaluation logic is properly exercised.


46-52: LGTM!

The test correctly verifies that response bodies can be set using expressions. The ${statusCode} expression properly resolves to "501" from the exchange response.

@rrayst rrayst merged commit fd4a13a into master Dec 19, 2025
4 of 5 checks passed
@rrayst rrayst deleted the set-body-interceptor branch December 19, 2025 16:23
@coderabbitai coderabbitai Bot mentioned this pull request Feb 20, 2026
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