diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index facecdaa578..93cba3a38cb 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -633,6 +633,118 @@ function _handleEids(payload, validBidRequests) { } } +function _isSchainObjectValid(schainObject) { + const schainErrorPrefix = 'Invalid schain object found: '; + const shouldBeAString = ' should be a string'; + const shouldBeAnInteger = ' should be an Integer'; + const shouldBeAnObject = ' should be an object'; + const shouldBeAnArray = ' should be an Array'; + + if (!utils.isPlainObject(schainObject)) { + utils.logError(schainErrorPrefix + `schain` + shouldBeAnObject); + return false; + } + + // complete: Integer + if (!utils.isNumber(schainObject.complete) || !utils.isInteger(schainObject.complete)) { + utils.logError(schainErrorPrefix + `schain.complete` + shouldBeAnInteger); + return false; + } + + // ver: String + if (!utils.isStr(schainObject.ver)) { + utils.logError(schainErrorPrefix + `schain.ver` + shouldBeAString); + return false; + } + + // ext: Object [optional] + if (utils.hasOwn(schainObject, 'ext')) { + if (!utils.isPlainObject(schainObject.ext)) { + utils.logError(schainErrorPrefix + `schain.ext` + shouldBeAnObject); + return false; + } + } + + // nodes: Array of objects + if (!utils.isArray(schainObject.nodes)) { + utils.logError(schainErrorPrefix + `schain.nodes` + shouldBeAnArray); + return false; + } + + // now validate each node + let isEachNodeIsValid = true; + schainObject.nodes.forEach(node => { + // asi: String + if (!utils.isStr(node.asi)) { + isEachNodeIsValid = isEachNodeIsValid && false; + utils.logError(schainErrorPrefix + `schain.nodes[].asi` + shouldBeAString); + } + + // sid: String + if (!utils.isStr(node.sid)) { + isEachNodeIsValid = isEachNodeIsValid && false; + utils.logError(schainErrorPrefix + `schain.nodes[].sid` + shouldBeAString); + } + + // hp: Integer + if (!utils.isNumber(node.hp) || !utils.isInteger(node.hp)) { + isEachNodeIsValid = isEachNodeIsValid && false; + utils.logError(schainErrorPrefix + `schain.nodes[].hp` + shouldBeAnInteger); + } + + // rid: String [Optional] + if (utils.hasOwn(node, 'rid')) { + if (!utils.isStr(node.rid)) { + isEachNodeIsValid = isEachNodeIsValid && false; + utils.logError(schainErrorPrefix + `schain.nodes[].rid` + shouldBeAString); + } + } + + // name: String [Optional] + if (utils.hasOwn(node, 'name')) { + if (!utils.isStr(node.name)) { + isEachNodeIsValid = isEachNodeIsValid && false; + utils.logError(schainErrorPrefix + `schain.nodes[].name` + shouldBeAString); + } + } + + // domain: String [Optional] + if (utils.hasOwn(node, 'domain')) { + if (!isStr(node.domain)) { + isEachNodeIsValid = isEachNodeIsValid && false; + utils.logError(schainErrorPrefix + `schain.nodes[].domain` + shouldBeAString); + } + } + + // ext: Object [Optional] + if (utils.hasOwn(node, 'ext')) { + if (!utils.isPlainObject(node.ext)) { + isEachNodeIsValid = isEachNodeIsValid && false; + logError(schainErrorPrefix + `schain.nodes[].ext` + shouldBeAnObject); + } + } + }); + + if (!isEachNodeIsValid) { + return false; + } + + return true; +} + +function _addSchainObjet(payload) { + let schainConfig = config.getConfig('schain'); + if (schainConfig && _isSchainObjectValid(schainConfig)) { + if (!payload.source) { + payload.source = {}; + } + if (!payload.source.ext) { + payload.source.ext = {}; + } + payload.source.ext.schain = schainConfig; + } +} + function _checkMediaType(adm, newBid) { // Create a regex here to check the strings var admStr = ''; @@ -873,15 +985,6 @@ export const spec = { payload.site.page = conf.kadpageurl.trim() || payload.site.page.trim(); payload.site.domain = _getDomainFromURL(payload.site.page); - // adding schain object - if (validBidRequests[0].schain) { - payload.source = { - ext: { - schain: validBidRequests[0].schain - } - }; - } - // Attaching GDPR Consent Params if (bidderRequest && bidderRequest.gdprConsent) { payload.user.ext = { @@ -895,6 +998,7 @@ export const spec = { }; } + _addSchainObjet(payload); _handleDealCustomTargetings(payload, dctrArr, validBidRequests); _handleEids(payload, validBidRequests); _blockedIabCategoriesValidation(payload, blockedIabCategories); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 3de83c56213..28134785764 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -24,27 +24,8 @@ describe('PubMatic adapter', function () { let bannerVideoAndNativeBidRequests; let bannerBidResponse; let videoBidResponse; - let schainConfig; beforeEach(function () { - schainConfig = { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'indirectseller.com', - 'sid': '00001', - 'hp': 1 - }, - - { - 'asi': 'indirectseller-2.com', - 'sid': '00002', - 'hp': 2 - } - ] - }; - bidRequests = [ { bidder: 'pubmatic', @@ -74,8 +55,7 @@ describe('PubMatic adapter', function () { bidId: '23acc48ad47af5', requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', bidderRequestId: '1c56ad30b9b8ca8', - transactionId: '92489f71-1bf2-49a0-adf9-000cea934729', - schain: schainConfig + transactionId: '92489f71-1bf2-49a0-adf9-000cea934729' } ]; @@ -748,7 +728,6 @@ describe('PubMatic adapter', function () { expect(data.imp[0].banner.h).to.equal(250); // height expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); - expect(data.source.ext.schain).to.deep.equal(bidRequests[0].schain); }); it('Request params check: without adSlot', function () { @@ -1476,6 +1455,125 @@ describe('PubMatic adapter', function () { }); }); + describe('schain from config', function() { + let schainConfig; + let sandbox; + + beforeEach(function() { + sandbox = sinon.sandbox.create(); + + schainConfig = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'indirectseller.com', + 'sid': '00001', + 'hp': 1 + }, + + { + 'asi': 'indirectseller-2.com', + 'sid': '00002', + 'hp': 2 + } + ] + }; + }); + + afterEach(() => { + sandbox.restore(); + delete window.DigiTrust; + }); + + it('if schain is not defined then schain should not be passed in request', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = {}; + return config[key]; + }); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.source).to.deep.equal(undefined); + }); + + it('if schain is a string then schain should not be passed in request', function() { + schainConfig = JSON.stringify(schainConfig); + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + schain: schainConfig + }; + return config[key]; + }); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.source).to.deep.equal(undefined); + }); + + it('if schain.complete is NOT Integer then schain should not be passed in request', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + schain: schainConfig + }; + return config[key]; + }); + let request, data; + + schainConfig.complete = 1; // integer + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.source.ext.schain).to.deep.equal(schainConfig); + + schainConfig.complete = '1'; // string + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.source).to.deep.equal(undefined); + + schainConfig.complete = 1.1; // float + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.source).to.deep.equal(undefined); + + schainConfig.complete = {}; // object + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.source).to.deep.equal(undefined); + + delete schainConfig.complete; // undefined + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.source).to.deep.equal(undefined); + + schainConfig.complete = true; // boolean + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.source).to.deep.equal(undefined); + + schainConfig.complete = []; // array + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.source).to.deep.equal(undefined); + }); + + it('if schain.version is NOT String then schain should not be passed in request', function() { + schainConfig.ver = 1; // Integer + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.source).to.deep.equal(undefined); + + schainConfig.ver = 1.1; // float + expect(isSchainObjectValid(schainConfig)).to.false; + schainConfig.ver = {}; // object + expect(isSchainObjectValid(schainConfig)).to.false; + delete schainConfig.ver; // undefined + expect(isSchainObjectValid(schainConfig)).to.false; + schainConfig.ver = true; // boolean + expect(isSchainObjectValid(schainConfig)).to.false; + schainConfig.ver = []; // array + expect(isSchainObjectValid(schainConfig)).to.false; + }); + + }); + it('Request params check for video ad', function () { let request = spec.buildRequests(videoBidRequests); let data = JSON.parse(request.data);