Skip to content

Commit

Permalink
Vidazoo Bid Adapter: support for gpp consent and bid data (#9480)
Browse files Browse the repository at this point in the history
* feat(module): multi size request

* fix getUserSyncs
added tests

* update(module): package-lock.json from master

* feat(module): VidazooBidAdapter - send top query params to server

* feat(module): pass gpp consent and bid data to server.

* fix(module): change spec bidder timeout to 3000.

---------

Co-authored-by: Udi Talias <udi.talias@gmail.com>
Co-authored-by: roman <shmoop207@gmail.com>
  • Loading branch information
3 people authored Feb 1, 2023
1 parent cb1a982 commit 4796d13
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 44 deletions.
78 changes: 56 additions & 22 deletions modules/vidazooBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { _each, deepAccess, parseSizesInput, parseUrl, uniques, isFn } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { getStorageManager } from '../src/storageManager.js';
import { bidderSettings } from '../src/bidderSettings.js';
import {_each, deepAccess, parseSizesInput, parseUrl, uniques, isFn} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {getStorageManager} from '../src/storageManager.js';
import {bidderSettings} from '../src/bidderSettings.js';
import { config } from '../src/config.js';

const GVLID = 744;
const DEFAULT_SUB_DOMAIN = 'prebid';
Expand All @@ -26,11 +27,11 @@ export const SUPPORTED_ID_SYSTEMS = {
'tdid': 1,
'pubProvidedId': 1
};
const storage = getStorageManager({ gvlid: GVLID, bidderCode: BIDDER_CODE });
const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE});

