Skip to content

refactor(backend): extract shared readRequestBody utility with UTF-8 safety (ACM-38043)#6529

Open
KevinFCormier wants to merge 1 commit into
mainfrom
refactor/ACM-38043-body-parser-utility
Open

refactor(backend): extract shared readRequestBody utility with UTF-8 safety (ACM-38043)#6529
KevinFCormier wants to merge 1 commit into
mainfrom
refactor/ACM-38043-body-parser-utility

Conversation

@KevinFCormier

@KevinFCormier KevinFCormier commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

📝 Summary

Ticket Summary (Title):
Backend: refactor repeated HTTP body chunk collection into shared utility with proper UTF-8 handling

Ticket Link:
https://issues.redhat.com/browse/ACM-38043

Type of Change:

  • 🐞 Bug Fix
  • ✨ Feature
  • 🔧 Refactor
  • 💸 Tech Debt
  • 🧪 Test-related
  • 📄 Docs

What

Extracts the repeated HTTP request body chunk-collection pattern into two shared utilities in backend/src/lib/body-parser.ts:

  • readRequestBody(req) — reads the full request body as a UTF-8 string, calling req.setEncoding('utf8') so Node's internal StringDecoder safely handles multi-byte character boundaries across chunks
  • parseRequestJsonBody<T>(req) — reads the body then JSON.parses it, returning a typed result

Why

10 backend route handlers duplicated the same ~6-line chunk-collection pattern with minor variable name variations (chucks vs chunks, data vs body). This duplication was:

  1. A maintenance burden — the .join() bug fixed in PR #6528 existed in 9 of 10 locations precisely because of copy-paste
  2. Theoretically unsafe for UTF-8 — without setEncoding('utf8'), data events emit Buffer objects. If a multi-byte UTF-8 character is split across chunks, calling .toString() on each chunk individually corrupts it. The new utility sets UTF-8 encoding on the stream so Node's StringDecoder handles this correctly.

Follow-up to ACM-38039 / PR #6528.

Changes

File Before After
body-parser.ts Added readRequestBody() and parseRequestJsonBody<T>()
ansibletower.ts manual chunks readRequestBody → manual JSON.parse
operatorCheck.ts manual chunks readRequestBody (preserves try/catch around parse)
rosaWizardApi.ts (×2) manual chunks parseRequestJsonBody<Payload>
statuses.ts manual chunks parseRequestJsonBody<IRequestStatuses>
appSetData.ts manual chunks readRequestBody (preserves try/catch around parse)
pagination.ts manual chunks parseRequestJsonBody<IRequestListView>
upgrade-risks-prediction.ts manual chunks parseRequestJsonBody<UpgradeRiskBody>
userpreference.ts manual chunks readRequestBody (raw string needed for PUT forwarding)
virtualMachineProxy.ts manual chunks readRequestBody (preserves try/catch around parse)

Net result: +53/−79 lines (26 lines removed).

How to test

  1. npm run test:backend — all 337 tests pass
  2. npm run check:backend — prettier, eslint, tsc all clean
  3. The existing multi-chunk regression test from PR fix(backend): use empty separator when joining HTTP body chunks (ACM-38039) #6528 validates the refactored path

✅ Checklist

General

  • PR title follows the convention
  • Code builds and runs locally without errors
  • No console logs, commented-out code, or unnecessary files
  • All commits are meaningful and well-labeled
  • All new display strings are externalized for localization (English only) — N/A

🗒️ Notes for Reviewers

  • Each handler's error handling, logging, and response logic is preserved exactly — only the chunk-collection boilerplate was replaced.
  • Handlers that need the raw string (e.g. userpreference.ts for PUT forwarding, operatorCheck.ts with try/catch around parse) use readRequestBody. Handlers that always JSON.parse use parseRequestJsonBody<T>.
  • void operator prefixes the .then() calls to satisfy @typescript-eslint/no-floating-promises.

Summary by CodeRabbit

  • Refactor
    • Standardized HTTP request body reading across backend routes with shared promise-based helpers.
    • Simplified JSON body parsing by reusing a common JSON parser and reducing repeated manual buffering.
    • Updated routes to use consistent asynchronous control flow and centralized error responses, while preserving existing request validation, authorization, and response formats.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Introduces shared helpers for reading UTF-8 request streams and parsing JSON bodies, then replaces duplicated data/end buffering in pagination and multiple backend routes.

Changes

Request body parsing

