Skip to content

Commit 35046f4

Browse files
feat(vtex): add support for preserving UTM characters in segmentBag
1 parent 59e1533 commit 35046f4

5 files changed

Lines changed: 67 additions & 54 deletions

File tree

vtex/actions/session/createSession.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,31 @@ async function action(
2121
const setCookiesSoFar = getSetCookies(ctx.response.headers);
2222
const { header: cookie } = buildCookieJar(req.headers, setCookiesSoFar);
2323

24-
const response = await vcs["POST /api/sessions"]({
25-
items: items.join(","),
26-
}, {
27-
body: {
28-
public: {
29-
...props.publicProperties,
24+
const url = new URL(req.url);
25+
const searchParams = new URLSearchParams(url.search);
26+
searchParams.set("items", items.join(","));
27+
28+
const response = await vcs["POST /api/sessions"](
29+
Object.fromEntries(searchParams.entries()),
30+
{
31+
body: {
32+
public: {
33+
...props.publicProperties,
34+
},
3035
},
36+
headers: { cookie },
3137
},
32-
headers: { cookie },
33-
});
38+
);
3439

3540
if (!response.ok) {
3641
throw new Error(`Failed to create session: ${response.status}`);
3742
}
3843

39-
setCookiesFromSession(response.headers, ctx.response.headers, req.url);
44+
setCookiesFromSession({
45+
from: response.headers,
46+
req,
47+
ctx,
48+
});
4049

4150
return await response.json() as GetSessionResponse;
4251
}

vtex/actions/session/editSession.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ async function action(
4141
throw new Error(`Failed to edit session: ${response.status}`);
4242
}
4343

44-
setCookiesFromSession(response.headers, ctx.response.headers, req.url);
44+
setCookiesFromSession({
45+
from: response.headers,
46+
req,
47+
ctx,
48+
});
4549

4650
return (await response.json()) as GetSessionResponse;
4751
}

vtex/mod.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,18 @@ export interface Props {
7676
*/
7777
setRefreshToken?: boolean;
7878
defaultSegment?: SegmentCulture;
79+
/**
80+
* @description Preserve UTM characters on cookies.
81+
* @default false
82+
*/
83+
preserveUtmChars?: boolean;
7984
usePortalSitemap?: boolean;
8085
/**
8186
* @description Use VTEX as backend platform
8287
* @default vtex
8388
* @hide true
8489
*/
8590
platform: "vtex";
86-
8791
advancedConfigs?: {
8892
doNotFetchVariantsForRelatedProducts?: boolean;
8993
};

vtex/utils/cookies.ts

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
getSetCookies,
55
setCookie,
66
} from "std/http/cookie.ts";
7-
import { SEGMENT_COOKIE_NAME } from "./segment.ts";
7+
import { SEGMENT_COOKIE_NAME, setSegmentBag } from "./segment.ts";
8+
import type { AppContext } from "../mod.ts";
89

910
export const stringify = (cookies: Record<string, string>) =>
1011
Object.entries(cookies)
@@ -30,28 +31,29 @@ export const proxySetCookie = (
3031
}
3132
};
3233

