Skip to content

Commit

Permalink
33Across: Update GDPR handling (prebid#3944)
Browse files Browse the repository at this point in the history
* check gdpr in buildRequest

* User sync based on whether gdpr applies or not

* check if consent data exists during user sync

* split user sync into further branches: 1) when gdpr does not apply 2) when consent data is unavailable

* contribute viewability to ttxRequest

* update tests

* remove window mock from tests

* use local variables

* introduce ServerRequestBuilder

* add withOptions() method to ServerRequestBuilder

* add semicolons

* sync up package-lock.json with upstream/master

* stub window.top in tests

* introduce getTopWindowSize() for test purpose

* reformat code

* add withSite() method to TtxRequestBuilder

add withSite() method to TtxRequestBuilder

* add isIframe() and _isViewabilityMeasurable()

* handle NON_MEASURABLE viewability in nested iframes

* consider page visibility, stub utils functions getWindowTop() and getWindowSelf()

* contribute viewability as 0 for inactive tab

* add prebidjs version to ttx request

* send caller as an array

* fix JSDoc in utils.js

* send viewability as non measurable when unable to locate target HTMLElement, add warning message

* introduce mapAdSlotPathToElementId()

* introduce getAdSlotHTMLElement(), add logging

* introduce mapAdSlotPathToElementId()

* update logging in ad unit path to element id mapping

* rephrase logging, fix tests

* update adapter documentation

* remove excessive logging

* improve logging

* revert change

* fix return of _mapAdUnitPathToElementId()

* improve logging of _mapAdUnitPathToElementId()

* do not use Array.find()

* return id once element is found

* return id once element is found

* let -> const

* Removing killswitch behavior for GDPR

* Updated comments to reflect current gdpr logic

* URI encode consent string

* Updated example site ID to help Prebid team e2e test our adapter
  • Loading branch information