Layer / File(s) Summary
Shared stream and JSON body helpers
backend/src/lib/body-parser.ts
Exports size-limited UTF-8 stream reading, RequestBodyTooLargeError, and typed JSON parsing.
Typed JSON body consumers
backend/src/lib/pagination.ts, backend/src/routes/aggregators/statuses.ts, backend/src/routes/rosaWizardApi.ts, backend/src/routes/upgrade-risks-prediction.ts
Replaces manual buffering and JSON.parse calls with the shared JSON parser and promise-based error handling.
Raw body consumers
backend/src/routes/aggregators/appSetData.ts, backend/src/routes/ansibletower.ts, backend/src/routes/operatorCheck.ts, backend/src/routes/userpreference.ts, backend/src/routes/virtualMachineProxy.ts
Uses the shared stream reader before route-specific parsing, payload construction, or proxying, with centralized rejection handling.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, specific, and accurately describes the main refactor and ticket scope.
Description check ✅ Passed The description follows the template and includes summary, ticket link, type, rationale, changes, testing, and reviewer notes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/ACM-38043-body-parser-utility

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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

@openshift-ci

openshift-ci Bot commented Jul 21, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: KevinFCormier

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (1)
backend/src/lib/body-parser.ts (1)

90-90: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use descriptive request-body parameter names.

  • backend/src/lib/body-parser.ts#L90-L90: rename req to request.
  • backend/src/routes/operatorCheck.ts#L39-L39: rename data to bodyString.
  • backend/src/routes/userpreference.ts#L78-L78: rename data to bodyString.
  • backend/src/routes/virtualMachineProxy.ts#L126-L126: rename bodyStr to bodyString.

As per coding guidelines, “Functions and variables must use descriptive camelCase names and avoid abbreviations.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/lib/body-parser.ts` at line 90, Rename the request-body variables
to descriptive camelCase names and update all references: use request in
readRequestBody, bodyString in the operatorCheck and userpreference route
handlers, and bodyString instead of bodyStr in virtualMachineProxy. Apply these
changes in backend/src/lib/body-parser.ts:90,
backend/src/routes/operatorCheck.ts:39, backend/src/routes/userpreference.ts:78,
and backend/src/routes/virtualMachineProxy.ts:126.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/lib/body-parser.ts`:
- Around line 90-100: The readRequestBody function must enforce a default
request-body byte limit before accumulating chunks. Track UTF-8 byte length,
stop or destroy the stream when the limit is exceeded, reject with an
identifiable size-limit error, and ensure callers propagate that error as HTTP
413 through their existing error paths.

In `@backend/src/lib/pagination.ts`:
- Line 63: Handle rejected body-reader and JSON-parsing promises by adding
terminal catch handling to each detached parser chain:
backend/src/lib/pagination.ts:63-63,
backend/src/routes/aggregators/statuses.ts:32-32,
backend/src/routes/rosaWizardApi.ts:34-34 and 61-61,
backend/src/routes/upgrade-risks-prediction.ts:44-44,
backend/src/routes/aggregators/appSetData.ts:30-30,
backend/src/routes/ansibletower.ts:33-33,
backend/src/routes/operatorCheck.ts:39-39,
backend/src/routes/userpreference.ts:78-78, and
backend/src/routes/virtualMachineProxy.ts:126-126. In each route’s existing
request-processing flow, emit the appropriate error response from the catch
handler, including the inner chain in ansibletower and preserving route-specific
responses; do not rely on surrounding try/catch blocks to catch detached
promises.

---

Nitpick comments:
In `@backend/src/lib/body-parser.ts`:
- Line 90: Rename the request-body variables to descriptive camelCase names and
update all references: use request in readRequestBody, bodyString in the
operatorCheck and userpreference route handlers, and bodyString instead of
bodyStr in virtualMachineProxy. Apply these changes in
backend/src/lib/body-parser.ts:90, backend/src/routes/operatorCheck.ts:39,
backend/src/routes/userpreference.ts:78, and
backend/src/routes/virtualMachineProxy.ts:126.
🪄 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: Enterprise

Run ID: 38037012-b1d1-4247-9e2b-4bebb3acecc0

📥 Commits

Reviewing files that changed from the base of the PR and between aee5af9 and 0077b22.