33-
export const setCookiesFromSession = (
34-
from: Headers,
35-
to: Headers,
36-
domain: URL | string,
37-
) => {
38-
const newDomain = new URL(domain);
34+
export const setCookiesFromSession = ({
35+
from,
36+
req,
37+
ctx,
38+
}: {
39+
from: Headers;
40+
req: Request;
41+
ctx: AppContext;
42+
}) => {
43+
const newDomain = new URL(req.url);
3944

4045
for (const cookie of getSetCookies(from)) {
41-
const newCookie = cookie.name === SEGMENT_COOKIE_NAME
42-
? {
43-
value: cookie.value,
44-
name: cookie.name,
45-
path: "/",
46-
secure: true,
47-
httpOnly: true,
48-
}
49-
: {
50-
...cookie,
51-
domain: newDomain.hostname,
52-
};
46+
if (cookie.name === SEGMENT_COOKIE_NAME) {
47+
const { cookies } = buildCookieJar(from, getSetCookies(from));
48+
setSegmentBag(cookies, req, ctx);
49+
continue;
50+
}
51+
const newCookie = {
52+
...cookie,
53+
domain: newDomain.hostname,
54+
};
5355

54-
setCookie(to, newCookie);
56+
setCookie(ctx.response.headers, newCookie);
5557
}
5658
};
5759

@@ -61,7 +63,7 @@ export const setCookiesFromSession = (
6163
*/
6264
export interface CookieJarResult {
6365
header: string;
64-
record: Record<string, string>;
66+
cookies: Record<string, string>;
6567
detailed: Cookie[];
6668
}
6769

@@ -85,12 +87,8 @@ export function buildCookieJar(
8587
const cookies = Array.from(jar.values());
8688

8789
return {
88-
header: cookies
89-
.map((c) => `${c.name}=${c.value}`)
90-
.join("; "),
91-
record: Object.fromEntries(
92-
cookies.map((c) => [c.name, c.value]),
93-
),
90+
header: cookies.map((c) => `${c.name}=${c.value}`).join("; "),
91+
cookies: Object.fromEntries(cookies.map((c) => [c.name, c.value])),
9492
detailed: cookies,
9593
};
9694
}

vtex/utils/segment.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,21 @@ const serialize = ({
9090
countryCode,
9191
cultureInfo,
9292
channelPrivacy,
93-
}: Partial<Segment>) => {
93+
}: Partial<Segment>, preserveUtmChars = false) => {
94+
const normalize = (value: string | null | undefined) =>
95+
value && !preserveUtmChars ? removeNonLatin1Chars(value) : value;
96+
9497
const seg = {
9598
campaigns,
9699
channel,
97100
priceTables,
98101
regionId,
99-
utm_campaign: utm_campaign &&
100-
removeNonLatin1Chars(utm_campaign).replace(/[\/\[\]{}()<>.]/g, ""),
101-
utm_source: utm_source &&
102-
removeNonLatin1Chars(utm_source).replace(/[\/\[\]{}()<>.]/g, ""),
103-
utm_medium: utm_medium &&
104-
removeNonLatin1Chars(utm_medium).replace(/[\/\[\]{}()<>.]/g, ""),
105-
utmi_campaign: utmi_campaign && removeNonLatin1Chars(utmi_campaign),
106-
utmi_page: utmi_page && removeNonLatin1Chars(utmi_page),
107-
utmi_part: utmi_part && removeNonLatin1Chars(utmi_part),
102+
utm_campaign: normalize(utm_campaign)?.replace(/[\/\[\]{}()<>.]/g, ""),
103+
utm_source: normalize(utm_source)?.replace(/[\/\[\]{}()<>.]/g, ""),
104+
utm_medium: normalize(utm_medium)?.replace(/[\/\[\]{}()<>.]/g, ""),
105+
utmi_campaign: normalize(utmi_campaign),
106+
utmi_page: normalize(utmi_page),
107+
utmi_part: normalize(utmi_part),
108108
currencyCode,
109109
currencySymbol,
110110
countryCode,
@@ -168,10 +168,9 @@ export const setSegmentBag = (
168168
const segmentFromCookie = vtex_segment ? parse(vtex_segment) : null;
169169

170170
const segmentFromSalesChannelCookie = cookies[SALES_CHANNEL_COOKIE]
171-
? {
172-
channel: cookies[SALES_CHANNEL_COOKIE]?.split("=")[1],
173-
}
171+
? { channel: cookies[SALES_CHANNEL_COOKIE]?.split("=")[1] }
174172
: {};
173+
175174
const segmentFromRequest = buildSegmentFromRequest(req);
176175

177176
const segment = {
@@ -182,10 +181,10 @@ export const setSegmentBag = (
182181
...segmentFromSalesChannelCookie,
183182
...segmentFromRequest,
184183
};
185-
const token = serialize(segment);
184+
185+
const token = serialize(segment, ctx.preserveUtmChars);
186186
setSegmentInBag(ctx, { payload: segment, token });
187187

188-
// If the user came from a sales channel in the URL, we set the cookie
189188
if (segmentFromRequest.channel) {
190189
setCookie(ctx.response.headers, {
191190
value: `sc=${segmentFromRequest.channel}`,
@@ -195,7 +194,6 @@ export const setSegmentBag = (
195194
});
196195
}
197196

198-
// Avoid setting cookie when segment from request matches the one generated
199197
if (vtex_segment !== token) {
200198
setCookie(ctx.response.headers, {
201199
value: token,

0 commit comments

Comments
 (0)