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

CCPA compliance #25

Merged
merged 11 commits into from
Jun 11, 2020
56 changes: 40 additions & 16 deletions modules/33acrossBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ const BIDDER_CODE = '33across';
const END_POINT = 'https://ssc.33across.com/api/v1/hb';
const SYNC_ENDPOINT = 'https://ssc-cms.33across.com/ps/?m=xch&rt=html&ru=deb';

const adapterState = {};
const adapterState = {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We need to really re-visit this. Saving the adapter state i.e. auction state forever is bad practice. It has led to unit tests being stateful.

For context, we had introduced adapterState in order to save the siteID of ad units passed through Bid Request phase in order to obtain them during user sync phase. The latter does not provide access (directly) to ad units.

uniqueSiteIds: []
};

const NON_MEASURABLE = 'nm';

Expand Down Expand Up @@ -65,7 +67,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 = {}, pageUrl) {
function _createServerRequest(bidRequest, gdprConsent = {}, uspConsent, pageUrl) {
const ttxRequest = {};
const params = bidRequest.params;
const element = _getAdSlotHTMLElement(bidRequest.adUnitCode);
Expand All @@ -84,7 +86,7 @@ function _createServerRequest(bidRequest, gdprConsent = {}, pageUrl) {
ttxRequest.imp = [];
ttxRequest.imp[0] = {
banner: {
format: sizes.map(size => Object.assign(size, {ext: {}}))
format: sizes
},
ext: {
ttx: {
Expand All @@ -97,6 +99,7 @@ function _createServerRequest(bidRequest, gdprConsent = {}, pageUrl) {
if (pageUrl) {
ttxRequest.site.page = pageUrl;
}

// Go ahead send the bidId in request to 33exchange so it's kept track of in the bid response and
// therefore in ad targetting process
ttxRequest.id = bidRequest.bidId;
Expand All @@ -109,19 +112,28 @@ function _createServerRequest(bidRequest, gdprConsent = {}, pageUrl) {
};
ttxRequest.regs = {
ext: {
gdpr: (gdprConsent.gdprApplies === true) ? 1 : 0
gdpr: (gdprConsent.gdprApplies === true) ? 1 : 0,
us_privacy: uspConsent || null
}
};
ttxRequest.ext = {
ttx: {
prebidStartedAt: Date.now(),
caller: [{
caller: [ {
'name': 'prebidjs',
'version': '$prebid.version$'
}]
} ]
}
};


if(bidRequest.schain) {
ttxRequest.source = {
ext: {
schain: bidRequest.schain
}
}
}

// Finally, set the openRTB 'test' param if this is to be a test bid
if (params.test === 1) {
ttxRequest.test = 1;
Expand All @@ -134,6 +146,7 @@ function _createServerRequest(bidRequest, gdprConsent = {}, pageUrl) {
contentType: 'text/plain',
withCredentials: true
};

// Allow the ability to configure the HB endpoint for testing purposes.
const ttxSettings = config.getConfig('ttxSettings');
const url = (ttxSettings && ttxSettings.url) || END_POINT;
Expand All @@ -148,15 +161,15 @@ function _createServerRequest(bidRequest, gdprConsent = {}, pageUrl) {
}

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

const {consentString, gdprApplies} = gdprConsent;
const { consentString, gdprApplies } = gdprConsent;

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

if (typeof gdprApplies === 'boolean') {
Expand Down Expand Up @@ -192,7 +205,7 @@ function _getBoundingBox(element, { w, h } = {}) {

function _transformSizes(sizes) {
if (utils.isArray(sizes) && sizes.length === 2 && !utils.isArray(sizes[0])) {
return [_getSize(sizes)];
return [ _getSize(sizes) ];
}

return sizes.map(_getSize);
Expand Down Expand Up @@ -239,7 +252,8 @@ function _getPercentInView(element, topWin, { w, h } = {}) {
bottom: topWin.innerHeight
}, elementBoundingBox ]);

let elementInViewArea, elementTotalArea;
let elementInViewArea,
elementTotalArea;

if (elementInViewBoundingBox !== null) {
// Some or all of the element is in view
Expand Down Expand Up @@ -301,11 +315,12 @@ function buildRequests(bidRequests, bidderRequest) {
gdprApplies: false
}, bidderRequest && bidderRequest.gdprConsent);

const uspConsent = bidderRequest && bidderRequest.uspConsent;
const pageUrl = (bidderRequest && bidderRequest.refererInfo) ? (bidderRequest.refererInfo.referer) : (undefined);

adapterState.uniqueSiteIds = bidRequests.map(req => req.params.siteId).filter(utils.uniques);

return bidRequests.map(req => _createServerRequest(req, gdprConsent, pageUrl));
return bidRequests.map(req => _createServerRequest(req, gdprConsent, uspConsent, pageUrl));
}

// NOTE: At this point, the response from 33exchange will only ever contain one bid i.e. the highest bid
Expand All @@ -324,13 +339,22 @@ function interpretResponse(serverResponse, bidRequest) {
// 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) {
return (syncOptions.iframeEnabled) ? adapterState.uniqueSiteIds.map((siteId) => _createSync({gdprConsent, siteId})) : ([]);
function getUserSyncs(syncOptions, responses, gdprConsent, uspConsent) {
const syncUrls = (
(syncOptions.iframeEnabled)
? adapterState.uniqueSiteIds.map((siteId) => _createSync({ gdprConsent, uspConsent, siteId }))
: ([])
);

// Clear adapter state of siteID's since we don't need this info anymore.
adapterState.uniqueSiteIds = [];

return syncUrls;
}

export const spec = {
NON_MEASURABLE,

code: BIDDER_CODE,

isBidRequestValid,
Expand Down
Loading