feat(dashboard): add raw JSON body mode to HTTP request step#10638
feat(dashboard): add raw JSON body mode to HTTP request step#10638FredRMonizze wants to merge 8 commits intonovuhq:nextfrom
Conversation
The HTTP Request action step only supported flat key-value pairs for the
request body, making it impossible to send nested JSON objects required
by many third-party APIs (e.g. CM Telecom Voice API).
This adds a toggle between "Key-Value" and "Raw JSON" modes:
- New `bodyMode` field ("key-value" | "raw") with backward-compatible default
- New `rawBody` string field for raw JSON input
- Frontend toggle in the HTTP request editor
- New `RawBodyEditor` component with multiline input and variable support
- Backend `parseRawBody()` function to deserialize raw JSON
- Updated curl preview to reflect raw body mode
- Unit tests for parseRawBody and existing utils
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update test-http-endpoint usecase to support rawBody mode (was always
using key-value pairs, breaking the test feature in raw mode)
- Update CurlDisplay component to render raw body when bodyMode is 'raw'
- Add live JSON validation in RawBodyEditor with error message display
- Skip validation when value contains LiquidJS variables ({{...}})
- Only persist raw body to form when JSON is valid (prevents broken state)
- Add confirmation prompt when switching back to key-value mode with
existing raw JSON content (prevents accidental data loss)
- Use spans with role=button instead of <button> elements to fix
invalid HTML nesting (button inside button warning)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
👷 Deploy request for dashboard-v2-novu-staging pending review.Visit the deploys page to approve it
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a "raw" JSON body mode for HTTP workflow steps across UI, schemas/DTOs, curl preview, parsing utilities and tests, and backend handling in test and execution use cases. (23 words) Changes
Sequence Diagram(s)sequenceDiagram
rect rgba(66,133,244,0.5)
participant Editor
end
rect rgba(15,157,88,0.5)
participant API
end
rect rgba(219,68,55,0.5)
participant Worker
end
rect rgba(244,180,0,0.5)
participant External
end
Editor->>Editor: User edits step (bodyMode + bodyPairs or rawBody)
Editor->>API: Save step (bodyMode, rawBody / body pairs)
API->>Worker: Trigger send (compiled step with bodyMode/rawBody)
alt bodyMode == "raw"
Worker->>Worker: parseRawBody(rawBody)
Worker->>Worker: buildNovuSignatureHeader(parsedBody)
else key-value
Worker->>Worker: toBodyRecord(bodyPairs)
Worker->>Worker: buildNovuSignatureHeader(bodyObject)
end
Worker->>External: Send HTTP request with computed body & headers
External->>Worker: Response
Worker->>API: Execution result
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (3)
libs/application-generic/src/services/http-client/http-request.utils.ts (1)
21-28: Add blank line beforereturnstatement.Per coding guidelines, include a blank line before every
returnstatement.✏️ Suggested fix
export function parseRawBody(raw: string): Record<string, unknown> { const parsed = JSON.parse(raw); if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) { throw new Error('Raw body must be a JSON object'); } + return parsed as Record<string, unknown>; }As per coding guidelines: "Include a blank line before every
returnstatement".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/application-generic/src/services/http-client/http-request.utils.ts` around lines 21 - 28, In parseRawBody, add a blank line immediately before the final return statement in the function parseRawBody to follow the coding guideline requiring a blank line before every return; keep the function logic unchanged and only insert the empty line above the return parsed as Record<string, unknown>.apps/dashboard/src/components/workflow-editor/steps/http-request/http-request-console-preview.tsx (1)
509-514: Consider includingrawBodyandbodyModeincontrolsKeyto reset test results when body mode changes.The
controlsKeyis used to detect control value changes and reset test results. However, it doesn't includerawBodyorbodyMode, so switching between body modes or editing raw body content won't trigger a reset of stale test results.✏️ Suggested fix
const controlsKey = JSON.stringify({ url: controlValues?.url, method: controlValues?.method, headers: controlValues?.headers, body: controlValues?.body, + rawBody: controlValues?.rawBody, + bodyMode: controlValues?.bodyMode, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dashboard/src/components/workflow-editor/steps/http-request/http-request-console-preview.tsx` around lines 509 - 514, controlsKey currently JSON.stringifies only url, method, headers, and body so changes to controlValues.rawBody or controlValues.bodyMode won't invalidate cached test results; update the controlsKey construction (the const controlsKey definition) to include controlValues.rawBody and controlValues.bodyMode so switching body mode or editing rawBody will produce a different key and reset test results accordingly.apps/api/src/app/workflows-v2/usecases/test-http-endpoint/test-http-endpoint.usecase.ts (1)
64-67: Consider handlingparseRawBodyerrors gracefully for better test endpoint UX.If
parseRawBodythrows (e.g., invalid JSON or non-object JSON), the error will propagate uncaught, resulting in a 500 error. For a test endpoint, it may be more user-friendly to catch this error and return a descriptive 400 response indicating the raw body is invalid.✏️ Suggested approach
+ let resolvedBodyPairs: Record<string, unknown>; + if (bodyMode === 'raw' && rawJsonBody) { + try { + resolvedBodyPairs = parseRawBody(rawJsonBody); + } catch (error) { + const durationMs = Math.round(performance.now() - startTime); + return { + statusCode: 400, + body: { error: error instanceof Error ? error.message : 'Invalid raw body JSON' }, + headers: {}, + durationMs, + resolvedRequest: { url: resolvedUrl, method, headers: resolvedHeaders }, + }; + } + } else { + resolvedBodyPairs = Object.fromEntries(compiledBody.filter(({ key }) => key).map(({ key, value }) => [key, value])); + } - const resolvedBodyPairs: Record<string, unknown> = - bodyMode === 'raw' && rawJsonBody - ? parseRawBody(rawJsonBody) - : Object.fromEntries(compiledBody.filter(({ key }) => key).map(({ key, value }) => [key, value]));Note:
startTimewould need to be moved before this block.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/api/src/app/workflows-v2/usecases/test-http-endpoint/test-http-endpoint.usecase.ts` around lines 64 - 67, The code may throw from parseRawBody when bodyMode === 'raw' and rawJsonBody is invalid, causing an uncaught 500; wrap the parseRawBody call in a try/catch and, on parse failure or non-object result, return a 400 response with a clear message that the raw body is invalid (including parse error details), using the same response path your use case returns for bad requests; ensure startTime (used for timing/logging) is initialized before this parsing block so timing/logging still works when you early-return.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/dashboard/src/components/workflow-editor/steps/http-request/curl-display.tsx`:
- Around line 25-43: The current curl-display logic falls back to key/value
`body` when bodyMode === 'raw' but rawBody is empty; update the branch in
curl-display (variables: canHaveBody, bodyMode, rawBody, bodyStr, body) so that
when bodyMode === 'raw' you treat raw mode as exclusive — if rawBody is falsy
set bodyStr to an empty string/null and do not serialize `body`; alternatively
call the existing serializer from curl-utils.ts instead of duplicating logic so
raw-mode behavior matches curl builder and prevents drift.
In
`@apps/dashboard/src/components/workflow-editor/steps/http-request/curl-utils.ts`:
- Around line 35-54: The curl generation currently treats bodyMode === 'raw' as
non-exclusive and falls back to key-value parsing when rawBody is empty; update
the logic in the canHaveBody block so raw mode is handled exclusively: if
bodyMode === 'raw' only use rawBody (set bodyStr when rawBody is present,
otherwise leave bodyStr empty/no fallback), and only run the
key-value/Object.fromEntries branch when bodyMode !== 'raw'; refer to the
variables/functions bodyMode, rawBody, body, bodyStr and the existing key-value
branch to implement this change.
- Around line 36-37: The curl command assembly is vulnerable to unescaped single
quotes in header values and body content (see the header construction and the
branches in curl-utils.ts where bodyMode === 'raw' with rawBody and the
JSON.stringify(bodyObj) branch build --data strings); fix by shell-escaping
single quotes before interpolation (replace each ' with '\'' ) for header values
and for both rawBody and the JSON.stringify(bodyObj) output so the resulting
--header and --data arguments are safe in a single-quoted curl command.
In
`@apps/dashboard/src/components/workflow-editor/steps/http-request/http-request-editor.tsx`:
- Around line 36-45: The confirmation text warns the raw body will be discarded
but the handler only changes bodyMode; update handleBodyModeChange so that when
switching from 'raw' to 'key-value' and the user confirms you also clear the raw
body from form state (e.g., call setValue for the raw body field) before calling
saveForm(); alternatively, change the confirmation copy to state that the raw
body will only be ignored while in Key-Value mode rather than discarded—refer to
handleBodyModeChange, bodyMode, rawBody, setValue('bodyMode', ...), saveForm()
to locate where to apply the change.
- Around line 61-87: The two span[role="button"] toggles for bodyMode should be
replaced with the accessible Radix-based primitives: use ToggleGroup
(type="single") with ToggleGroupItem elements instead of the spans; wire
ToggleGroup's value to bodyMode and onValueChange to handleBodyModeChange, and
move the existing className/conditional styling into each ToggleGroupItem so
visual state remains identical while gaining correct ARIA pressed state,
keyboard/focus handling, and semantics; update imports to pull ToggleGroup and
ToggleGroupItem from src/components/primitives and remove the onClick/onKeyDown
handlers from the old spans.
In
`@apps/worker/src/app/workflow/usecases/send-message/execute-http-request-step.usecase.ts`:
- Line 150: The code currently calls parseRawBody inline when computing
bodyObject in executeHttpRequestStep (bodyMode, rawJsonBody, toBodyRecord) which
will throw on malformed JSON and skip creating execution details; wrap the
parseRawBody call in a try/catch, and on catch call CreateExecutionDetails (with
the step id/context and the caught error message) to record the failure before
rethrowing or returning a handled error; ensure bodyObject is assigned a
sensible fallback (e.g., null or an empty record) when parsing fails and that
the catch includes enough context from the request/step to aid observability.
In
`@libs/application-generic/src/dtos/workflow/controls/http-request-control.dto.ts`:
- Around line 51-57: Add a runtime validator restricting allowed values for the
bodyMode property: import and apply the `@IsIn`(['key-value','raw']) decorator to
the bodyMode field (in http-request-control.dto.ts, on the bodyMode property) so
runtime validation matches the TypeScript type; keep `@IsOptional` and `@IsString`
as needed and ensure the `@IsIn` import is added from class-validator.
In `@libs/application-generic/src/usecases/preview/preview.usecase.ts`:
- Around line 215-220: The body selection logic allows a raw mode to fall back
to stale key-value pairs; change the computation of bodyRecord so that when
bodyMode === 'raw' you only use rawJsonBody (parseRawBody(rawJsonBody) if
present, otherwise undefined) and do not fall back to bodyPairs, and when
bodyMode !== 'raw' use bodyPairs via toBodyRecord; update the same pattern in
execute-http-request-step.usecase.ts where similar logic occurs to ensure raw
mode is exclusive.
---
Nitpick comments:
In
`@apps/api/src/app/workflows-v2/usecases/test-http-endpoint/test-http-endpoint.usecase.ts`:
- Around line 64-67: The code may throw from parseRawBody when bodyMode ===
'raw' and rawJsonBody is invalid, causing an uncaught 500; wrap the parseRawBody
call in a try/catch and, on parse failure or non-object result, return a 400
response with a clear message that the raw body is invalid (including parse
error details), using the same response path your use case returns for bad
requests; ensure startTime (used for timing/logging) is initialized before this
parsing block so timing/logging still works when you early-return.
In
`@apps/dashboard/src/components/workflow-editor/steps/http-request/http-request-console-preview.tsx`:
- Around line 509-514: controlsKey currently JSON.stringifies only url, method,
headers, and body so changes to controlValues.rawBody or controlValues.bodyMode
won't invalidate cached test results; update the controlsKey construction (the
const controlsKey definition) to include controlValues.rawBody and
controlValues.bodyMode so switching body mode or editing rawBody will produce a
different key and reset test results accordingly.
In `@libs/application-generic/src/services/http-client/http-request.utils.ts`:
- Around line 21-28: In parseRawBody, add a blank line immediately before the
final return statement in the function parseRawBody to follow the coding
guideline requiring a blank line before every return; keep the function logic
unchanged and only insert the empty line above the return parsed as
Record<string, unknown>.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fb1ed1f7-727b-4942-ba97-42853c2ac77b
📒 Files selected for processing (15)
apps/api/src/app/workflows-v2/usecases/test-http-endpoint/test-http-endpoint.usecase.tsapps/dashboard/src/components/workflow-editor/steps/component-utils.tsxapps/dashboard/src/components/workflow-editor/steps/http-request/configure-http-request-step-preview.tsxapps/dashboard/src/components/workflow-editor/steps/http-request/curl-display.tsxapps/dashboard/src/components/workflow-editor/steps/http-request/curl-utils.tsapps/dashboard/src/components/workflow-editor/steps/http-request/http-request-console-preview.tsxapps/dashboard/src/components/workflow-editor/steps/http-request/http-request-editor.tsxapps/dashboard/src/components/workflow-editor/steps/http-request/raw-body-editor.tsxapps/worker/src/app/workflow/usecases/send-message/execute-http-request-step.usecase.tslibs/application-generic/src/dtos/workflow/controls/http-request-control.dto.tslibs/application-generic/src/services/http-client/http-request.utils.spec.tslibs/application-generic/src/services/http-client/http-request.utils.tslibs/application-generic/src/usecases/preview/preview.usecase.tspackages/shared/src/consts/providers/channels/http-request.tspackages/shared/src/dto/workflows/step.dto.ts
apps/dashboard/src/components/workflow-editor/steps/http-request/curl-display.tsx
Show resolved
Hide resolved
apps/dashboard/src/components/workflow-editor/steps/http-request/curl-utils.ts
Show resolved
Hide resolved
apps/dashboard/src/components/workflow-editor/steps/http-request/curl-utils.ts
Outdated
Show resolved
Hide resolved
apps/dashboard/src/components/workflow-editor/steps/http-request/http-request-editor.tsx
Show resolved
Hide resolved
apps/dashboard/src/components/workflow-editor/steps/http-request/http-request-editor.tsx
Outdated
Show resolved
Hide resolved
apps/worker/src/app/workflow/usecases/send-message/execute-http-request-step.usecase.ts
Outdated
Show resolved
Hide resolved
libs/application-generic/src/dtos/workflow/controls/http-request-control.dto.ts
Show resolved
Hide resolved
libs/application-generic/src/usecases/preview/preview.usecase.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 15f7166. Configure here.
apps/worker/src/app/workflow/usecases/send-message/execute-http-request-step.usecase.ts
Outdated
Show resolved
Hide resolved
- Use ToggleGroup primitive for body mode toggle (proper ARIA, keyboard handling) instead of custom span buttons - Wrap parseRawBody in try/catch in execute-http-request-step usecase and create execution details on parse failure (was throwing silently) - Wrap parseRawBody in try/catch in test-http-endpoint usecase and return 400 with clear message instead of uncaught 500 - Add @isin validator on bodyMode field in HttpRequestControlDto - Make raw mode exclusive in preview and execute usecases (no fallback to stale key-value pairs when bodyMode is raw) - Include rawBody and bodyMode in controlsKey to invalidate test cache when switching modes or editing raw body - Use local draft state in RawBodyEditor so the form (and preview) only updates when JSON is valid, while the user can still freely type Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the user confirms switching from raw to key-value mode, also clear the rawBody field so the discard message matches actual behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/dashboard/src/components/workflow-editor/steps/http-request/raw-body-editor.tsx`:
- Around line 20-23: The JSON validator in raw-body-editor.tsx (validateJson)
currently rejects top-level arrays by checking Array.isArray(parsed) and
returning 'Body must be a JSON object'; change the validation to accept both
objects and arrays: after JSON.parse(value) ensure parsed is not null and that
it's either an object or an array (i.e., remove the Array.isArray(parsed)
rejection and update the error message to reflect "Body must be valid JSON" or
"Body must be a JSON object or array"); update any related conditional that uses
parsed to distinguish behavior when an object vs array is required downstream if
necessary.
- Around line 62-69: The onChange handler currently persists whitespace-only
drafts verbatim; update the save path so after computing newVal (and calling
setDraft) you normalize whitespace-only content to an empty string before
propagating: use validateJson(newVal) as before, but when deciding what to
persist call something like trimmed = newVal.trim() and if trimmed === '' pass
'' to field.onChange and saveForm, otherwise pass the original newVal; touch the
onChange block around setDraft, validateJson, field.onChange, and saveForm to
implement this.
In
`@apps/worker/src/app/workflow/usecases/send-message/execute-http-request-step.usecase.ts`:
- Around line 151-157: The raw-body handling currently narrows payloads to
Record<string, unknown> which rejects top-level JSON arrays; update the parsing
and type usage so raw JSON can be any JSON value (object or array). Change the
local variable in execute-http-request-step.usecase.ts from Record<string,
unknown> | undefined to unknown | undefined (or a JSONValue type that includes
arrays), update parseRawBody in http-request.utils.ts to return unknown (or
JSONValue) instead of Record<string, unknown>, and adjust shouldIncludeBody and
the preview/test codepaths in preview.usecase.ts (the HMAC generation path) to
accept the widened type so top-level arrays like [{"id":1}] are allowed
end-to-end.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 663c4792-d6cf-40c2-af40-4b9a0d24c586
📒 Files selected for processing (7)
apps/api/src/app/workflows-v2/usecases/test-http-endpoint/test-http-endpoint.usecase.tsapps/dashboard/src/components/workflow-editor/steps/http-request/http-request-console-preview.tsxapps/dashboard/src/components/workflow-editor/steps/http-request/http-request-editor.tsxapps/dashboard/src/components/workflow-editor/steps/http-request/raw-body-editor.tsxapps/worker/src/app/workflow/usecases/send-message/execute-http-request-step.usecase.tslibs/application-generic/src/dtos/workflow/controls/http-request-control.dto.tslibs/application-generic/src/usecases/preview/preview.usecase.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/dashboard/src/components/workflow-editor/steps/http-request/http-request-console-preview.tsx
- apps/api/src/app/workflows-v2/usecases/test-http-endpoint/test-http-endpoint.usecase.ts
- libs/application-generic/src/dtos/workflow/controls/http-request-control.dto.ts
- apps/dashboard/src/components/workflow-editor/steps/http-request/http-request-editor.tsx
apps/dashboard/src/components/workflow-editor/steps/http-request/raw-body-editor.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/components/workflow-editor/steps/http-request/raw-body-editor.tsx
Show resolved
Hide resolved
apps/worker/src/app/workflow/usecases/send-message/execute-http-request-step.usecase.ts
Outdated
Show resolved
Hide resolved
…drafts - Allow top-level JSON arrays in raw body mode (parseRawBody, validateJson, shouldIncludeBody) — APIs like batch endpoints expect arrays at the root - Update parseRawBody return type to Record<string, unknown> | unknown[] - Update bodyObject types in execute, preview, and test usecases accordingly - Persist whitespace-only raw body drafts as empty string so the backend doesn't see a non-empty body containing only spaces - Update unit tests to verify array support
Both CurlDisplay and buildRawCurlString still fell back to rendering the key-value body when bodyMode === 'raw' but rawBody was empty. This made the preview show a payload that the backend would not actually send. Now in raw mode, the curl preview only renders rawBody (or nothing).
Headers, URL, and body values containing a single quote (e.g.
{"text":"it's ok"}) would break the generated curl command because
single quotes cannot appear inside POSIX shell single-quoted strings.
Add an escapeShellSingleQuoted helper that closes, escapes, and reopens
the quote ('\''), and apply it everywhere curl-utils and curl-display
interpolate user-provided values into the rendered curl command.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/dashboard/src/components/workflow-editor/steps/http-request/curl-utils.ts (1)
13-15: Match the repo's return-spacing rule in this helper.Please add a blank line before the
returnhere to keep the file aligned with the shared TypeScript formatting convention.As per coding guidelines, "Include a blank line before every return statement".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dashboard/src/components/workflow-editor/steps/http-request/curl-utils.ts` around lines 13 - 15, The helper function escapeShellSingleQuoted currently returns immediately without a preceding blank line; update the function (escapeShellSingleQuoted) to insert a blank line before the return statement to comply with the repo's "blank line before return" formatting rule and ensure the file matches shared TypeScript conventions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/dashboard/src/components/workflow-editor/steps/http-request/curl-utils.ts`:
- Around line 67-71: The generated curl command omits the provided method
parameter, so the parts array (`parts`) must include a `--request` option to
reflect non-GET methods; update the first element (the string built with
`escapeShellSingleQuoted(url || 'https://api.example.com/endpoint')`) to include
`--request '${method}'` (use the `method` variable, preferably uppercased) so
the resulting command shows the correct HTTP verb alongside `headerArgs` and
`bodyStr`.
---
Nitpick comments:
In
`@apps/dashboard/src/components/workflow-editor/steps/http-request/curl-utils.ts`:
- Around line 13-15: The helper function escapeShellSingleQuoted currently
returns immediately without a preceding blank line; update the function
(escapeShellSingleQuoted) to insert a blank line before the return statement to
comply with the repo's "blank line before return" formatting rule and ensure the
file matches shared TypeScript conventions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 04bf6697-9f19-4aa9-94e6-50750010968d
📒 Files selected for processing (2)
apps/dashboard/src/components/workflow-editor/steps/http-request/curl-display.tsxapps/dashboard/src/components/workflow-editor/steps/http-request/curl-utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/dashboard/src/components/workflow-editor/steps/http-request/curl-display.tsx
apps/dashboard/src/components/workflow-editor/steps/http-request/curl-utils.ts
Show resolved
Hide resolved
The generated curl command always omitted the --request flag, so non-GET methods (POST, PUT, etc.) rendered as plain GET requests in the preview, which was misleading. Add --request '<METHOD>' to both buildRawCurlString and CurlDisplay, escaping the method for shell safety.

Summary
The HTTP Request action step currently only supports flat key-value pairs for the request body, making it impossible to send nested JSON objects required by many third-party APIs (e.g. CM Telecom Voice API which expects a
voice: { language, gender, ... }object).This PR adds a toggle between Key-Value and Raw JSON modes in the HTTP Request step, allowing users to paste/type a full JSON body with nested objects, arrays, and LiquidJS variables.
Related issue: #8784
Why
When integrating with APIs that expect nested JSON bodies, users currently have no way to send something like:
```json
{
"callee": "+32...",
"voice": {
"language": "fr",
"gender": "Male"
}
}
```
Workarounds tried (all failed):
Changes
Schema & Types
Frontend (Dashboard)
Backend
Tests
Backward compatibility
Test plan
Note
Medium Risk
Adds a new raw JSON body path that is parsed/validated and used for request signing/sending; invalid JSON or unexpected body types can cause runtime failures in HTTP request execution/testing/preview. Defaults preserve existing behavior, but this touches both dashboard UX and worker/api execution paths.
Overview
Adds support for sending HTTP Request step bodies as raw JSON in addition to the existing key-value pairs.
Updates shared schema/DTOs to include
bodyModeandrawBody, adds a dashboardRawBodyEditorwith JSON validation and a mode toggle (with confirmation when switching back), and updates cURL previews/copy to reflect raw bodies.On the backend/worker, introduces
parseRawBodyand wires it into HTTP request execution, test endpoint, and preview signature generation so raw JSON is parsed as an object before signing/sending; includes unit tests for the new parsing utility.Reviewed by Cursor Bugbot for commit 15f7166. Configure here.
What changed
Added a Raw JSON body mode to the HTTP Request action so users can enter nested JSON objects, arrays, and LiquidJS variables as the request body. The UI now offers a Key-Value ↔ Raw toggle and a multiline RawBodyEditor with live JSON validation (skipped when LiquidJS templates are present), draft behavior to avoid publishing invalid/whitespace-only bodies, and a confirmation when switching back to key-value. Backend and worker code parse and validate raw JSON (objects or arrays), return clear errors on parse failure, and ensure HMAC signature previews and curl/test flows reflect raw payloads.
Affected areas
api: Test endpoint use case reads bodyMode/rawBody and uses parseRawBody() for raw mode, returning 400 with a descriptive error and omitting the body in resolvedRequest on parse failure.
dashboard (react): Adds RawBodyEditor, body-mode toggle, draft/validation behavior, and threads rawBody/bodyMode into curl, console, and preview components so previews and generated curl include raw payloads.
worker: ExecuteHttpRequestStep uses parseRawBody() when bodyMode === 'raw', records parse failures to execution details, and fails the step safely while honoring continueOnFailure.
application-generic: Adds parseRawBody(), extends HttpRequestControlDto with bodyMode and rawBody, updates shouldIncludeBody and preview signature generation to support raw mode (objects or arrays).
shared: Adds bodyMode/rawBody to HTTP request control and UI schema and new UiComponentEnum entries DESTINATION_BODY_MODE and DESTINATION_RAW_BODY.
tests: New unit tests for parseRawBody (including top-level arrays), toBodyRecord, and toHeadersRecord; additional manual/preview checks described in the PR test plan.
Key technical decisions
Testing
Added unit tests for parsing and conversions; manual/preview testing covers editor validation, mode-switch confirmation, nested JSON/array sending, LiquidJS resolution in previews, curl/test endpoint raw behavior, and HMAC signature consistency.