-
Notifications
You must be signed in to change notification settings - Fork 29
Feat(VTEX): Recommendations BFF API #1532
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5e5dd88
807adf9
2172269
29beb57
646aaba
ca9d67b
fd5cbcd
2811c9d
25a5da7
9379bd1
30661af
c8840f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import { AppContext } from "../../mod.ts"; | ||
| import { proxySetCookie } from "../../utils/cookies.ts"; | ||
| import { parseCookie } from "../../utils/orderForm.ts"; | ||
| import type { OrderForm } from "../../utils/types.ts"; | ||
|
|
||
| export interface Props { | ||
| appId: string; | ||
| // deno-lint-ignore no-explicit-any | ||
| body: any; | ||
| } | ||
|
|
||
| /** | ||
| * @title Update Custom Data | ||
| * @description Update the custom data in the cart | ||
| */ | ||
| const action = async ( | ||
| props: Props, | ||
| req: Request, | ||
| ctx: AppContext, | ||
| ): Promise<OrderForm> => { | ||
| const { vcsDeprecated } = ctx; | ||
| const { | ||
| appId, | ||
| body, | ||
| } = props; | ||
| const { orderFormId } = parseCookie(req.headers); | ||
|
|
||
| if (!orderFormId || orderFormId === "") { | ||
| throw new Error("Order form ID is required"); | ||
| } | ||
|
|
||
| const cookie = req.headers.get("cookie") ?? ""; | ||
|
|
||
| const response = await vcsDeprecated | ||
| ["PUT /api/checkout/pub/orderForm/:orderFormId/customData/:appId"]({ | ||
| orderFormId, | ||
| appId, | ||
| }, { | ||
| body, | ||
| headers: { | ||
| accept: "application/json", | ||
| "content-type": "application/json", | ||
| cookie, | ||
| }, | ||
| }); | ||
|
|
||
| proxySetCookie(response.headers, ctx.response.headers, req.url); | ||
|
|
||
| return response.json(); | ||
| }; | ||
|
|
||
| export default action; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import type { AppContext } from "../../mod.ts"; | ||
| import { getOrigin, parseCookie } from "../../utils/recommendations.ts"; | ||
| import type { ProductViewSource } from "../../utils/types.ts"; | ||
|
|
||
| interface Props { | ||
| /** | ||
| * @title User ID | ||
| * @description The ID of the user who viewed the recommendation. | ||
| */ | ||
| userId?: string; | ||
| /** | ||
| * @title Product ID | ||
| * @description The ID of the product that was clicked. | ||
| */ | ||
| productId: string; | ||
| /** | ||
| * @title Source | ||
| * @description The source of the product view event. | ||
| */ | ||
| source: ProductViewSource; | ||
| /** | ||
| * @description The origin of the recommendation request. E.g: apiexamples/storefront/vtex.recommendation-shelf@2.x | ||
| */ | ||
| "x-vtex-rec-origin"?: string; | ||
| } | ||
|
|
||
| export default async function action( | ||
| props: Props, | ||
| req: Request, | ||
| ctx: AppContext, | ||
| ) { | ||
| const { bff } = ctx; | ||
|
|
||
| const origin = getOrigin(req, ctx.account, props["x-vtex-rec-origin"]); | ||
| if (!origin) { | ||
| throw new Error("x-vtex-rec-origin header is required"); | ||
| } | ||
|
|
||
| const cookies = parseCookie(req.headers); | ||
| const userId = props.userId ?? cookies?.userId; | ||
|
|
||
| if (!userId) { | ||
| throw new Error("userId is required"); | ||
| } | ||
|
|
||
| await bff["POST /api/recommend-bff/v2/events/product-view"]({ | ||
| an: ctx.account, | ||
| }, { | ||
| body: { | ||
| userId, | ||
| product: props.productId, | ||
| source: props.source, | ||
| }, | ||
| headers: { | ||
| "x-vtex-rec-origin": origin, | ||
| "user-agent": req.headers.get("user-agent") || "", | ||
| cookie: req.headers.get("cookie") || "", | ||
| }, | ||
| }); | ||
|
|
||
| return { ok: true }; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import { AppContext } from "../../mod.ts"; | ||
| import { getOrigin, parseCookie } from "../../utils/recommendations.ts"; | ||
|
|
||
| interface Props { | ||
| /** | ||
| * @title User ID | ||
| * @description The ID of the user who viewed the recommendation. | ||
| */ | ||
| userId?: string; | ||
| /** | ||
| * @title Product ID | ||
| * @description The ID of the product that was clicked. | ||
| */ | ||
| productId: string; | ||
| /** | ||
| * @title Correlation ID | ||
| * @description The correlation ID of the recommendation request. | ||
| */ | ||
| correlationId: string; | ||
| /** | ||
| * @description The origin of the recommendation request. E.g: apiexamples/storefront/vtex.recommendation-shelf@2.x | ||
| */ | ||
| "x-vtex-rec-origin"?: string; | ||
| } | ||
|
|
||
| export default async function loader( | ||
| props: Props, | ||
| req: Request, | ||
| ctx: AppContext, | ||
| ) { | ||
| const { bff } = ctx; | ||
|
|
||
| const origin = getOrigin(req, ctx.account, props["x-vtex-rec-origin"]); | ||
| if (!origin) { | ||
| throw new Error("x-vtex-rec-origin header is required"); | ||
| } | ||
|
|
||
| const cookies = parseCookie(req.headers); | ||
| const userId = props.userId ?? cookies?.userId; | ||
|
|
||
| if (!userId) { | ||
| throw new Error("userId is required"); | ||
| } | ||
|
|
||
| await bff["POST /api/recommend-bff/v2/events/recommendation-click"]({ | ||
| an: ctx.account, | ||
| }, { | ||
| body: { | ||
| correlationId: props.correlationId, | ||
| userId, | ||
| product: props.productId, | ||
| }, | ||
| headers: { | ||
| "x-vtex-rec-origin": origin, | ||
| }, | ||
| }); | ||
|
|
||
| return { ok: true }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import { AppContext } from "../../mod.ts"; | ||
| import { getOrigin, parseCookie } from "../../utils/recommendations.ts"; | ||
|
|
||
| interface Props { | ||
| /** | ||
| * @title User ID | ||
| * @description The ID of the user who viewed the recommendation. | ||
| */ | ||
| userId?: string; | ||
| /** | ||
| * @title Correlation ID | ||
| * @description The correlation ID of the recommendation request. | ||
| */ | ||
| correlationId: string; | ||
| /** | ||
| * @title Products | ||
| * @description The products SKU IDs that were viewed. | ||
| */ | ||
| products: string[]; | ||
| /** | ||
| * @description The origin of the recommendation request. E.g: apiexamples/storefront/vtex.recommendation-shelf@2.x | ||
| */ | ||
| "x-vtex-rec-origin"?: string; | ||
| } | ||
|
|
||
| export default async function loader( | ||
| props: Props, | ||
| req: Request, | ||
| ctx: AppContext, | ||
| ) { | ||
| const { bff } = ctx; | ||
|
|
||
| const origin = getOrigin(req, ctx.account, props["x-vtex-rec-origin"]); | ||
| if (!origin) { | ||
| throw new Error("x-vtex-rec-origin header is required"); | ||
| } | ||
|
|
||
| const cookies = parseCookie(req.headers); | ||
| const userId = props.userId ?? cookies?.userId; | ||
|
|
||
| if (!userId) { | ||
| throw new Error("userId is required"); | ||
| } | ||
|
|
||
| await bff["POST /api/recommend-bff/v2/events/recommendation-view"]({ | ||
| an: ctx.account, | ||
| }, { | ||
| body: { | ||
| correlationId: props.correlationId, | ||
| userId, | ||
| products: props.products, | ||
| }, | ||
| headers: { | ||
| "x-vtex-rec-origin": origin, | ||
| }, | ||
| }); | ||
|
|
||
| return { ok: true }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import type { AppContext } from "../../mod.ts"; | ||
| import { proxySetCookie } from "../../utils/cookies.ts"; | ||
| import { parseCookie as parseOrderformCookie } from "../../utils/orderForm.ts"; | ||
| import { parseCookie as parseRecommendationsCookie } from "../../utils/recommendations.ts"; | ||
|
|
||
| export default async function action( | ||
| _: unknown, | ||
| req: Request, | ||
| ctx: AppContext, | ||
| ) { | ||
| const { bff } = ctx; | ||
| const { orderFormId } = parseOrderformCookie(req.headers); | ||
| const { userId } = parseRecommendationsCookie(req.headers); | ||
|
Comment on lines
+12
to
+13
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Skip the BFF call when
Also applies to: 34-39 🤖 Prompt for AI Agents |
||
| if (userId) { | ||
| return { recommendationsUserId: userId }; | ||
| } | ||
|
|
||
| const url = new URL(req.url); | ||
| const host = url.host; | ||
|
|
||
| const headers = new Headers(); | ||
| headers.set( | ||
| "x-vtex-rec-origin", | ||
| `${ctx.account}/storefront/deco.recommendations@1.x`, | ||
| ); | ||
|
Comment on lines
+21
to
+25
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Respect caller-provided The PR contract says callers can send 🤖 Prompt for AI Agents |
||
| headers.set("x-forwarded-host", host); | ||
| headers.set("host", `${ctx.account}.vtexcommercestable.com.br`); | ||
|
|
||
| const cookie = req.headers.get("cookie"); | ||
| if (cookie) { | ||
| headers.set("cookie", cookie); | ||
| } | ||
|
|
||
| const response = await bff["POST /api/recommend-bff/v2/users/start-session"]({ | ||
| an: ctx.account, | ||
| }, { | ||
| body: { orderFormId }, | ||
| headers, | ||
| }); | ||
|
|
||
| const data = await response.json(); | ||
|
|
||
| proxySetCookie(response.headers, ctx.response.headers, req.url); | ||
|
|
||
| return data; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix product-view prop description typo.
Line 13 says the product was “clicked”, but this loader emits a product-view event.
✏️ Suggested edit
📝 Committable suggestion
🤖 Prompt for AI Agents