curlyblueeagle authored and Alex committed Aug 1, 2019
1 parent 39747b7 commit 1450cbd
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 54 deletions.
30 changes: 17 additions & 13 deletions modules/33acrossBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function _getAdSlotHTMLElement(adUnitCode) {

// Infer the necessary data from valid bid for a minimal ttxRequest and create HTTP request
// NOTE: At this point, TTX only accepts request for a single impression
function _createServerRequest(bidRequest, gdprConsent) {
function _createServerRequest(bidRequest, gdprConsent = {}) {
const ttxRequest = {};
const params = bidRequest.params;
const element = _getAdSlotHTMLElement(bidRequest.adUnitCode);
Expand Down Expand Up @@ -143,14 +143,22 @@ function _createServerRequest(bidRequest, gdprConsent) {
}

// Sync object will always be of type iframe for TTX
function _createSync(siteId) {
function _createSync({siteId, gdprConsent = {}}) {
const ttxSettings = config.getConfig('ttxSettings');
const syncUrl = (ttxSettings && ttxSettings.syncUrl) || SYNC_ENDPOINT;

return {
const {consentString, gdprApplies} = gdprConsent;

const sync = {
type: 'iframe',
url: `${syncUrl}&id=${siteId}`
url: `${syncUrl}&id=${siteId}&gdpr_consent=${encodeURIComponent(consentString)}`
};

if (typeof gdprApplies === 'boolean') {
sync.url += `&gdpr=${Number(gdprApplies)}`;
}

return sync;
}

function _getSize(size) {
Expand Down Expand Up @@ -282,8 +290,6 @@ function isBidRequestValid(bid) {

// NOTE: With regards to gdrp consent data,
// - the server independently infers gdpr applicability therefore, setting the default value to false
// - the server, at this point, also doesn't need the consent string to handle gdpr compliance. So passing
// value whether set or not, for the sake of future dev.
function buildRequests(bidRequests, bidderRequest) {
const gdprConsent = Object.assign({
consentString: undefined,
Expand All @@ -307,14 +313,12 @@ function interpretResponse(serverResponse, bidRequest) {
return bidResponses;
}

// Register one sync per unique guid
// NOTE: If gdpr applies do not sync
// Register one sync per unique guid so long as iframe is enable
// Else no syncs
// For logic on how we handle gdpr data see _createSyncs and module's unit tests
// '33acrossBidAdapter#getUserSyncs'
function getUserSyncs(syncOptions, responses, gdprConsent) {
if (gdprConsent && gdprConsent.gdprApplies === true) {
return []
} else {
return (syncOptions.iframeEnabled) ? adapterState.uniqueSiteIds.map(_createSync) : ([]);
}
return (syncOptions.iframeEnabled) ? adapterState.uniqueSiteIds.map((siteId) => _createSync({gdprConsent, siteId})) : ([]);
}

export const spec = {
Expand Down
2 changes: 1 addition & 1 deletion modules/33acrossBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var adUnits = [
bids: [{
bidder: '33across',
params: {
siteId: 'examplePub1234',
siteId: 'cxBE0qjUir6iopaKkGJozW',
productId: 'siab'
}
}]
Expand Down
147 changes: 107 additions & 40 deletions test/spec/modules/33acrossBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -618,73 +618,140 @@ describe('33acrossBidAdapter:', function () {
];
});

context('when gdpr does not apply', function() {
let gdprConsent;
context('when iframe is not enabled', function() {
it('returns empty sync array', function() {
const syncOptions = {};

spec.buildRequests(bidRequests);

expect(spec.getUserSyncs(syncOptions)).to.deep.equal([]);
});
});

context('when iframe is enabled', function() {
let syncOptions;
beforeEach(function() {
gdprConsent = {
gdprApplies: false
syncOptions = {
iframeEnabled: true
};
});

context('when iframe is not enabled', function() {
it('returns empty sync array', function() {
const syncOptions = {};
context('when there is no gdpr consent data', function() {
it('returns sync urls with undefined consent string as param', function() {
spec.buildRequests(bidRequests);

const syncResults = spec.getUserSyncs(syncOptions, {}, undefined);
const expectedSyncs = [
{
type: 'iframe',
url: `${syncs[0].url}&gdpr_consent=undefined`
},
{
type: 'iframe',
url: `${syncs[1].url}&gdpr_consent=undefined`
}
]

expect(syncResults).to.deep.equal(expectedSyncs);
})
});

context('when gdpr applies but there is no consent string', function() {
it('returns sync urls with undefined consent string as param and gdpr=1', function() {
spec.buildRequests(bidRequests);

expect(spec.getUserSyncs(syncOptions, {}, gdprConsent)).to.deep.equal([]);
const syncResults = spec.getUserSyncs(syncOptions, {}, {gdprApplies: true});
const expectedSyncs = [
{
type: 'iframe',
url: `${syncs[0].url}&gdpr_consent=undefined&gdpr=1`
},
{
type: 'iframe',
url: `${syncs[1].url}&gdpr_consent=undefined&gdpr=1`
}
];

expect(syncResults).to.deep.equal(expectedSyncs);
});
});

context('when iframe is enabled', function() {
it('returns sync array equal to number of unique siteIDs', function() {
const syncOptions = {
iframeEnabled: true
};

context('when gdpr applies and there is consent string', function() {
it('returns sync urls with gdpr_consent=consent string as param and gdpr=1', function() {
spec.buildRequests(bidRequests);

expect(spec.getUserSyncs(syncOptions, {}, gdprConsent)).to.deep.equal(syncs);
const syncResults = spec.getUserSyncs(syncOptions, {}, {gdprApplies: true, consentString: 'consent123A'});
const expectedSyncs = [
{
type: 'iframe',
url: `${syncs[0].url}&gdpr_consent=consent123A&gdpr=1`
},
{
type: 'iframe',
url: `${syncs[1].url}&gdpr_consent=consent123A&gdpr=1`
}
];

expect(syncResults).to.deep.equal(expectedSyncs);
});
});
});

context('when consent data is not defined', function() {
context('when iframe is not enabled', function() {
it('returns empty sync array', function() {
const syncOptions = {};

context('when gdpr does not apply and there is no consent string', function() {
it('returns sync urls with undefined consent string as param and gdpr=0', function() {
spec.buildRequests(bidRequests);

expect(spec.getUserSyncs(syncOptions)).to.deep.equal([]);
const syncResults = spec.getUserSyncs(syncOptions, {}, {gdprApplies: false});
const expectedSyncs = [
{
type: 'iframe',
url: `${syncs[0].url}&gdpr_consent=undefined&gdpr=0`
},
{
type: 'iframe',
url: `${syncs[1].url}&gdpr_consent=undefined&gdpr=0`
}
];
expect(syncResults).to.deep.equal(expectedSyncs);
});
});

context('when iframe is enabled', function() {
it('returns sync array equal to number of unique siteIDs', function() {
const syncOptions = {
iframeEnabled: true
};

context('when gdpr is unknown and there is consent string', function() {
it('returns sync urls with only consent string as param', function() {
spec.buildRequests(bidRequests);

expect(spec.getUserSyncs(syncOptions)).to.deep.equal(syncs);
const syncResults = spec.getUserSyncs(syncOptions, {}, {consentString: 'consent123A'});
const expectedSyncs = [
{
type: 'iframe',
url: `${syncs[0].url}&gdpr_consent=consent123A`
},
{
type: 'iframe',
url: `${syncs[1].url}&gdpr_consent=consent123A`
}
];
expect(syncResults).to.deep.equal(expectedSyncs);
});
});
});

context('when gdpr applies', function() {
it('returns empty sync array', function() {
const syncOptions = {};
const gdprConsent = {
gdprApplies: true
};

spec.buildRequests(bidRequests);
context('when gdpr does not apply and there is consent string (yikes!)', function() {
it('returns sync urls with consent string as param and gdpr=0', function() {
spec.buildRequests(bidRequests);

expect(spec.getUserSyncs(syncOptions, {}, gdprConsent)).to.deep.equal([]);
const syncResults = spec.getUserSyncs(syncOptions, {}, {gdprApplies: false, consentString: 'consent123A'});
const expectedSyncs = [
{
type: 'iframe',
url: `${syncs[0].url}&gdpr_consent=consent123A&gdpr=0`
},
{
type: 'iframe',
url: `${syncs[1].url}&gdpr_consent=consent123A&gdpr=0`
}
];
expect(syncResults).to.deep.equal(expectedSyncs);
});
});
})
});
});
});

0 comments on commit 1450cbd

Please sign in to comment.