function getTopWindowQueryParams() {
try {
const parsedUrl = parseUrl(window.top.document.URL, { decodeSearchAsString: true });
const parsedUrl = parseUrl(window.top.document.URL, {decodeSearchAsString: true});
return parsedUrl.search;
} catch (e) {
return '';
Expand Down Expand Up @@ -58,10 +59,23 @@ function isBidRequestValid(bid) {
return !!(extractCID(params) && extractPID(params));
}

function buildRequest(bid, topWindowUrl, sizes, bidderRequest) {
const { params, bidId, userId, adUnitCode, schain, mediaTypes } = bid;
const { ext } = params;
let { bidFloor } = params;
function buildRequest(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) {
const {
params,
bidId,
userId,
adUnitCode,
schain,
mediaTypes,
auctionId,
transactionId,
bidderRequestId,
bidRequestsCount,
bidderRequestsCount,
bidderWinsCount
} = bid;
const {ext} = params;
let {bidFloor} = params;
const hashUrl = hashCode(topWindowUrl);
const dealId = getNextDealId(hashUrl);
const uniqueDealId = getUniqueDealId(hashUrl);
Expand Down Expand Up @@ -110,7 +124,14 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) {
isStorageAllowed: isStorageAllowed,
gpid: gpid,
cat: cat,
pagecat: pagecat
pagecat: pagecat,
auctionId: auctionId,
transactionId: transactionId,
bidderRequestId: bidderRequestId,
bidRequestsCount: bidRequestsCount,
bidderRequestsCount: bidderRequestsCount,
bidderWinsCount: bidderWinsCount,
bidderTimeout: bidderTimeout
};

appendUserIdsToRequestPayload(data, userId);
Expand All @@ -127,6 +148,14 @@ function buildRequest(bid, topWindowUrl, sizes, bidderRequest) {
data.usPrivacy = bidderRequest.uspConsent;
}

if (bidderRequest.gppConsent) {
data.gppString = bidderRequest.gppConsent.gppString;
data.gppSid = bidderRequest.gppConsent.applicableSections;
} else if (bidderRequest.ortb2?.regs?.gpp) {
data.gppString = bidderRequest.ortb2.regs.gpp;
data.gppSid = bidderRequest.ortb2.regs.gpp_sid;
}

const dto = {
method: 'POST',
url: `${createDomain(subDomain)}/prebid/multi/${cId}`,
Expand Down Expand Up @@ -169,10 +198,11 @@ function appendUserIdsToRequestPayload(payloadRef, userIds) {
function buildRequests(validBidRequests, bidderRequest) {
// TODO: does the fallback make sense here?
const topWindowUrl = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation;
const bidderTimeout = config.getConfig('bidderTimeout');
const requests = [];
validBidRequests.forEach(validBidRequest => {
const sizes = parseSizesInput(validBidRequest.sizes);
const request = buildRequest(validBidRequest, topWindowUrl, sizes, bidderRequest);
const request = buildRequest(validBidRequest, topWindowUrl, sizes, bidderRequest, bidderTimeout);
requests.push(request);
});
return requests;
Expand All @@ -182,14 +212,14 @@ function interpretResponse(serverResponse, request) {
if (!serverResponse || !serverResponse.body) {
return [];
}
const { bidId } = request.data;
const { results } = serverResponse.body;
const {bidId} = request.data;
const {results} = serverResponse.body;

let output = [];

try {
results.forEach(result => {
const { creativeId, ad, price, exp, width, height, currency, advertiserDomains, mediaType = BANNER } = result;
const {creativeId, ad, price, exp, width, height, currency, advertiserDomains, mediaType = BANNER} = result;
if (!ad || !price) {
return;
}
Expand Down Expand Up @@ -228,8 +258,8 @@ function interpretResponse(serverResponse, request) {

function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '') {
let syncs = [];
const { iframeEnabled, pixelEnabled } = syncOptions;
const { gdprApplies, consentString = '' } = gdprConsent;
const {iframeEnabled, pixelEnabled} = syncOptions;
const {gdprApplies, consentString = ''} = gdprConsent;

const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques);
const params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}`
Expand All @@ -253,7 +283,9 @@ export function hashCode(s, prefix = '_') {
let h = 0
let i = 0;
if (l > 0) {
while (i < l) { h = (h << 5) - h + s.charCodeAt(i++) | 0; }
while (i < l) {
h = (h << 5) - h + s.charCodeAt(i++) | 0;
}
}
return prefix + h;
}
Expand Down Expand Up @@ -310,17 +342,19 @@ export function getCacheOpt() {
export function getStorageItem(key) {
try {
return tryParseJSON(storage.getDataFromLocalStorage(key));
} catch (e) { }
} catch (e) {
}

return null;
}

export function setStorageItem(key, value, timestamp) {
try {
const created = timestamp || Date.now();
const data = JSON.stringify({ value, created });
const data = JSON.stringify({value, created});
storage.setDataInLocalStorage(key, data);
} catch (e) { }
} catch (e) {
}
}

export function tryParseJSON(value) {
Expand Down
83 changes: 61 additions & 22 deletions test/spec/modules/vidazooBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect } from 'chai';
import {expect} from 'chai';
import {
spec as adapter,
SUPPORTED_ID_SYSTEMS,
Expand All @@ -15,9 +15,10 @@ import {
getVidazooSessionId,
} from 'modules/vidazooBidAdapter.js';
import * as utils from 'src/utils.js';
import { version } from 'package.json';
import { useFakeTimers } from 'sinon';
import { BANNER, VIDEO } from '../../../src/mediaTypes';
import {version} from 'package.json';
import {useFakeTimers} from 'sinon';
import {BANNER, VIDEO} from '../../../src/mediaTypes';
import {config} from '../../../src/config';

const SUB_DOMAIN = 'openrtb';

Expand All @@ -38,6 +39,10 @@ const BID = {
'transactionId': 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf',
'sizes': [[300, 250], [300, 600]],
'bidderRequestId': '1fdb5ff1b6eaa7',
'auctionId': 'auction_id',
'bidRequestsCount': 4,
'bidderRequestsCount': 3,
'bidderWinsCount': 1,
'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a',
'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc',
'mediaTypes': [BANNER],
Expand All @@ -53,6 +58,10 @@ const VIDEO_BID = {
'adUnitCode': '63550ad1ff6642d368cba59dh5884270560',
'bidderRequestId': '12a8ae9ada9c13',
'transactionId': '56e184c6-bde9-497b-b9b9-cf47a61381ee',
'auctionId': 'auction_id',
'bidRequestsCount': 4,
'bidderRequestsCount': 3,
'bidderWinsCount': 1,
'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc',
'params': {
'subDomain': SUB_DOMAIN,
Expand Down Expand Up @@ -85,6 +94,8 @@ const BIDDER_REQUEST = {
'consentString': 'consent_string',
'gdprApplies': true
},
'gppString': 'gpp_string',
'gppSid': [7],
'uspConsent': 'consent_string',
'refererInfo': {
'page': 'https://www.greatsite.com',
Expand All @@ -94,6 +105,10 @@ const BIDDER_REQUEST = {
'site': {
'cat': ['IAB2'],
'pagecat': ['IAB2-2']
},
'regs': {
'gpp': 'gpp_string',
'gpp_sid': [7]
}
},
};
Expand Down Expand Up @@ -147,7 +162,7 @@ const REQUEST = {

function getTopWindowQueryParams() {
try {
const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true });
const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true});
return parsedUrl.search;
} catch (e) {
return '';
Expand Down Expand Up @@ -226,6 +241,9 @@ describe('VidazooBidAdapter', function () {

it('should build video request', function () {
const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page);
config.setConfig({
bidderTimeout: 3000
});
const requests = adapter.buildRequests([VIDEO_BID], BIDDER_REQUEST);
expect(requests).to.have.length(1);
expect(requests[0]).to.deep.equal({
Expand All @@ -243,6 +261,15 @@ describe('VidazooBidAdapter', function () {
gdpr: 1,
gdprConsent: 'consent_string',
usPrivacy: 'consent_string',
gppString: 'gpp_string',
gppSid: [7],
auctionId: 'auction_id',
bidRequestsCount: 4,
bidderRequestsCount: 3,
bidderWinsCount: 1,
bidderTimeout: 3000,
transactionId: '56e184c6-bde9-497b-b9b9-cf47a61381ee',
bidderRequestId: '12a8ae9ada9c13',
gpid: '',
prebidVersion: version,
ptrace: '1000',
Expand Down Expand Up @@ -278,6 +305,9 @@ describe('VidazooBidAdapter', function () {
});

it('should build banner request for each size', function () {
config.setConfig({
bidderTimeout: 3000
});
const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page);
const requests = adapter.buildRequests([BID], BIDDER_REQUEST);
expect(requests).to.have.length(1);
Expand All @@ -288,6 +318,15 @@ describe('VidazooBidAdapter', function () {
gdprConsent: 'consent_string',
gdpr: 1,
usPrivacy: 'consent_string',
gppString: 'gpp_string',
gppSid: [7],
auctionId: 'auction_id',
bidRequestsCount: 4,
bidderRequestsCount: 3,
bidderWinsCount: 1,
bidderTimeout: 3000,
transactionId: 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf',
bidderRequestId: '1fdb5ff1b6eaa7',
sizes: ['300x250', '300x600'],
url: 'https%3A%2F%2Fwww.greatsite.com',
referrer: 'https://www.somereferrer.com',
Expand Down Expand Up @@ -324,7 +363,7 @@ describe('VidazooBidAdapter', function () {

describe('getUserSyncs', function () {
it('should have valid user sync with iframeEnabled', function () {
const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]);
const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]);

expect(result).to.deep.equal([{
type: 'iframe',
Expand All @@ -333,15 +372,15 @@ describe('VidazooBidAdapter', function () {
});

it('should have valid user sync with cid on response', function () {
const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]);
const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]);
expect(result).to.deep.equal([{
type: 'iframe',
url: 'https://sync.cootlogix.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy='
}]);
});

it('should have valid user sync with pixelEnabled', function () {
const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]);
const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]);

expect(result).to.deep.equal([{
'url': 'https://sync.cootlogix.com/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=',
Expand All @@ -357,12 +396,12 @@ describe('VidazooBidAdapter', function () {
});

it('should return empty array when there is no ad', function () {
const responses = adapter.interpretResponse({ price: 1, ad: '' });
const responses = adapter.interpretResponse({price: 1, ad: ''});
expect(responses).to.be.empty;
});

it('should return empty array when there is no price', function () {
const responses = adapter.interpretResponse({ price: null, ad: 'great ad' });
const responses = adapter.interpretResponse({price: null, ad: 'great ad'});
expect(responses).to.be.empty;
});

Expand Down Expand Up @@ -422,11 +461,11 @@ describe('VidazooBidAdapter', function () {
const userId = (function () {
switch (idSystemProvider) {
case 'lipb':
return { lipbid: id };
return {lipbid: id};
case 'parrableId':
return { eid: id };
return {eid: id};
case 'id5id':
return { uid: id };
return {uid: id};
default:
return id;
}
Expand All @@ -445,18 +484,18 @@ describe('VidazooBidAdapter', function () {

describe('alternate param names extractors', function () {
it('should return undefined when param not supported', function () {
const cid = extractCID({ 'c_id': '1' });
const pid = extractPID({ 'p_id': '1' });
const subDomain = extractSubDomain({ 'sub_domain': 'prebid' });
const cid = extractCID({'c_id': '1'});
const pid = extractPID({'p_id': '1'});
const subDomain = extractSubDomain({'sub_domain': 'prebid'});
expect(cid).to.be.undefined;
expect(pid).to.be.undefined;
expect(subDomain).to.be.undefined;
});

it('should return value when param supported', function () {
const cid = extractCID({ 'cID': '1' });
const pid = extractPID({ 'Pid': '2' });
const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' });
const cid = extractCID({'cID': '1'});
const pid = extractPID({'Pid': '2'});
const subDomain = extractSubDomain({'subDOMAIN': 'prebid'});
expect(cid).to.be.equal('1');
expect(pid).to.be.equal('2');
expect(subDomain).to.be.equal('prebid');
Expand Down Expand Up @@ -569,7 +608,7 @@ describe('VidazooBidAdapter', function () {
now
});
setStorageItem('myKey', 2020);
const { value, created } = getStorageItem('myKey');
const {value, created} = getStorageItem('myKey');
expect(created).to.be.equal(now);
expect(value).to.be.equal(2020);
expect(typeof value).to.be.equal('number');
Expand All @@ -585,8 +624,8 @@ describe('VidazooBidAdapter', function () {
});

it('should parse JSON value', function () {
const data = JSON.stringify({ event: 'send' });
const { event } = tryParseJSON(data);
const data = JSON.stringify({event: 'send'});
const {event} = tryParseJSON(data);
expect(event).to.be.equal('send');
});

Expand Down

0 comments on commit 4796d13

Please sign in to comment.