From bb7af03ad99bee2dfa4435fd98801a112f7ddf14 Mon Sep 17 00:00:00 2001 From: federico Date: Wed, 6 Jul 2022 12:40:11 +0200 Subject: [PATCH 1/5] Adds support for price floor, supply chain, GPID --- modules/onetagBidAdapter.js | 52 ++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 89c614dba23..cfbda094d98 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -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, logWarn, logError, deepAccess } from '../src/utils.js'; const ENDPOINT = 'https://onetag-sys.com/prebid-request'; const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/'; @@ -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'); @@ -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 => { @@ -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); @@ -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) { @@ -373,6 +379,38 @@ 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; +} + +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, From ce8f405d4e23b878d1d62c87fee3bf712b232ea9 Mon Sep 17 00:00:00 2001 From: federico Date: Tue, 12 Jul 2022 10:38:48 +0200 Subject: [PATCH 2/5] Removes unused import --- modules/onetagBidAdapter.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index cfbda094d98..9600757922d 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -7,7 +7,7 @@ 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, logWarn, logError, deepAccess } from '../src/utils.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/'; @@ -270,7 +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['gpid'] = deepAccess(bidRequest, 'ortb2Imp.ext.gpid') || deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot'); this['pubId'] = params.pubId; this['ext'] = params.ext; if (params.pubClick) { @@ -384,13 +384,12 @@ function getBidFloor(bidRequest, mediaType, sizes) { if (typeof bidRequest.getFloor === 'function') { sizes.forEach(size => { const floor = bidRequest.getFloor({ - currency: 'EUR', + currency: 'EUR', mediaType: mediaType || '*', - size: [size.width, size.height] - }); + size: [size.width, size.height] + }); floor.size = deepClone(size); - if (!floor.floor) - floor.floor = null; + if (!floor.floor) { floor.floor = null; } priceFloors.push(floor); }); } From 0262563e7923d15bd8516124efb78559f7b15974 Mon Sep 17 00:00:00 2001 From: federico Date: Tue, 12 Jul 2022 10:45:52 +0200 Subject: [PATCH 3/5] Updates onetag test file --- test/spec/modules/onetagBidAdapter_spec.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index f335f2ec62a..48acf661310 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -193,7 +193,8 @@ describe('onetag', function () { 'context', 'playerSize', 'mediaTypeInfo', - 'type' + 'type', + 'priceFloors' ); } else if (isValid(BANNER, bid)) { expect(bid).to.have.all.keys( @@ -205,7 +206,8 @@ describe('onetag', function () { 'transactionId', 'mediaTypeInfo', 'sizes', - 'type' + 'type', + 'priceFloors' ); } expect(bid.bidId).to.be.a('string'); From 8c6fa95a9b570720066d85240e767e626fd78e2b Mon Sep 17 00:00:00 2001 From: federico Date: Tue, 12 Jul 2022 12:43:01 +0200 Subject: [PATCH 4/5] Remove trailing space --- test/spec/modules/onetagBidAdapter_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index 48acf661310..19d0986d867 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -193,7 +193,7 @@ describe('onetag', function () { 'context', 'playerSize', 'mediaTypeInfo', - 'type', + 'type', 'priceFloors' ); } else if (isValid(BANNER, bid)) { @@ -206,7 +206,7 @@ describe('onetag', function () { 'transactionId', 'mediaTypeInfo', 'sizes', - 'type', + 'type', 'priceFloors' ); } From 53544a3165fd4302943148b0f042a6e5875bb798 Mon Sep 17 00:00:00 2001 From: federico Date: Wed, 20 Jul 2022 12:28:48 +0200 Subject: [PATCH 5/5] Add unit tests for schain validation function --- modules/onetagBidAdapter.js | 2 +- test/spec/modules/onetagBidAdapter_spec.js | 51 +++++++++++++++++++++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 9600757922d..0364acd5d21 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -396,7 +396,7 @@ function getBidFloor(bidRequest, mediaType, sizes) { return priceFloors; } -function isSchainValid(schain) { +export function isSchainValid(schain) { let isValid = false; const requiredFields = ['asi', 'sid', 'hp']; if (!schain || !schain.nodes) return isValid; diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index 19d0986d867..2dc0a43bbb0 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -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'; @@ -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 + } + ] + } + }, }; } @@ -210,6 +224,9 @@ describe('onetag', function () { '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'); } @@ -360,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() {