📒 Files selected for processing (10)
  • backend/src/lib/body-parser.ts
  • backend/src/lib/pagination.ts
  • backend/src/routes/aggregators/appSetData.ts
  • backend/src/routes/aggregators/statuses.ts
  • backend/src/routes/ansibletower.ts
  • backend/src/routes/operatorCheck.ts
  • backend/src/routes/rosaWizardApi.ts
  • backend/src/routes/upgrade-risks-prediction.ts
  • backend/src/routes/userpreference.ts
  • backend/src/routes/virtualMachineProxy.ts

Comment thread backend/src/lib/body-parser.ts
Comment thread backend/src/lib/pagination.ts Outdated
const body = chucks.join('')

const request = JSON.parse(body) as IRequestListView
void parseRequestJsonBody<IRequestListView>(req).then(async (request) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Handle rejected body-reader promises.

Each void read/parseRequestBody(...).then(...) discards stream and JSON parsing rejections. Invalid JSON or a request-stream error can leave the request unanswered and produce an unhandled rejection. Add a terminal .catch() that emits the route-appropriate error response.

  • backend/src/lib/pagination.ts#L63-L63: handle parser rejection before pagination processing.
  • backend/src/routes/aggregators/statuses.ts#L32-L32: handle parser rejection before status aggregation.
  • backend/src/routes/rosaWizardApi.ts#L34-L34: attach rejection handling; the outer try/catch cannot catch this detached chain.
  • backend/src/routes/rosaWizardApi.ts#L61-L61: attach rejection handling; the outer try/catch cannot catch this detached chain.
  • backend/src/routes/upgrade-risks-prediction.ts#L44-L44: handle parser rejection before prediction requests.
  • backend/src/routes/aggregators/appSetData.ts#L30-L30: handle body-read rejection.
  • backend/src/routes/ansibletower.ts#L33-L33: handle invalid JSON/body-read rejection in the inner chain.
  • backend/src/routes/operatorCheck.ts#L39-L39: handle body-read rejection.
  • backend/src/routes/userpreference.ts#L78-L78: handle body-read and POST JSON parsing rejection.
  • backend/src/routes/virtualMachineProxy.ts#L126-L126: handle body-read rejection.
📍 Affects 9 files
  • backend/src/lib/pagination.ts#L63-L63 (this comment)
  • backend/src/routes/aggregators/statuses.ts#L32-L32
  • backend/src/routes/rosaWizardApi.ts#L34-L34
  • backend/src/routes/rosaWizardApi.ts#L61-L61
  • backend/src/routes/upgrade-risks-prediction.ts#L44-L44
  • backend/src/routes/aggregators/appSetData.ts#L30-L30
  • backend/src/routes/ansibletower.ts#L33-L33
  • backend/src/routes/operatorCheck.ts#L39-L39
  • backend/src/routes/userpreference.ts#L78-L78
  • backend/src/routes/virtualMachineProxy.ts#L126-L126
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/lib/pagination.ts` at line 63, Handle rejected body-reader and
JSON-parsing promises by adding terminal catch handling to each detached parser
chain: backend/src/lib/pagination.ts:63-63,
backend/src/routes/aggregators/statuses.ts:32-32,
backend/src/routes/rosaWizardApi.ts:34-34 and 61-61,
backend/src/routes/upgrade-risks-prediction.ts:44-44,
backend/src/routes/aggregators/appSetData.ts:30-30,
backend/src/routes/ansibletower.ts:33-33,
backend/src/routes/operatorCheck.ts:39-39,
backend/src/routes/userpreference.ts:78-78, and
backend/src/routes/virtualMachineProxy.ts:126-126. In each route’s existing
request-processing flow, emit the appropriate error response from the catch
handler, including the inner chain in ansibletower and preserving route-specific
responses; do not rely on surrounding try/catch blocks to catch detached
promises.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

💡 Need a hand with PR review? Try Gitar by Sonar!

…safety (ACM-38043)

Signed-off-by: Kevin Cormier <kcormier@redhat.com>
@KevinFCormier
KevinFCormier force-pushed the refactor/ACM-38043-body-parser-utility branch from 0077b22 to b3dbb65 Compare July 21, 2026 19:00
Comment thread backend/src/routes/ansibletower.ts Dismissed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
backend/src/lib/body-parser.ts (1)

108-112: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Do not destroy the HTTP/2 stream before returning 413.

Line 111 destroys the underlying Http2Stream, so callers cannot reliably send a response after the overflow rejection. The supplied paginate consumer also currently maps this error to its generic 500 path. Drain/stop buffering without destroying the request, then map RequestBodyTooLargeError to HTTP 413 in every consumer. Node documents that destroying an Http2ServerRequest destroys its underlying stream. (nodejs.org)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/lib/body-parser.ts` around lines 108 - 112, Update the request
handling around the data listener and RequestBodyTooLargeError: stop buffering
and reject on overflow without calling req.destroy(), preserving the stream so a
413 response can be sent. In every consumer, including paginate, explicitly map
RequestBodyTooLargeError to HTTP 413 instead of the generic 500 path.
🧹 Nitpick comments (2)
backend/src/lib/body-parser.ts (1)

103-130: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use descriptive parameter names.

Rename req to request in both exported helpers. As per coding guidelines, “Name functions and variables using descriptive camelCase names and avoid abbreviations.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/lib/body-parser.ts` around lines 103 - 130, Rename the req
parameter to request in both exported helpers, readRequestBody and
parseRequestJsonBody, and update every reference within those functions to use
the new descriptive name without changing behavior.

Source: Coding guidelines

backend/src/routes/ansibletower.ts (1)

37-40: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use descriptive request and error identifiers.

Replace towerReq with towerRequest, omit unused err, and rename e to error.

As per coding guidelines, functions and variables must use descriptive camelCase names and avoid abbreviations.

Also applies to: 59-72

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/routes/ansibletower.ts` around lines 37 - 40, Update the ansible
tower route’s request and error identifiers: rename towerReq to towerRequest,
remove the unused err binding in the URL parsing catch, and rename e to error in
the related error-handling blocks around the tower request flow.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/routes/ansibletower.ts`:
- Line 56: Update the HTTPS request configuration in the Ansible Tower route to
remove the rejectUnauthorized: false override. Configure the AAP CA through the
appropriate trusted certificate setting and keep TLS certificate validation
enabled for the credential-bearing request.
- Around line 33-74: Update the route handler containing readRequestBody to use
the required (req: Http2ServerRequest, res: Http2ServerResponse): Promise<void>
signature, and return the readRequestBody(req).then(...).catch(...) chain
instead of prefixing it with void, so the framework can await all request
processing.

---

Duplicate comments:
In `@backend/src/lib/body-parser.ts`:
- Around line 108-112: Update the request handling around the data listener and
RequestBodyTooLargeError: stop buffering and reject on overflow without calling
req.destroy(), preserving the stream so a 413 response can be sent. In every
consumer, including paginate, explicitly map RequestBodyTooLargeError to HTTP
413 instead of the generic 500 path.

---

Nitpick comments:
In `@backend/src/lib/body-parser.ts`:
- Around line 103-130: Rename the req parameter to request in both exported
helpers, readRequestBody and parseRequestJsonBody, and update every reference
within those functions to use the new descriptive name without changing
behavior.

In `@backend/src/routes/ansibletower.ts`:
- Around line 37-40: Update the ansible tower route’s request and error
identifiers: rename towerReq to towerRequest, remove the unused err binding in
the URL parsing catch, and rename e to error in the related error-handling
blocks around the tower request flow.
🪄 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: Enterprise

Run ID: b4fc0004-e325-4d63-b0b1-8b563c978cc8

📥 Commits

Reviewing files that changed from the base of the PR and between 0077b22 and b3dbb65.

📒 Files selected for processing (10)
  • backend/src/lib/body-parser.ts
  • backend/src/lib/pagination.ts
  • backend/src/routes/aggregators/appSetData.ts
  • backend/src/routes/aggregators/statuses.ts
  • backend/src/routes/ansibletower.ts
  • backend/src/routes/operatorCheck.ts
  • backend/src/routes/rosaWizardApi.ts
  • backend/src/routes/upgrade-risks-prediction.ts
  • backend/src/routes/userpreference.ts
  • backend/src/routes/virtualMachineProxy.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • backend/src/routes/aggregators/appSetData.ts
  • backend/src/routes/aggregators/statuses.ts
  • backend/src/routes/userpreference.ts
  • backend/src/routes/upgrade-risks-prediction.ts
  • backend/src/routes/virtualMachineProxy.ts
  • backend/src/lib/pagination.ts
  • backend/src/routes/rosaWizardApi.ts
  • backend/src/routes/operatorCheck.ts

Comment thread backend/src/routes/ansibletower.ts
Comment thread backend/src/routes/ansibletower.ts
@openshift-ci

openshift-ci Bot commented Jul 21, 2026

Copy link
Copy Markdown

@KevinFCormier: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/unit-tests-sonarcloud b3dbb65 link true /test unit-tests-sonarcloud

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants