From bb97526cf0f11ea8227b0a332c42f012cef4b49e Mon Sep 17 00:00:00 2001
From: h12media <65672347+h12media@users.noreply.github.com>
Date: Thu, 25 Mar 2021 14:00:58 +0300
Subject: [PATCH] H12media Bid Adapter: added new optional params, fixes, and
various integration support (#6436)
* Change module H12 Media
* Change module H12 Media
* Change module H12 Media
* Change module H12 Media
* Update module H12 Media
* Update module H12 Media
* Update module H12 Media
* Update module H12 Media
* Update module H12 Media
* Update module H12 Media
---
modules/h12mediaBidAdapter.js | 208 +++++++++++--------
test/spec/modules/h12mediaBidAdapter_spec.js | 135 ++++++++----
2 files changed, 218 insertions(+), 125 deletions(-)
diff --git a/modules/h12mediaBidAdapter.js b/modules/h12mediaBidAdapter.js
index 0d2c22a3f688..7b736780226a 100644
--- a/modules/h12mediaBidAdapter.js
+++ b/modules/h12mediaBidAdapter.js
@@ -1,6 +1,5 @@
import * as utils from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
-import find from 'core-js-pure/features/array/find.js';
const BIDDER_CODE = 'h12media';
const DEFAULT_URL = 'https://bidder.h12-media.com/prebid/';
const DEFAULT_CURRENCY = 'USD';
@@ -16,21 +15,31 @@ export const spec = {
},
buildRequests: function(validBidRequests, bidderRequest) {
- const requestUrl = validBidRequests[0].params.endpointdom || DEFAULT_URL;
- const isiframe = !((window.self === window.top) || window.frameElement);
+ const isiframe = utils.inIframe();
const screenSize = getClientDimensions();
const docSize = getDocumentDimensions();
- const bidrequests = validBidRequests.map((bidRequest) => {
+ return validBidRequests.map((bidRequest) => {
const bidderParams = bidRequest.params;
- const adUnitElement = document.getElementById(bidRequest.adUnitCode);
+ const requestUrl = bidderParams.endpointdom || DEFAULT_URL;
+ let pubsubid = bidderParams.pubsubid || '';
+ if (pubsubid && pubsubid.length > 32) {
+ utils.logError('Bidder param \'pubsubid\' should be not more than 32 chars.');
+ pubsubid = '';
+ }
+ const pubcontainerid = bidderParams.pubcontainerid;
+ const adUnitElement = document.getElementById(pubcontainerid || bidRequest.adUnitCode);
const ishidden = !isVisible(adUnitElement);
- const coords = {
+ const framePos = getFramePos();
+ const coords = isiframe ? {
+ x: framePos[0],
+ y: framePos[1],
+ } : {
x: adUnitElement && adUnitElement.getBoundingClientRect().x,
y: adUnitElement && adUnitElement.getBoundingClientRect().y,
};
- return {
+ const bidrequest = {
bidId: bidRequest.bidId,
transactionId: bidRequest.transactionId,
adunitId: bidRequest.adUnitCode,
@@ -40,33 +49,46 @@ export const spec = {
adunitSize: bidRequest.mediaTypes.banner.sizes || [],
coords,
ishidden,
+ pubsubid,
+ pubcontainerid,
};
- });
- return {
- method: 'POST',
- url: requestUrl,
- options: {withCredentials: false},
- data: {
- gdpr: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') ? Boolean(bidderRequest.gdprConsent.gdprApplies & 1) : false,
- gdpr_cs: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies') ? bidderRequest.gdprConsent.consentString : '',
- topLevelUrl: window.top.location.href,
- refererUrl: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer : '',
- isiframe,
- version: '$prebid.version$',
- visitorInfo: {
- localTime: getLocalDateFormatted(),
- dayOfWeek: new Date().getDay(),
- screenWidth: screenSize[0],
- screenHeight: screenSize[1],
- docWidth: docSize[0],
- docHeight: docSize[1],
- scrollbarx: window.scrollX,
- scrollbary: window.scrollY,
+ let windowTop;
+ try {
+ windowTop = window.top;
+ } catch (e) {
+ utils.logMessage(e);
+ windowTop = window;
+ }
+
+ return {
+ method: 'POST',
+ url: requestUrl,
+ options: {withCredentials: true},
+ data: {
+ gdpr: !!utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies', false),
+ gdpr_cs: utils.deepAccess(bidderRequest, 'gdprConsent.consentString', ''),
+ usp: !!utils.deepAccess(bidderRequest, 'uspConsent', false),
+ usp_cs: utils.deepAccess(bidderRequest, 'uspConsent', ''),
+ topLevelUrl: utils.deepAccess(bidderRequest, 'refererInfo.referer', ''),
+ refererUrl: windowTop.document.referrer,
+ isiframe,
+ version: '$prebid.version$',
+ ExtUserIDs: bidRequest.userId,
+ visitorInfo: {
+ localTime: getLocalDateFormatted(),
+ dayOfWeek: new Date().getDay(),
+ screenWidth: screenSize[0],
+ screenHeight: screenSize[1],
+ docWidth: docSize[0],
+ docHeight: docSize[1],
+ scrollbarx: windowTop.scrollX,
+ scrollbary: windowTop.scrollY,
+ },
+ bidrequest,
},
- bidrequests,
- },
- };
+ };
+ });
},
interpretResponse: function(serverResponse, bidRequests) {
@@ -74,29 +96,28 @@ export const spec = {
try {
const serverBody = serverResponse.body;
if (serverBody) {
- if (serverBody.bids) {
- serverBody.bids.forEach(bidBody => {
- const bidRequest = find(bidRequests.data.bidrequests, bid => bid.bidId === bidBody.bidId);
- const bidResponse = {
- currency: serverBody.currency || DEFAULT_CURRENCY,
- netRevenue: serverBody.netRevenue || DEFAULT_NET_REVENUE,
- ttl: serverBody.ttl || DEFAULT_TTL,
- requestId: bidBody.bidId,
- cpm: bidBody.cpm,
- width: bidBody.width,
- height: bidBody.height,
- creativeId: bidBody.creativeId,
- ad: bidBody.ad,
- meta: bidBody.meta,
- mediaType: 'banner',
- };
- if (bidRequest) {
- bidResponse.pubid = bidRequest.pubid;
- bidResponse.placementid = bidRequest.placementid;
- bidResponse.size = bidRequest.size;
- }
- bidResponses.push(bidResponse);
- });
+ if (serverBody.bid) {
+ const bidBody = serverBody.bid;
+ const bidRequest = bidRequests.data.bidrequest;
+ const bidResponse = {
+ currency: serverBody.currency || DEFAULT_CURRENCY,
+ netRevenue: serverBody.netRevenue || DEFAULT_NET_REVENUE,
+ ttl: serverBody.ttl || DEFAULT_TTL,
+ requestId: bidBody.bidId,
+ cpm: bidBody.cpm,
+ width: bidBody.width,
+ height: bidBody.height,
+ creativeId: bidBody.creativeId,
+ ad: bidBody.ad,
+ meta: bidBody.meta,
+ mediaType: 'banner',
+ };
+ if (bidRequest) {
+ bidResponse.pubid = bidRequest.pubid;
+ bidResponse.placementid = bidRequest.placementid;
+ bidResponse.size = bidRequest.size;
+ }
+ bidResponses.push(bidResponse);
}
}
return bidResponses;
@@ -105,47 +126,50 @@ export const spec = {
}
},
- getUserSyncs: function(syncOptions, serverResponses, gdprConsent) {
- const serverBody = serverResponses[0].body;
+ getUserSyncs: function(syncOptions, serverResponses, gdprConsent, usPrivacy) {
const syncs = [];
+ const uspApplies = !!utils.deepAccess(usPrivacy, 'uspConsent', false);
+ const uspString = utils.deepAccess(usPrivacy, 'uspConsent', '');
gdprConsent = gdprConsent || {
gdprApplies: false, consentString: '',
};
- if (serverBody) {
- if (serverBody.bids) {
- serverBody.bids.forEach(bidBody => {
- const userSyncUrls = bidBody.usersync || [];
- const userSyncUrlProcess = url => {
- return url
- .replace('{gdpr}', gdprConsent.gdprApplies)
- .replace('{gdpr_cs}', gdprConsent.consentString);
- }
+ const userSyncUrlProcess = url => {
+ return url
+ .replace('{gdpr}', gdprConsent.gdprApplies)
+ .replace('{gdpr_cs}', gdprConsent.consentString)
+ .replace('{usp}', uspApplies)
+ .replace('{usp_cs}', uspString);
+ }
- userSyncUrls.forEach(sync => {
- if (syncOptions.iframeEnabled && sync.type === 'iframe' && sync.url) {
- syncs.push({
- type: 'iframe',
- url: userSyncUrlProcess(sync.url),
- });
- }
- if (syncOptions.pixelEnabled && sync.type === 'image' && sync.url) {
- syncs.push({
- type: 'image',
- url: userSyncUrlProcess(sync.url),
- });
- }
+ serverResponses.forEach(serverResponse => {
+ const userSyncUrls = serverResponse.body.usersync || [];
+ userSyncUrls.forEach(sync => {
+ if (syncOptions.iframeEnabled && sync.type === 'iframe' && sync.url) {
+ syncs.push({
+ type: 'iframe',
+ url: userSyncUrlProcess(sync.url),
});
- });
- }
- }
+ }
+ if (syncOptions.pixelEnabled && sync.type === 'image' && sync.url) {
+ syncs.push({
+ type: 'image',
+ url: userSyncUrlProcess(sync.url),
+ });
+ }
+ })
+ });
return syncs;
},
}
function getContext(elem) {
- return elem && window.document.body.contains(elem) ? window : (window.top.document.body.contains(elem) ? top : undefined);
+ try {
+ return elem && window.document.body.contains(elem) ? window : (window.top.document.body.contains(elem) ? top : undefined);
+ } catch (e) {
+ return undefined;
+ }
}
function isDefined(val) {
@@ -206,4 +230,24 @@ function getLocalDateFormatted() {
return `${d.getFullYear()}-${two(d.getMonth() + 1)}-${two(d.getDate())} ${two(d.getHours())}:${two(d.getMinutes())}:${two(d.getSeconds())}`;
}
+function getFramePos() {
+ let t = window;
+ let m = 0;
+ let frmLeft = 0;
+ let frmTop = 0;
+ do {
+ m = m + 1;
+ try {
+ if (m > 1) {
+ t = t.parent
+ }
+ frmLeft = frmLeft + t.frameElement.getBoundingClientRect().left;
+ frmTop = frmTop + t.frameElement.getBoundingClientRect().top;
+ } catch (o) { /* keep looping */
+ }
+ } while ((m < 100) && (t.parent !== t.self))
+
+ return [frmLeft, frmTop];
+}
+
registerBidder(spec);
diff --git a/test/spec/modules/h12mediaBidAdapter_spec.js b/test/spec/modules/h12mediaBidAdapter_spec.js
index 08a83ce981fc..9861069f260b 100644
--- a/test/spec/modules/h12mediaBidAdapter_spec.js
+++ b/test/spec/modules/h12mediaBidAdapter_spec.js
@@ -1,6 +1,7 @@
import {expect} from 'chai';
import {spec} from 'modules/h12mediaBidAdapter';
import {newBidder} from 'src/adapters/bidderFactory';
+import * as utils from 'src/utils';
describe('H12 Media Adapter', function () {
const DEFAULT_CURRENCY = 'USD';
@@ -21,6 +22,7 @@ describe('H12 Media Adapter', function () {
auctionId: '9adc85ed-43ee-4a78-816b-52b7e578f313',
params: {
pubid: 123321,
+ pubsubid: 'pubsubtestid',
},
};
@@ -72,34 +74,34 @@ describe('H12 Media Adapter', function () {
currency: 'EUR',
netRevenue: true,
ttl: 500,
- bids: [{
+ bid: {
bidId: validBid.bidId,
cpm: 0.33,
width: 300,
height: 600,
creativeId: '335566',
ad: '
my ad
',
- usersync: [
- {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'image'},
- {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'iframe'}
- ],
meta: {
advertiserId: '54321',
advertiserName: 'My advertiser',
advertiserDomains: ['test.com']
}
- }]
+ },
+ usersync: [
+ {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'image'},
+ {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'iframe'}
+ ],
};
const serverResponse2 = {
- bids: [{
+ bid: {
bidId: validBid2.bidId,
cpm: 0.33,
width: 300,
height: 600,
creativeId: '335566',
ad: 'my ad 2
',
- }]
+ }
};
function removeElement(id) {
@@ -152,6 +154,10 @@ describe('H12 Media Adapter', function () {
beforeEach(function () {
sandbox = sinon.sandbox.create();
+ sandbox.stub(frameElement, 'getBoundingClientRect').returns({
+ left: 10,
+ top: 10,
+ });
});
afterEach(function () {
@@ -186,36 +192,62 @@ describe('H12 Media Adapter', function () {
createElementVisible(validBid.adUnitCode);
createElementVisible(validBid2.adUnitCode);
const requests = spec.buildRequests([validBid, validBid2], bidderRequest);
- const requestsData = requests.data;
+ const requestsData = requests[0].data.bidrequest;
- expect(requestsData.bidrequests[0]).to.include({adunitSize: validBid.mediaTypes.banner.sizes});
+ expect(requestsData).to.include({adunitSize: validBid.mediaTypes.banner.sizes});
});
it('should return empty bid size', function () {
createElementVisible(validBid.adUnitCode);
createElementVisible(validBid2.adUnitCode);
const requests = spec.buildRequests([validBid, validBid2], bidderRequest);
- const requestsData = requests.data;
+ const requestsData2 = requests[1].data.bidrequest;
+
+ expect(requestsData2).to.deep.include({adunitSize: []});
+ });
+
+ it('should return pubsubid from params', function () {
+ createElementVisible(validBid.adUnitCode);
+ createElementVisible(validBid2.adUnitCode);
+ const requests = spec.buildRequests([validBid, validBid2], bidderRequest);
+ const requestsData = requests[0].data.bidrequest;
+ const requestsData2 = requests[1].data.bidrequest;
+
+ expect(requestsData).to.include({pubsubid: 'pubsubtestid'});
+ expect(requestsData2).to.include({pubsubid: ''});
+ });
- expect(requestsData.bidrequests[1]).to.deep.include({adunitSize: []});
+ it('should return empty for incorrect pubsubid from params', function () {
+ createElementVisible(validBid.adUnitCode);
+ createElementVisible(validBid2.adUnitCode);
+ const bidWithPub = {...validBid};
+ bidWithPub.params.pubsubid = 'iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii'; // More than 32 chars
+ const requests = spec.buildRequests([bidWithPub], bidderRequest);
+ const requestsData = requests[0].data.bidrequest;
+
+ expect(requestsData).to.include({pubsubid: ''});
});
it('should return bid size from params', function () {
createElementVisible(validBid.adUnitCode);
createElementVisible(validBid2.adUnitCode);
const requests = spec.buildRequests([validBid, validBid2], bidderRequest);
- const requestsData = requests.data;
+ const requestsData = requests[0].data.bidrequest;
+ const requestsData2 = requests[1].data.bidrequest;
- expect(requestsData.bidrequests[1]).to.include({size: validBid2.params.size});
+ expect(requestsData).to.include({size: ''});
+ expect(requestsData2).to.include({size: validBid2.params.size});
});
it('should return GDPR info', function () {
createElementVisible(validBid.adUnitCode);
createElementVisible(validBid2.adUnitCode);
const requests = spec.buildRequests([validBid, validBid2], bidderRequest);
- const requestsData = requests.data;
+ const requestsData = requests[0].data;
+ const requestsData2 = requests[1].data;
expect(requestsData).to.include({gdpr: true, gdpr_cs: bidderRequest.gdprConsent.consentString});
+ expect(requestsData2).to.include({gdpr: true, gdpr_cs: bidderRequest.gdprConsent.consentString});
});
it('should not have error on empty GDPR', function () {
@@ -223,9 +255,23 @@ describe('H12 Media Adapter', function () {
createElementVisible(validBid2.adUnitCode);
const bidderRequestWithoutGDRP = {...bidderRequest, gdprConsent: null};
const requests = spec.buildRequests([validBid, validBid2], bidderRequestWithoutGDRP);
- const requestsData = requests.data;
+ const requestsData = requests[0].data;
+ const requestsData2 = requests[1].data;
expect(requestsData).to.include({gdpr: false});
+ expect(requestsData2).to.include({gdpr: false});
+ });
+
+ it('should not have error on empty USP', function () {
+ createElementVisible(validBid.adUnitCode);
+ createElementVisible(validBid2.adUnitCode);
+ const bidderRequestWithoutUSP = {...bidderRequest, uspConsent: null};
+ const requests = spec.buildRequests([validBid, validBid2], bidderRequestWithoutUSP);
+ const requestsData = requests[0].data;
+ const requestsData2 = requests[1].data;
+
+ expect(requestsData).to.include({usp: false});
+ expect(requestsData2).to.include({usp: false});
});
it('should create single POST', function () {
@@ -233,7 +279,8 @@ describe('H12 Media Adapter', function () {
createElementVisible(validBid2.adUnitCode);
const requests = spec.buildRequests([validBid, validBid2], bidderRequest);
- expect(requests.method).to.equal('POST');
+ expect(requests[0].method).to.equal('POST');
+ expect(requests[1].method).to.equal('POST');
});
});
@@ -241,42 +288,44 @@ describe('H12 Media Adapter', function () {
it('should return coords', function () {
createElementVisible(validBid.adUnitCode);
const requests = spec.buildRequests([validBid], bidderRequest);
- const requestsData = requests.data;
+ const requestsData = requests[0].data.bidrequest;
- expect(requestsData.bidrequests[0]).to.deep.include({coords: {x: 10, y: 10}});
+ expect(requestsData).to.deep.include({coords: {x: 10, y: 10}});
});
- it('should define not iframe', function () {
+ it('should define iframe', function () {
createElementVisible(validBid.adUnitCode);
createElementVisible(validBid2.adUnitCode);
const requests = spec.buildRequests([validBid, validBid2], bidderRequest);
- const requestsData = requests.data;
+ const requestsData = requests[0].data;
+ const requestsData2 = requests[1].data;
- expect(requestsData).to.include({isiframe: false});
+ expect(requestsData).to.include({isiframe: true});
+ expect(requestsData2).to.include({isiframe: true});
});
it('should define visible element', function () {
createElementVisible(validBid.adUnitCode);
const requests = spec.buildRequests([validBid], bidderRequest);
- const requestsData = requests.data;
+ const requestsData = requests[0].data.bidrequest;
- expect(requestsData.bidrequests[0]).to.include({ishidden: false});
+ expect(requestsData).to.include({ishidden: false});
});
it('should define invisible element', function () {
createElementInvisible(validBid.adUnitCode);
const requests = spec.buildRequests([validBid], bidderRequest);
- const requestsData = requests.data;
+ const requestsData = requests[0].data.bidrequest;
- expect(requestsData.bidrequests[0]).to.include({ishidden: true});
+ expect(requestsData).to.include({ishidden: true});
});
it('should define hidden element', function () {
createElementHidden(validBid.adUnitCode);
const requests = spec.buildRequests([validBid], bidderRequest);
- const requestsData = requests.data;
+ const requestsData = requests[0].data.bidrequest;
- expect(requestsData.bidrequests[0]).to.include({ishidden: true});
+ expect(requestsData).to.include({ishidden: true});
});
});
@@ -290,27 +339,27 @@ describe('H12 Media Adapter', function () {
it('should return no bids if the response is empty', function () {
const bidResponse = spec.interpretResponse({ body: [] }, { validBid });
- expect(bidResponse.length).to.equal(0);
+ expect(bidResponse).to.be.empty;
});
it('should return valid bid responses', function () {
createElementVisible(validBid.adUnitCode);
createElementVisible(validBid2.adUnitCode);
const request = spec.buildRequests([validBid, validBid2], bidderRequest);
- const bidResponse = spec.interpretResponse({body: serverResponse}, request);
+ const bidResponse = spec.interpretResponse({body: serverResponse}, request[0]);
expect(bidResponse[0]).to.deep.include({
requestId: validBid.bidId,
- ad: serverResponse.bids[0].ad,
+ ad: serverResponse.bid.ad,
mediaType: 'banner',
- creativeId: serverResponse.bids[0].creativeId,
- cpm: serverResponse.bids[0].cpm,
- width: serverResponse.bids[0].width,
- height: serverResponse.bids[0].height,
+ creativeId: serverResponse.bid.creativeId,
+ cpm: serverResponse.bid.cpm,
+ width: serverResponse.bid.width,
+ height: serverResponse.bid.height,
currency: 'EUR',
netRevenue: true,
ttl: 500,
- meta: serverResponse.bids[0].meta,
+ meta: serverResponse.bid.meta,
pubid: validBid.params.pubid
});
});
@@ -319,17 +368,17 @@ describe('H12 Media Adapter', function () {
createElementVisible(validBid.adUnitCode);
createElementVisible(validBid2.adUnitCode);
const request = spec.buildRequests([validBid, validBid2], bidderRequest);
- const bidResponse = spec.interpretResponse({body: serverResponse2}, request);
+ const bidResponse = spec.interpretResponse({body: serverResponse2}, request[0]);
expect(bidResponse[0]).to.deep.include({
requestId: validBid2.bidId,
- ad: serverResponse2.bids[0].ad,
+ ad: serverResponse2.bid.ad,
mediaType: 'banner',
- creativeId: serverResponse2.bids[0].creativeId,
- cpm: serverResponse2.bids[0].cpm,
- width: serverResponse2.bids[0].width,
- height: serverResponse2.bids[0].height,
- meta: serverResponse2.bids[0].meta,
+ creativeId: serverResponse2.bid.creativeId,
+ cpm: serverResponse2.bid.cpm,
+ width: serverResponse2.bid.width,
+ height: serverResponse2.bid.height,
+ meta: serverResponse2.bid.meta,
pubid: validBid2.params.pubid,
currency: DEFAULT_CURRENCY,
netRevenue: DEFAULT_NET_REVENUE,