Skip to content
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

PBID-91: Store third-party cookie support state in compound cookie and expire after ttl #18

2 changes: 1 addition & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ gulp.task('build-bundle-dev', gulp.series(makeDevpackPkg, gulpBundle.bind(null,
gulp.task('build-bundle-prod', gulp.series(makeWebpackPkg, gulpBundle.bind(null, false)));

// public tasks (dependencies are needed for each task since they can be ran on their own)
gulp.task('test', gulp.series(clean, lint, test));
victorigualada marked this conversation as resolved.
Show resolved Hide resolved
gulp.task('test', gulp.series(clean, test));

gulp.task('test-coverage', gulp.series(clean, testCoverage));
gulp.task(viewCoverage);
Expand Down
29 changes: 25 additions & 4 deletions modules/parrableIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,13 @@ function deserializeParrableId(parrableIdStr) {

values.forEach(function(value) {
const pair = value.split(':');
// unpack a value of 1 as true
parrableId[pair[0]] = +pair[1] === 1 ? true : pair[1];
if (+pair[1] === 1 || (pair[1] !== null && +pair[1] === 0)) { // unpack a value of 0 or 1 as boolean
parrableId[pair[0]] = Boolean(+pair[1]);
} else if (!isNaN(pair[1])) { // convert to number if is a number
parrableId[pair[0]] = +pair[1]
} else {
parrableId[pair[0]] = pair[1]
}
});

return parrableId;
Expand All @@ -53,6 +58,12 @@ function serializeParrableId(parrableId) {
if (parrableId.ccpaOptout) {
components.push('ccpaOptout:1');
}
if (parrableId.tpcSupport !== undefined) {
const tpcSupportComponent = parrableId.tpcSupport === true ? 'tpcSupport:1' : 'tpcSupport:0';
victorigualada marked this conversation as resolved.
Show resolved Hide resolved
const tpcUntil = `tpcUntil:${parrableId.tpcUntil}`;
components.push(tpcSupportComponent);
components.push(tpcUntil);
}

return components.join(',');
}
Expand Down Expand Up @@ -190,10 +201,12 @@ function fetchId(configParams, gdprConsentData) {

const eid = (parrableId) ? parrableId.eid : null;
const refererInfo = getRefererInfo();
const tpcSupport = (parrableId) ? parrableId.tpcSupport : null
const tpcUntil = (parrableId) ? +parrableId.tpcUntil : null
const uspString = uspDataHandler.getConsentData();
const gdprApplies = (gdprConsentData && typeof gdprConsentData.gdprApplies === 'boolean' && gdprConsentData.gdprApplies);
const gdprConsentString = (gdprConsentData && gdprApplies && gdprConsentData.consentString) || '';
const partners = configParams.partners || configParams.partner
const partners = configParams.partners || configParams.partner;
const trackers = typeof partners === 'string'
? partners.split(',')
: partners;
Expand All @@ -203,9 +216,13 @@ function fetchId(configParams, gdprConsentData) {
trackers,
url: refererInfo.referer,
prebidVersion: '$prebid.version$',
isIframe: utils.inIframe()
isIframe: utils.inIframe(),
};

if (Date.now() < tpcUntil) {
data.tpcSupport = tpcSupport;
}
victorigualada marked this conversation as resolved.
Show resolved Hide resolved

const searchParams = {
data: encodeBase64UrlSafe(btoa(JSON.stringify(data))),
gdpr: gdprApplies ? 1 : 0,
Expand Down Expand Up @@ -242,6 +259,10 @@ function fetchId(configParams, gdprConsentData) {
if (responseObj.ibaOptout === true) {
newParrableId.ibaOptout = true;
}
if (responseObj.tpcSupport !== undefined) {
newParrableId.tpcSupport = responseObj.tpcSupport;
newParrableId.tpcUntil = Date.now() + responseObj.tpcSupportTtl;
victorigualada marked this conversation as resolved.
Show resolved Hide resolved
}
}
} catch (error) {
utils.logError(error);
Expand Down
138 changes: 135 additions & 3 deletions test/spec/modules/parrableIdSystem_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const P_CONFIG_MOCK = {
partners: 'parrable_test_partner_123,parrable_test_partner_456'
}
};
const RESPONSE_HEADERS = { 'Content-Type': 'application/json' };

function getConfigMock() {
return {
Expand Down Expand Up @@ -57,6 +58,11 @@ function serializeParrableId(parrableId) {
if (parrableId.ccpaOptout) {
str += ',ccpaOptout:1';
}
if (parrableId.tpcSupport !== undefined) {
const tpcSupportComponent = parrableId.tpcSupport === true ? 'tpcSupport:1' : 'tpcSupport:0';
str += `,${tpcSupportComponent}`;
str += `,tpcUntil:${parrableId.tpcUntil}`;
}
return str;
}

Expand All @@ -83,7 +89,7 @@ function decodeBase64UrlSafe(encBase64) {
return encBase64.replace(/[-_.]/g, (m) => DEC[m]);
}

describe('Parrable ID System', function() {
describe.only('Parrable ID System', function() {
victorigualada marked this conversation as resolved.
Show resolved Hide resolved
describe('parrableIdSystem.getId()', function() {
describe('response callback function', function() {
let logErrorStub;
Expand Down Expand Up @@ -125,7 +131,6 @@ describe('Parrable ID System', function() {
{ 'Content-Type': 'text/plain' },
JSON.stringify({ eid: P_XHR_EID })
);

expect(callbackSpy.lastCall.lastArg).to.deep.equal({
eid: P_XHR_EID
});
Expand Down Expand Up @@ -242,6 +247,133 @@ describe('Parrable ID System', function() {
})
});
});

describe('third party cookie support', function () {
victorigualada marked this conversation as resolved.
Show resolved Hide resolved
let logErrorStub;
let callbackSpy = sinon.spy();

beforeEach(function() {
logErrorStub = sinon.stub(utils, 'logError');
});

afterEach(function () {
callbackSpy.resetHistory();
removeParrableCookie();
});

afterEach(function() {
logErrorStub.restore();
});

describe('when getting tpcSupport from XHR response', function () {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a parallel block

describe('when reading tpcSupport status from cookie');

and it tests sending the tpc support to the backend, and then when the tpcUntil expires it should test suppressing the status from the cookie.

let request;
let dateNowStub;
const dateNowMock = 1;
victorigualada marked this conversation as resolved.
Show resolved Hide resolved
const tpcSupportTtl = 1;

before(() => {
dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock);
});

after(() => {
dateNowStub.restore();
});

it('should pass tpcSupport: true to the callback', function () {
let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK);
callback(callbackSpy);
request = server.requests[0];

request.respond(
200,
RESPONSE_HEADERS,
JSON.stringify({ eid: P_XHR_EID, tpcSupport: true, tpcSupportTtl })
);

expect(callbackSpy.lastCall.args[0]).to.deep.equal({
eid: P_XHR_EID,
tpcSupport: true,
tpcUntil: dateNowMock + tpcSupportTtl
victorigualada marked this conversation as resolved.
Show resolved Hide resolved
});
});

it('should pass tpcSupport: false to the callback', function () {
let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK);
callback(callbackSpy);
request = server.requests[0];
request.respond(
200,
RESPONSE_HEADERS,
JSON.stringify({ eid: P_XHR_EID, tpcSupport: false, tpcSupportTtl })
);

expect(callbackSpy.lastCall.args[0]).to.deep.equal({
eid: P_XHR_EID,
tpcSupport: false,
tpcUntil: dateNowMock + tpcSupportTtl
});
});

it('should not pass tpcSupport to the callback', function () {
let { callback } = parrableIdSubmodule.getId(P_CONFIG_MOCK);
callback(callbackSpy);
request = server.requests[0];

request.respond(
200,
RESPONSE_HEADERS,
JSON.stringify({ eid: P_XHR_EID })
);

expect(callbackSpy.lastCall.args[0]).to.deep.equal({
eid: P_XHR_EID
});
});
});

describe('when getting tpcSupport from compound cookie', function () {
let dateNowStub;
const dateNowMock = Date.now();
const tpcSupportTtl = 1;

before(() => {
dateNowStub = sinon.stub(Date, 'now').returns(dateNowMock);
});

after(() => {
dateNowStub.restore();
});

it('should send tpcSupport: true', function () {
writeParrableCookie({ eid: P_COOKIE_EID, tpcSupport: true, tpcUntil: dateNowMock + tpcSupportTtl });
const getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK);

expect(getIdResult.id).to.deep.equal({
eid: P_COOKIE_EID,
tpcSupport: true,
tpcUntil: dateNowMock + tpcSupportTtl
});
});

it('should send tpcSupport: false in the xhr', function () {
writeParrableCookie({ eid: P_COOKIE_EID, tpcSupport: false, tpcUntil: dateNowMock + tpcSupportTtl });
const getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK);

expect(getIdResult.id).to.deep.equal({
eid: P_COOKIE_EID,
tpcSupport: false,
tpcUntil: dateNowMock + tpcSupportTtl
});
});

it('should not send tpcSupport in the xhr when not found', function () {
writeParrableCookie({ eid: P_COOKIE_EID });
const getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK);

expect(getIdResult.id).to.not.have.property('tpcSupport');
});
});
});
});

describe('parrableIdSystem.decode()', function() {
Expand Down Expand Up @@ -529,7 +661,7 @@ describe('Parrable ID System', function() {
});
});

describe('partners parsing', () => {
describe('partners parsing', function () {
let callbackSpy = sinon.spy();

const partnersTestCase = [
Expand Down