diff --git a/modules/adoceanBidAdapter.js b/modules/adoceanBidAdapter.js index 0c23f5e3d8a..bf5596ab22a 100644 --- a/modules/adoceanBidAdapter.js +++ b/modules/adoceanBidAdapter.js @@ -6,7 +6,7 @@ const BIDDER_CODE = 'adocean'; function buildEndpointUrl(emiter, payloadMap) { const payload = []; _each(payloadMap, function(v, k) { - payload.push(k + '=' + encodeURIComponent(v)); + payload.push(k + '=' + (k === 'schain' ? v : encodeURIComponent(v))); }); const randomizedPart = Math.random().toString().slice(2); @@ -23,6 +23,10 @@ function buildRequest(masterBidRequests, masterId, gdprConsent) { payload.gdpr_consent = gdprConsent.consentString || undefined; payload.gdpr = gdprConsent.gdprApplies ? 1 : 0; } + const anyKey = Object.keys(masterBidRequests)[0]; + if (masterBidRequests[anyKey].schain) { + payload.schain = serializeSupplyChain(masterBidRequests[anyKey].schain); + } const bidIdMap = {}; @@ -48,6 +52,30 @@ function buildRequest(masterBidRequests, masterId, gdprConsent) { }; } +const SCHAIN_FIELDS = ['asi', 'sid', 'hp', 'rid', 'name', 'domain', 'ext']; +function serializeSupplyChain(schain) { + const header = `${schain.ver},${schain.complete}!`; + + const serializedNodes = []; + _each(schain.nodes, function(node) { + const serializedNode = SCHAIN_FIELDS + .map(fieldName => { + if (fieldName === 'ext') { + // do not serialize ext data, just mark if it was available + return ('ext' in node ? '1' : '0'); + } + if (fieldName in node) { + return encodeURIComponent(node[fieldName]).replace(/!/g, '%21'); + } + return ''; + }) + .join(','); + serializedNodes.push(serializedNode); + }); + + return header + serializedNodes.join('!'); +} + function assignToMaster(bidRequest, bidRequestsByMaster) { const masterId = bidRequest.params.masterId; const slaveId = bidRequest.params.slaveId; diff --git a/test/spec/modules/adoceanBidAdapter_spec.js b/test/spec/modules/adoceanBidAdapter_spec.js index 43316cd7483..ba691825112 100644 --- a/test/spec/modules/adoceanBidAdapter_spec.js +++ b/test/spec/modules/adoceanBidAdapter_spec.js @@ -83,6 +83,20 @@ describe('AdoceanAdapter', function () { 'auctionId': '1d1a030790a475', } ]; + const schainExample = { + 'schain': { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001!,2', + rid: 'BidRequest1', + hp: 1 + } + ] + } + }; const bidderRequest = { gdprConsent: { @@ -134,7 +148,17 @@ describe('AdoceanAdapter', function () { expect(requests[0].url).to.include('aosspsizes=myaozpniqismex~300x250_300x600-myaowafpdwlrks~300x200_600x250'); expect((requests[0].url.match(/aosspsizes=/g) || []).length).to.equal(1); }); - }) + + it('should attach schain parameter if available', function() { + let requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests.some(e => e.url.includes('schain='))).to.be.false; + + const bidsWithSchain = deepClone(bidRequests).map(e => ({...e, ...schainExample})); + requests = spec.buildRequests(bidsWithSchain, bidderRequest); + expect(requests.every(e => e.url.includes('schain=1.0,1!directseller.com,00001%21%2C2,1,BidRequest1,,,0')), + `One of urls does not contain valid schain param: ${requests.map(e => e.url).join('\n')}`).to.be.true; + }); + }); describe('interpretResponse', function () { const response = {