Skip to content

Commit

Permalink
Passthrough gpp alongside gpp_sid when available
Browse files Browse the repository at this point in the history
  • Loading branch information
zapo committed Dec 18, 2024
1 parent 056e7a2 commit 890ebb6
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 83 deletions.
19 changes: 14 additions & 5 deletions lib/core/network.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,28 @@ import { buildRequest } from "./network";
import { default as buildInfo } from "../build.json";

describe("buildRequest", () => {
test("preserves path query string", () => {
it("preserves path query string", () => {
const dcn = {
cookies: true,
host: "host",
site: "site",
consent: { reg: "can", gpp: "gpp" },
consent: { reg: "can", gpp: "gpp", gppSectionIDs: [1, 2] },
};

const req = { method: "GET" };
const request = buildRequest("/path?query=string", dcn, req);

expect(request.url).toBe(
`https://host/site/path?query=string&osdk=web-${buildInfo.version}&reg=can&gpp=gpp&cookies=yes`
);
const url = new URL(request.url);
expect(url.host).toBe("host");
expect(url.protocol).toBe("https:");
expect(url.pathname).toBe("/site/path");
expect([...url.searchParams.entries()]).toEqual([
["query", "string"],
["osdk", `web-${buildInfo.version}`],
["reg", "can"],
["gpp", "gpp"],
["gpp_sid", "1,2"],
["cookies", "yes"],
]);
});
});
4 changes: 4 additions & 0 deletions lib/core/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ function buildRequest(path: string, config: ResolvedConfig, init?: RequestInit):
url.searchParams.set("gpp", config.consent.gpp);
}

if (config.consent.gppSectionIDs) {
url.searchParams.set("gpp_sid", config.consent.gppSectionIDs.join(","));
}

if (config.consent.tcf) {
url.searchParams.set("tcf", config.consent.tcf);
}
Expand Down
152 changes: 90 additions & 62 deletions lib/core/regs/consent.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { getConsent } from "./consent";
import { SectionID as TCFEuV2SectionID, APIPrefix as TCFEuV2APIPrefix } from "./gpp/tcfeuv2";
import { SectionID as TCFCaV1SectionID, APIPrefix as TCFCaV1APIPrefix } from "./gpp/tcfcav1";
import * as gpp from "./gpp";

describe("getConsent", () => {
let windowSpy;
Expand Down Expand Up @@ -61,22 +60,7 @@ describe("getConsent", () => {
});

it("updates consent based on tcf signals for gdpr", () => {
let listener;
const tcf = jest.fn((_command, _version, cb) => {
listener = cb;
});
windowSpy.mockImplementation(() => ({ __tcfapi: tcf }));

const signal = (overrides) => {
listener(
{
gdprApplies: true,
eventStatus: "tcloaded",
...overrides,
},
true
);
};
const signal = mockTCFSignal(windowSpy);

const consent = getConsent("gdpr");
// By default device access is denied
Expand Down Expand Up @@ -114,129 +98,173 @@ describe("getConsent", () => {
});

it("updates consent based on gpp signals for gdpr", () => {
let listener;
const gpp = jest.fn((_command, cb) => {
listener = cb;
});
windowSpy.mockImplementation(() => ({ __gpp: gpp }));
const signal = mockGPPSignal(windowSpy);

const consent = getConsent("gdpr");
// By default device access is denied
expect(consent.deviceAccess).toBe(false);
expect(consent.reg).toBe("gdpr");

const signal = (pingData) => {
listener(
{
data: "ready",
eventName: "signalStatus",
pingData,
},
true
);
};

// Simulate gpp ready event indicating no applicable sections
signal({ applicableSections: [-1], gppString: "ignored" });
expect(consent.deviceAccess).toBe(false);
expect(consent.gpp).toBeUndefined();

// Section tcfeuv2 applies but nothing in parsed sections
signal({ parsedSections: {}, applicableSections: [TCFEuV2SectionID], gppString: "noparsedsections" });
signal({ parsedSections: {}, applicableSections: [gpp.tcfeuv2.SectionID], gppString: "noparsedsections" });
expect(consent.deviceAccess).toBe(false);
expect(consent.gpp).toBe("noparsedsections");
expect(consent.gppSectionIDs).toEqual([gpp.tcfeuv2.SectionID]);

// Section tcfeuv2 applies but no publisher segment
signal({
parsedSections: { [TCFEuV2APIPrefix]: [] },
applicableSections: [TCFEuV2SectionID],
parsedSections: { [gpp.tcfeuv2.APIPrefix]: [] },
applicableSections: [gpp.tcfeuv2.SectionID],
gppString: "nopublishersegment",
});
expect(consent.deviceAccess).toBe(false);
expect(consent.gpp).toBe("nopublishersegment");
expect(consent.gppSectionIDs).toEqual([gpp.tcfeuv2.SectionID]);

// Section tcfeuv2 applies but no purpose 1 consent in publisher segment
signal({
parsedSections: {
[TCFEuV2APIPrefix]: [
[gpp.tcfeuv2.APIPrefix]: [
{
SegmentType: 3,
PubPurposesConsent: [],
},
],
},
applicableSections: [TCFEuV2SectionID],
applicableSections: [gpp.tcfeuv2.SectionID],
gppString: "nopurpose1consent",
});
expect(consent.deviceAccess).toBe(false);
expect(consent.gpp).toBe("nopurpose1consent");
expect(consent.gppSectionIDs).toEqual([gpp.tcfeuv2.SectionID]);

// Section tcfeuv2 applies and purpose 1 granted to publisher
signal({
parsedSections: {
[TCFEuV2APIPrefix]: [
[gpp.tcfeuv2.APIPrefix]: [
{
SegmentType: 3,
PubPurposesConsent: [1],
},
],
},
applicableSections: [TCFEuV2SectionID],
applicableSections: [gpp.tcfeuv2.SectionID],
gppString: "purpose1",
});
expect(consent.deviceAccess).toBe(true);
expect(consent.gpp).toBe("purpose1");
expect(consent.gppSectionIDs).toEqual([gpp.tcfeuv2.SectionID]);

// Section tcfeuv2 applies and purpose 1 revoked
// to publisher
signal({
parsedSections: {
[TCFEuV2APIPrefix]: [
[gpp.tcfeuv2.APIPrefix]: [
{
SegmentType: 3,
PubPurposesConsent: [],
},
],
},
applicableSections: [TCFEuV2SectionID],
applicableSections: [gpp.tcfeuv2.SectionID],
gppString: "revoked",
});
expect(consent.deviceAccess).toBe(false);
expect(consent.gpp).toBe("revoked");
expect(consent.gppSectionIDs).toEqual([gpp.tcfeuv2.SectionID]);
});

it("updates consent based on gpp signals for can", () => {
let listener;
const gpp = jest.fn((_command, cb) => {
listener = cb;
});
windowSpy.mockImplementation(() => ({ __gpp: gpp }));
const signal = mockGPPSignal(windowSpy);

const consent = getConsent("can");
// Device access is always granted
expect(consent.deviceAccess).toBe(true);
expect(consent.reg).toBe("can");

const signal = (pingData) => {
listener(
{
data: "ready",
eventName: "signalStatus",
pingData,
},
true
);
};
// Simulate gpp ready event indicating no applicable sections
signal({ applicableSections: [-1], gppString: "ignored" });
expect(consent.deviceAccess).toBe(true);
expect(consent.gpp).toBeUndefined();
expect(consent.gppSectionIDs).toBeUndefined();

// It listens to can section changes and propagates gpp and gpp sid
signal({ parsedSections: {}, applicableSections: [gpp.tcfcav1.SectionID], gppString: "can" });
expect(consent.deviceAccess).toBe(true);
expect(consent.gpp).toBe("can");
expect(consent.gppSectionIDs).toEqual([gpp.tcfcav1.SectionID]);
});

it("updates consent based on gpp signals for us", () => {
const signal = mockGPPSignal(windowSpy);

const consent = getConsent("us");
// Device access is always granted
expect(consent.deviceAccess).toBe(true);
expect(consent.reg).toBe("us");

// Simulate gpp ready event indicating no applicable sections
signal({ applicableSections: [-1], gppString: "ignored" });
expect(consent.deviceAccess).toBe(true);
expect(consent.gpp).toBeUndefined();
expect(consent.gppSectionIDs).toBeUndefined();

// Section tcfcav1 applies but nothing in parsed sections doesn't impact
// device access
signal({ parsedSections: {}, applicableSections: [TCFCaV1SectionID], gppString: "noparsedsections" });
// It listens to us sections changes and propagates gpp and gpp sid
signal({ parsedSections: {}, applicableSections: [gpp.usnat.SectionID], gppString: "usnat" });
expect(consent.deviceAccess).toBe(true);
expect(consent.gpp).toBe("noparsedsections");
expect(consent.gpp).toBe("usnat");
expect(consent.gppSectionIDs).toEqual([gpp.usnat.SectionID]);

signal({
parsedSections: {},
applicableSections: [gpp.usnat.SectionID, gpp.usca.SectionID],
gppString: "usnat_and_usca",
});
expect(consent.deviceAccess).toBe(true);
expect(consent.gpp).toBe("usnat_and_usca");
expect(consent.gppSectionIDs).toEqual([gpp.usnat.SectionID, gpp.usca.SectionID]);
});
});

function mockGPPSignal(windowSpy) {
let listener;
const gpp = jest.fn((_command, cb) => {
listener = cb;
});
windowSpy.mockImplementation(() => ({ __gpp: gpp }));

return (pingData) => {
listener(
{
data: "ready",
eventName: "signalStatus",
pingData,
},
true
);
};
}

function mockTCFSignal(windowSpy) {
let listener;
const tcf = jest.fn((_command, _version, cb) => {
listener = cb;
});
windowSpy.mockImplementation(() => ({ __tcfapi: tcf }));

return (overrides) => {
listener(
{
gdprApplies: true,
eventStatus: "tcloaded",
...overrides,
},
true
);
};
}
Loading

0 comments on commit 890ebb6

Please sign in to comment.