Skip to content

Commit

Permalink
onetag Bid Adapter : add support for price floors, supply chain, and …
Browse files Browse the repository at this point in the history
…gpid (prebid#8675)

* Adds support for price floor, supply chain, GPID

* Removes unused import

* Updates onetag test file

* Remove trailing space

* Add unit tests for schain validation function

Co-authored-by: federico <f.liccione@onetag.com>
  • Loading branch information
2 people authored and JacobKlein26 committed Feb 8, 2023
1 parent ae0a7db commit 3d00665
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 11 deletions.
51 changes: 44 additions & 7 deletions modules/onetagBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
'use strict';

import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {INSTREAM, OUTSTREAM} from '../src/video.js';
import {Renderer} from '../src/Renderer.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { INSTREAM, OUTSTREAM } from '../src/video.js';
import { Renderer } from '../src/Renderer.js';
import {find} from '../src/polyfill.js';
import {getStorageManager} from '../src/storageManager.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {createEidsArray} from './userId/eids.js';
import {deepClone} from '../src/utils.js';
import { getStorageManager } from '../src/storageManager.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { createEidsArray } from './userId/eids.js';
import { deepClone, logError, deepAccess } from '../src/utils.js';

const ENDPOINT = 'https://onetag-sys.com/prebid-request';
const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/';
Expand Down Expand Up @@ -68,6 +68,9 @@ function buildRequests(validBidRequests, bidderRequest) {
if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].userId) {
payload.userId = createEidsArray(validBidRequests[0].userId);
}
if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].schain && isSchainValid(validBidRequests[0].schain)) {
payload.schain = validBidRequests[0].schain;
}
try {
if (storage.hasLocalStorage()) {
payload.onetagSid = storage.getDataFromLocalStorage('onetag_sid');
Expand Down Expand Up @@ -245,6 +248,7 @@ function requestsToBids(bidRequests) {
// Other params
videoObj['mediaTypeInfo'] = deepClone(bidRequest.mediaTypes.video);
videoObj['type'] = VIDEO;
videoObj['priceFloors'] = getBidFloor(bidRequest, VIDEO, videoObj['playerSize']);
return videoObj;
});
const bannerBidRequests = bidRequests.filter(bidRequest => isValid(BANNER, bidRequest)).map(bidRequest => {
Expand All @@ -253,6 +257,7 @@ function requestsToBids(bidRequests) {
bannerObj['sizes'] = parseSizes(bidRequest);
bannerObj['type'] = BANNER;
bannerObj['mediaTypeInfo'] = deepClone(bidRequest.mediaTypes.banner);
bannerObj['priceFloors'] = getBidFloor(bidRequest, BANNER, bannerObj['sizes']);
return bannerObj;
});
return videoBidRequests.concat(bannerBidRequests);
Expand All @@ -265,6 +270,7 @@ function setGeneralInfo(bidRequest) {
this['bidderRequestId'] = bidRequest.bidderRequestId;
this['auctionId'] = bidRequest.auctionId;
this['transactionId'] = bidRequest.transactionId;
this['gpid'] = deepAccess(bidRequest, 'ortb2Imp.ext.gpid') || deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot');
this['pubId'] = params.pubId;
this['ext'] = params.ext;
if (params.pubClick) {
Expand Down Expand Up @@ -373,6 +379,37 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) {
return syncs;
}

function getBidFloor(bidRequest, mediaType, sizes) {
const priceFloors = [];
if (typeof bidRequest.getFloor === 'function') {
sizes.forEach(size => {
const floor = bidRequest.getFloor({
currency: 'EUR',
mediaType: mediaType || '*',
size: [size.width, size.height]
});
floor.size = deepClone(size);
if (!floor.floor) { floor.floor = null; }
priceFloors.push(floor);
});
}
return priceFloors;
}

export function isSchainValid(schain) {
let isValid = false;
const requiredFields = ['asi', 'sid', 'hp'];
if (!schain || !schain.nodes) return isValid;
isValid = schain.nodes.reduce((status, node) => {
if (!status) return status;
return requiredFields.every(field => node.hasOwnProperty(field));
}, true);
if (!isValid) {
logError('OneTag: required schain params missing');
}
return isValid;
}

export const spec = {
code: BIDDER_CODE,
gvlid: GVLID,
Expand Down
57 changes: 53 additions & 4 deletions test/spec/modules/onetagBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { spec, isValid, hasTypeVideo } from 'modules/onetagBidAdapter.js';
import { spec, isValid, hasTypeVideo, isSchainValid } from 'modules/onetagBidAdapter.js';
import { expect } from 'chai';
import {find} from 'src/polyfill.js';
import { BANNER, VIDEO } from 'src/mediaTypes.js';
Expand All @@ -15,7 +15,21 @@ describe('onetag', function () {
'bidId': '30b31c1838de1e',
'bidderRequestId': '22edbae2733bf6',
'auctionId': '1d1a030790a475',
'transactionId': 'qwerty123'
'transactionId': 'qwerty123',
'schain': {
'validation': 'off',
'config': {
'ver': '1.0',
'complete': 1,
'nodes': [
{
'asi': 'indirectseller.com',
'sid': '00001',
'hp': 1
}
]
}
},
};
}

Expand Down Expand Up @@ -193,7 +207,8 @@ describe('onetag', function () {
'context',
'playerSize',
'mediaTypeInfo',
'type'
'type',
'priceFloors'
);
} else if (isValid(BANNER, bid)) {
expect(bid).to.have.all.keys(
Expand All @@ -205,9 +220,13 @@ describe('onetag', function () {
'transactionId',
'mediaTypeInfo',
'sizes',
'type'
'type',
'priceFloors'
);
}
if (bid.schain && isSchainValid(bid.schain)) {
expect(data).to.have.all.keys('schain');
}
expect(bid.bidId).to.be.a('string');
expect(bid.pubId).to.be.a('string');
}
Expand Down Expand Up @@ -358,6 +377,36 @@ describe('onetag', function () {
expect(syncs[0].url).to.match(/(?:[?&](?:us_privacy=us_foo(?:[&][^&]*)*))+$/);
});
});
describe('isSchainValid', function () {
it('Should return false when schain is null or undefined', function () {
expect(isSchainValid(null)).to.be.false;
expect(isSchainValid(undefined)).to.be.false;
});
it('Should return false when schain is missing nodes key', function () {
const schain = {'otherKey': 'otherValue'};
expect(isSchainValid(schain)).to.be.false;
});
it('Should return false when schain is missing one of the required SupplyChainNode attribute', function () {
const missingAsiNode = {'sid': '00001', 'hp': 1};
const missingSidNode = {'asi': 'indirectseller.com', 'hp': 1};
const missingHpNode = {'asi': 'indirectseller.com', 'sid': '00001'};
expect(isSchainValid({'config': {'nodes': [missingAsiNode]}})).to.be.false;
expect(isSchainValid({'config': {'nodes': [missingSidNode]}})).to.be.false;
expect(isSchainValid({'config': {'nodes': [missingHpNode]}})).to.be.false;
});
it('Should return true when schain contains all required attributes', function () {
const validSchain = {
'nodes': [
{
'asi': 'indirectseller.com',
'sid': '00001',
'hp': 1
}
]
};
expect(isSchainValid(validSchain)).to.be.true;
})
});
});

function getBannerVideoResponse() {
Expand Down

0 comments on commit 3d00665

Please sign in to comment.