Conversation
Tagging OptionsShould a new tag be published when this PR is merged?
|
📝 WalkthroughWalkthroughThis PR introduces an event-driven recommendation system that integrates with VTEX's Recommendations BFF API. It adds event tracking loaders (product view, recommendation click/view), session management, product recommendation fetchers from multiple sources, utility functions for origin resolution and cookie parsing, OpenAPI specifications for the BFF API, and updates the manifest and module configuration to register new components. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant App as Deco App
participant BFF as Recommendations BFF
Client->>App: Request product list with campaign config
App->>App: Determine origin & segment from request
App->>App: Parse cookies for userId (or start session)
App->>BFF: POST /api/recommend-bff/v2/recommendations<br/>(account, campaign, location, user, products)
BFF-->>App: RecommendationsV2Response
App->>App: Map LegacyProduct[] to Product[]<br/>Enrich with correlationId, campaignId,<br/>campaignTitle, campaignType
App-->>Client: Product[]
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 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. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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.
1 issue found across 14 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="vtex/actions/recommendation/startSession.ts">
<violation number="1" location="vtex/actions/recommendation/startSession.ts:39">
P2: Logging response data and Set-Cookie headers can leak sensitive session/auth information into logs. Remove or sanitize these logs in production.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
|
||
| const data = await response.json(); | ||
|
|
||
| console.log(data, getSetCookies(response.headers)); |
There was a problem hiding this comment.
P2: Logging response data and Set-Cookie headers can leak sensitive session/auth information into logs. Remove or sanitize these logs in production.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At vtex/actions/recommendation/startSession.ts, line 39:
<comment>Logging response data and Set-Cookie headers can leak sensitive session/auth information into logs. Remove or sanitize these logs in production.</comment>
<file context>
@@ -0,0 +1,43 @@
+
+ const data = await response.json();
+
+ console.log(data, getSetCookies(response.headers));
+ proxySetCookie(response.headers, ctx.response.headers, req.url);
+
</file context>
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@vtex/actions/events/productView.ts`:
- Around line 12-14: Update the JSDoc for the "Product ID" prop in the
product-view loader so the description reflects a view event rather than a
click: locate the comment block with `@title` "Product ID" in productView.ts (the
product-view loader) and change the phrase "The ID of the product that was
clicked." to something like "The ID of the product that was viewed." to match
the emitted product-view event.
In `@vtex/actions/recommendation/startSession.ts`:
- Line 39: Remove the sensitive console.log in the startSession action: delete
the line that logs "data" and the result of getSetCookies(response.headers) in
the startSession function, since it exposes session/recommendation identifiers;
if you need runtime visibility, replace it with a non-sensitive log such as
response.status or a sanitized message (e.g., "startSession succeeded") and
never log raw cookies or full response body—avoid calling getSetCookies for
logging or redact its output before any log.
In `@vtex/loaders/recommendations/productList.ts`:
- Around line 106-112: For rec-cross-v2 and rec-similar-v2 ensure a source
product exists before building the products/productIds: check campaignType and
validate props.products?.[0] is present; if missing, bail out (throw, return an
error response, or skip calling the BFF) instead of constructing products =
[props.products?.[0]] which yields undefined and productIds = "". Update the
logic around the products and productIds variables (referencing campaignType,
products, productIds, and props.products) to perform this guard and handle the
missing-product case explicitly.
- Line 123: The headers spread is dropping the segment cookie because
withSegmentCookie(segment) returns a Headers instance which doesn't spread into
a plain object; change the merge to preserve entries by converting the Headers
to a plain object (e.g. Object.fromEntries(withSegmentCookie(segment))) or build
a Headers object and call .set("x-vtex-rec-origin", origin) so the segment
cookie from withSegmentCookie(segment) is retained; update the code that
currently uses headers: { ...withSegmentCookie(segment), "x-vtex-rec-origin":
origin } to one of these approaches (refer to withSegmentCookie and the headers
property in productList.ts).
In `@vtex/utils/openapi/recommendations-bff.openapi.json`:
- Around line 266-269: The documentation for the "userId" query parameter
references the wrong cookie name; update its description to mention the correct
cookie "vtex-rec-user-id" (replace `vtex-recommendations-user` with
`vtex-rec-user-id`) so it matches the session flow and utilities, and scan the
OpenAPI spec for any other occurrences of `vtex-recommendations-user` to replace
them with `vtex-rec-user-id`.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
vtex/actions/events/productView.tsvtex/actions/events/recommendationClick.tsvtex/actions/events/recommendationView.tsvtex/actions/recommendation/startSession.tsvtex/loaders/recommendations/productList.tsvtex/loaders/recommendations/productListFromCart.tsvtex/loaders/recommendations/productListFromPage.tsvtex/manifest.gen.tsvtex/mod.tsvtex/utils/openapi/recommendations-bff.openapi.gen.tsvtex/utils/openapi/recommendations-bff.openapi.jsonvtex/utils/recommendations.tsvtex/utils/transform.tsvtex/utils/types.ts
| * @title Product ID | ||
| * @description The ID of the product that was clicked. | ||
| */ |
There was a problem hiding this comment.
Fix product-view prop description typo.
Line 13 says the product was “clicked”, but this loader emits a product-view event.
✏️ Suggested edit
- * `@description` The ID of the product that was clicked.
+ * `@description` The ID of the product that was viewed.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * @title Product ID | |
| * @description The ID of the product that was clicked. | |
| */ | |
| * `@title` Product ID | |
| * `@description` The ID of the product that was viewed. | |
| */ |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@vtex/actions/events/productView.ts` around lines 12 - 14, Update the JSDoc
for the "Product ID" prop in the product-view loader so the description reflects
a view event rather than a click: locate the comment block with `@title` "Product
ID" in productView.ts (the product-view loader) and change the phrase "The ID of
the product that was clicked." to something like "The ID of the product that was
viewed." to match the emitted product-view event.
|
|
||
| const data = await response.json(); | ||
|
|
||
| console.log(data, getSetCookies(response.headers)); |
There was a problem hiding this comment.
Remove sensitive logging from start-session action.
Line 39 logs response data and Set-Cookie values, which can expose recommendation/session identifiers in logs.
🔐 Proposed fix
- console.log(data, getSetCookies(response.headers));
proxySetCookie(response.headers, ctx.response.headers, req.url);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| console.log(data, getSetCookies(response.headers)); | |
| proxySetCookie(response.headers, ctx.response.headers, req.url); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@vtex/actions/recommendation/startSession.ts` at line 39, Remove the sensitive
console.log in the startSession action: delete the line that logs "data" and the
result of getSetCookies(response.headers) in the startSession function, since it
exposes session/recommendation identifiers; if you need runtime visibility,
replace it with a non-sensitive log such as response.status or a sanitized
message (e.g., "startSession succeeded") and never log raw cookies or full
response body—avoid calling getSetCookies for logging or redact its output
before any log.
| const products = | ||
| campaignType === "rec-cross-v2" || campaignType === "rec-similar-v2" | ||
| ? [props.products?.[0]] | ||
| : props.products; | ||
|
|
||
| const productIds = products?.filter(Boolean).join(","); | ||
|
|
There was a problem hiding this comment.
Validate required product context for cross/similar campaigns.
Lines 106-112 allow missing product IDs for rec-cross-v2 / rec-similar-v2, which can send an empty products parameter and cause avoidable BFF failures.
🛡️ Suggested fix
- const products =
- campaignType === "rec-cross-v2" || campaignType === "rec-similar-v2"
- ? [props.products?.[0]]
- : props.products;
+ const requiresSingleProduct =
+ campaignType === "rec-cross-v2" || campaignType === "rec-similar-v2";
+
+ if (requiresSingleProduct && !props.products?.[0]) {
+ throw new Error(
+ `campaignType "${props.campaignType}" requires one product ID`,
+ );
+ }
+
+ const products = requiresSingleProduct
+ ? [props.products![0]]
+ : props.products;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const products = | |
| campaignType === "rec-cross-v2" || campaignType === "rec-similar-v2" | |
| ? [props.products?.[0]] | |
| : props.products; | |
| const productIds = products?.filter(Boolean).join(","); | |
| const requiresSingleProduct = | |
| campaignType === "rec-cross-v2" || campaignType === "rec-similar-v2"; | |
| if (requiresSingleProduct && !props.products?.[0]) { | |
| throw new Error( | |
| `campaignType "${props.campaignType}" requires one product ID`, | |
| ); | |
| } | |
| const products = requiresSingleProduct | |
| ? [props.products![0]] | |
| : props.products; | |
| const productIds = products?.filter(Boolean).join(","); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@vtex/loaders/recommendations/productList.ts` around lines 106 - 112, For
rec-cross-v2 and rec-similar-v2 ensure a source product exists before building
the products/productIds: check campaignType and validate props.products?.[0] is
present; if missing, bail out (throw, return an error response, or skip calling
the BFF) instead of constructing products = [props.products?.[0]] which yields
undefined and productIds = "". Update the logic around the products and
productIds variables (referencing campaignType, products, productIds, and
props.products) to perform this guard and handle the missing-product case
explicitly.
| products: productIds, | ||
| salesChannel: segment?.payload?.channel || ctx.salesChannel, | ||
| }, { | ||
| headers: { ...withSegmentCookie(segment), "x-vtex-rec-origin": origin }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check vtex/utils/segment.ts for withSegmentCookie definition and Headers usage
echo "=== Checking vtex/utils/segment.ts ==="
rg -n -C3 'export const withSegmentCookie|new Headers\(|return h' vtex/utils/segment.ts
echo ""
echo "=== Checking vtex/loaders/recommendations/productList.ts line 123 ==="
rg -n -C3 '\.\.\.withSegmentCookie\(segment\)' vtex/loaders/recommendations/productList.tsRepository: deco-cx/apps
Length of output: 858
Segment cookie is dropped when spreading Headers into a plain object.
Line 123 spreads withSegmentCookie(segment) (which returns a Headers instance) into a plain object literal. A Headers object doesn't preserve its entries when spread into {} because it's a Web API object with custom iteration semantics, not a plain object with enumerable properties. This silently drops the segment cookie and breaks segment-aware recommendations.
Fix
- const response = await bff["GET /api/recommend-bff/v2/recommendations"]({
+ const response = await bff["GET /api/recommend-bff/v2/recommendations"]({
an: account,
campaignVrn,
pickupPoint: props.pickupPoint,
regionId: props.regionId,
zipCode: props.zipCode,
userId,
products: productIds,
salesChannel: segment?.payload?.channel || ctx.salesChannel,
}, {
- headers: { ...withSegmentCookie(segment), "x-vtex-rec-origin": origin },
+ headers: withSegmentCookie(
+ segment,
+ new Headers({ "x-vtex-rec-origin": origin }),
+ ),
}).then((res) => res.json());🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@vtex/loaders/recommendations/productList.ts` at line 123, The headers spread
is dropping the segment cookie because withSegmentCookie(segment) returns a
Headers instance which doesn't spread into a plain object; change the merge to
preserve entries by converting the Headers to a plain object (e.g.
Object.fromEntries(withSegmentCookie(segment))) or build a Headers object and
call .set("x-vtex-rec-origin", origin) so the segment cookie from
withSegmentCookie(segment) is retained; update the code that currently uses
headers: { ...withSegmentCookie(segment), "x-vtex-rec-origin": origin } to one
of these approaches (refer to withSegmentCookie and the headers property in
productList.ts).
| "name": "userId", | ||
| "in": "query", | ||
| "description": "User ID, required for personalized recommendations based on user behavior. It can be found in the `vtex-recommendations-user` cookie.", | ||
| "required": false, |
There was a problem hiding this comment.
Fix inconsistent cookie name in userId parameter docs.
Line 268 references vtex-recommendations-user, but the session flow and utilities use vtex-rec-user-id. This inconsistency can cause integration mistakes.
🛠️ Proposed fix
- "description": "User ID, required for personalized recommendations based on user behavior. It can be found in the `vtex-recommendations-user` cookie.",
+ "description": "User ID, required for personalized recommendations based on user behavior. It can be found in the `vtex-rec-user-id` cookie.",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "name": "userId", | |
| "in": "query", | |
| "description": "User ID, required for personalized recommendations based on user behavior. It can be found in the `vtex-recommendations-user` cookie.", | |
| "required": false, | |
| "name": "userId", | |
| "in": "query", | |
| "description": "User ID, required for personalized recommendations based on user behavior. It can be found in the `vtex-rec-user-id` cookie.", | |
| "required": false, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@vtex/utils/openapi/recommendations-bff.openapi.json` around lines 266 - 269,
The documentation for the "userId" query parameter references the wrong cookie
name; update its description to mention the correct cookie "vtex-rec-user-id"
(replace `vtex-recommendations-user` with `vtex-rec-user-id`) so it matches the
session flow and utilities, and scan the OpenAPI spec for any other occurrences
of `vtex-recommendations-user` to replace them with `vtex-rec-user-id`.
What is this Contribution About?
Implementation of VTEX Recommendations BFF API
Summary by cubic
Adds VTEX Recommendations BFF integration to fetch product recommendations and track product/recommendation events. Includes session bootstrap and enriches products with correlation and campaign metadata.
New Features
Migration
Written for commit fd5cbcd. Summary will update on new commits.
Summary by CodeRabbit
Release Notes