diff --git a/lib/config.test.js b/lib/config.test.js index 92be806..5292667 100644 --- a/lib/config.test.js +++ b/lib/config.test.js @@ -3,6 +3,14 @@ import { DCN_DEFAULTS, getConfig } from "./config"; const defaultConsent = DCN_DEFAULTS.consent; describe("getConfig", () => { + beforeEach(() => { + jest.spyOn(crypto, "randomUUID").mockReturnValue("0"); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + it("returns the default config when no overrides are provided", () => { expect(getConfig({ host: "host", site: "site" })).toEqual({ host: "host", @@ -10,6 +18,7 @@ describe("getConfig", () => { cookies: true, initPassport: true, consent: defaultConsent, + sessionID: "0", }); }); @@ -28,6 +37,7 @@ describe("getConfig", () => { cookies: false, initPassport: false, consent: defaultConsent, + sessionID: "0", }); }); diff --git a/lib/config.ts b/lib/config.ts index 240fc73..eded239 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -18,6 +18,7 @@ type InitConfig = { type ResolvedConfig = Required> & { consent: Consent; + sessionID: string; }; const DCN_DEFAULTS = { @@ -39,6 +40,7 @@ function getConfig(init: InitConfig): ResolvedConfig { cookies: init.cookies ?? DCN_DEFAULTS.cookies, initPassport: init.initPassport ?? DCN_DEFAULTS.initPassport, consent: DCN_DEFAULTS.consent, + sessionID: generateSessionID(), }; if (init.consent?.static) { @@ -50,5 +52,9 @@ function getConfig(init: InitConfig): ResolvedConfig { return config; } +function generateSessionID(): string { + return crypto.randomUUID(); +} + export type { InitConsent, CMPApiConfig, InitConfig, ResolvedConfig }; export { getConfig, DCN_DEFAULTS }; diff --git a/lib/core/network.test.js b/lib/core/network.test.js index ef37fcf..cf3c2b2 100644 --- a/lib/core/network.test.js +++ b/lib/core/network.test.js @@ -8,6 +8,7 @@ describe("buildRequest", () => { host: "host", site: "site", consent: { reg: "can", gpp: "gpp", gppSectionIDs: [1, 2] }, + sessionID: "123", }; const req = { method: "GET" }; @@ -20,6 +21,7 @@ describe("buildRequest", () => { expect([...url.searchParams.entries()]).toEqual([ ["query", "string"], ["osdk", `web-${buildInfo.version}`], + ["sid", "123"], ["reg", "can"], ["gpp", "gpp"], ["gpp_sid", "1,2"], diff --git a/lib/core/network.ts b/lib/core/network.ts index 7cd6b2a..c5a127e 100644 --- a/lib/core/network.ts +++ b/lib/core/network.ts @@ -7,6 +7,7 @@ function buildRequest(path: string, config: ResolvedConfig, init?: RequestInit): const url = new URL(`${site}${path}`, `https://${host}`); url.searchParams.set("osdk", `web-${buildInfo.version}`); + url.searchParams.set("sid", config.sessionID); if (config.consent.reg) { url.searchParams.set("reg", config.consent.reg); diff --git a/lib/edge/resolve.test.js b/lib/edge/resolve.test.js index eb4cf36..a873971 100644 --- a/lib/edge/resolve.test.js +++ b/lib/edge/resolve.test.js @@ -3,6 +3,14 @@ import { TEST_HOST, TEST_SITE, TEST_BASE_URL } from "../test/mocks"; import { parseResolveResponse, Resolve } from "./resolve"; describe("resolve", () => { + beforeEach(() => { + jest.spyOn(crypto, "randomUUID").mockReturnValue("0"); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + test("forwards identifier when present", () => { const config = getConfig({ host: TEST_HOST, site: TEST_SITE }); const fetchSpy = jest.spyOn(window, "fetch"); @@ -11,14 +19,14 @@ describe("resolve", () => { expect(fetchSpy).toHaveBeenCalledWith( expect.objectContaining({ method: "GET", - url: `${TEST_BASE_URL}/v1/resolve?id=id&osdk=web-0.0.0-experimental&cookies=yes`, + url: `${TEST_BASE_URL}/v1/resolve?id=id&osdk=web-0.0.0-experimental&sid=0&cookies=yes`, }) ); Resolve(config); expect.objectContaining({ method: "GET", - url: `${TEST_BASE_URL}/v1/resolve?osdk=web-0.0.0-experimental&cookies=yes`, + url: `${TEST_BASE_URL}/v1/resolve?osdk=web-0.0.0-experimental&sid=0&cookies=yes`, }); }); }); diff --git a/lib/sdk.test.ts b/lib/sdk.test.ts index 97fd9c6..af27db9 100644 --- a/lib/sdk.test.ts +++ b/lib/sdk.test.ts @@ -202,10 +202,14 @@ describe("Breaking change detection: if typescript complains or a test fails it' describe("behavior testing of", () => { beforeEach(() => { - localStorage.clear(); + jest.spyOn(crypto, "randomUUID").mockReturnValue("0"); jest.clearAllMocks(); }); + afterEach(() => { + localStorage.clear(); + }); + test("constructor with cookies and initPassport set to false initializes without localStorage", async () => { const fetchSpy = jest.spyOn(window, "fetch"); const sdk = new OptableSDK({ @@ -222,6 +226,7 @@ describe("behavior testing of", () => { site: "site", cookies: false, initPassport: false, + sessionID: "0", }); await sdk["init"]; expect(localStorage.setItem).toBeCalledTimes(0); @@ -232,7 +237,7 @@ describe("behavior testing of", () => { expect.objectContaining({ method: "POST", _bodyText: '["c:a1a335b8216658319f96a4b0c718557ba41dd1f5"]', - url: `${TEST_BASE_URL}/identify?osdk=web-0.0.0-experimental&cookies=no&passport=`, + url: `${TEST_BASE_URL}/identify?osdk=web-0.0.0-experimental&sid=0&cookies=no&passport=`, }) ); @@ -242,7 +247,7 @@ describe("behavior testing of", () => { expect.objectContaining({ method: "POST", _bodyText: '["c:a1a335b8216658319f96a4b0c718557ba41dd1f6"]', - url: `${TEST_BASE_URL}/identify?osdk=web-0.0.0-experimental&cookies=no&passport=PASSPORT`, + url: `${TEST_BASE_URL}/identify?osdk=web-0.0.0-experimental&sid=0&cookies=no&passport=PASSPORT`, }) ); }); @@ -259,6 +264,7 @@ describe("behavior testing of", () => { site: "site", cookies: true, initPassport: true, + sessionID: "0", }); await sdk["init"]; expect(window.localStorage.setItem).toHaveBeenLastCalledWith( @@ -270,7 +276,7 @@ describe("behavior testing of", () => { expect.objectContaining({ method: "GET", bodyUsed: false, - url: expect.stringContaining("config?osdk=web-0.0.0-experimental&cookies=yes"), + url: expect.stringContaining("config?osdk=web-0.0.0-experimental&sid=0&cookies=yes"), }) ); @@ -280,7 +286,7 @@ describe("behavior testing of", () => { expect.objectContaining({ method: "POST", _bodyText: '["c:a1a335b8216658319f96a4b0c718557ba41dd1f5"]', - url: `${TEST_BASE_URL}/identify?osdk=web-0.0.0-experimental&cookies=yes`, + url: `${TEST_BASE_URL}/identify?osdk=web-0.0.0-experimental&sid=0&cookies=yes`, }) ); }); diff --git a/setup-jest.js b/setup-jest.js index ad4b574..f633d7d 100644 --- a/setup-jest.js +++ b/setup-jest.js @@ -2,7 +2,12 @@ import "whatwg-fetch"; import { TextEncoder } from "node:util"; import { TransformStream } from "node:stream/web"; import { BroadcastChannel } from "node:worker_threads"; +import { webcrypto } from 'node:crypto'; global.TextEncoder = TextEncoder; global.TransformStream = TransformStream; global.BroadcastChannel = BroadcastChannel; + +Object.defineProperty(globalThis, 'crypto', { + value: webcrypto, +});