From 94d2b3a1d8ae16f299e4ade2ae720ec5026ae2cf Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Thu, 12 Mar 2020 16:49:58 +0300 Subject: [PATCH 01/57] Added TheMediaGridNM Bid Adapter --- modules/gridNMBidAdapter.js | 248 ++++++++++++++ modules/gridNMBidAdapter.md | 33 ++ test/spec/modules/gridNMBidAdapter_spec.js | 376 +++++++++++++++++++++ test/spec/modules/vdoaiBidAdapter_spec.js | 210 ++++++------ 4 files changed, 762 insertions(+), 105 deletions(-) create mode 100644 modules/gridNMBidAdapter.js create mode 100644 modules/gridNMBidAdapter.md create mode 100644 test/spec/modules/gridNMBidAdapter_spec.js diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js new file mode 100644 index 00000000000..550f2392cb4 --- /dev/null +++ b/modules/gridNMBidAdapter.js @@ -0,0 +1,248 @@ +import * as utils from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { Renderer } from '../src/Renderer.js'; +import { VIDEO } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'gridNM'; +const ENDPOINT_URL = 'https://grid.bidswitch.net/hbnm'; +const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=iponweblabs'; +const TIME_TO_LIVE = 360; +const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; + +let hasSynced = false; + +const LOG_ERROR_MESS = { + noAuid: 'Bid from response has no auid parameter - ', + noAdm: 'Bid from response has no adm parameter - ', + noPrice: 'Bid from response has no price parameter - ', + noSizes: 'Bid from response has no w or h parameter - ', + wrongContentType: 'Bid from response has wrong content_type parameter - ', + noBid: 'Array of bid objects is empty', + noPlacementCode: 'Can\'t find in requested bids the bid with auid - ', + emptyUids: 'Uids should be not empty', + emptySeatbid: 'Seatbid array from response has empty item', + emptyResponse: 'Response is empty', + hasEmptySeatbidArray: 'Response has empty seatbid array', + hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' +}; + +// const KEYS_FOR_PARAMS = ['secid','pubid','pubdata','floorcpm','source','video']; +// const KEYS_FOR_VIDEO = ['mimes','mind','maxd','protocols','size','linearity','skip','skipmin','skipafter','api','startdelay','placement','playbackmethod']; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [ VIDEO ], + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function(bid) { + let invalid = !bid.params.source || !utils.isStr(bid.params.source); + if (!invalid) { + invalid = !bid.params.video || !bid.params.video.protocols || !bid.params.video.mimes; + } + if (!invalid) { + const {protocols, mimes} = bid.params.video; + invalid = !utils.isArray(mimes) || mimes.filter((it) => !(it && utils.isStr(it))).length; + invalid = invalid || !utils.isArray(protocols) || protocols.filter((it) => !(utils.isNumber(it) && it > 0 && !(it % 1))).length; + } + return !invalid; + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests - an array of bids + * @param {bidderRequest} bidderRequest bidder request object + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + const bids = validBidRequests || []; + const requests = []; + + bids.forEach(bid => { + const {params, bidderRequestId, sizes} = bid; + const payload = { + sizes: utils.parseSizesInput(sizes).join(','), + r: bidderRequestId, + wrapperType: 'Prebid_js', + wrapperVersion: '$prebid.version$' + }; + + if (bidderRequest) { + if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { + payload.u = bidderRequest.refererInfo.referer; + } + if (bidderRequest.timeout) { + payload.wtimeout = bidderRequest.timeout; + } + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.consentString) { + payload.gdpr_consent = bidderRequest.gdprConsent.consentString; + } + payload.gdpr_applies = + (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') + ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; + } + if (bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent; + } + } + + /* const content = {}; + KEYS_FOR_PARAMS.forEach((key) => { + if (params.hasOwnProperty(key) && params[key]) { + if (key === 'video') { + const paramsVideo = params.video; + const video = {}; + KEYS_FOR_VIDEO.forEach((vKey) => { + if (paramsVideo.hasOwnProperty(vKey)) { + video[vKey] = paramsVideo[vKey]; + } + }); + content.video = video; + } else { + content[key] = params[key]; + } + } + }); */ + + requests.push({ + method: 'POST', + url: ENDPOINT_URL + '?' + utils.parseQueryStringParameters(payload).replace(/\&$/, ''), + bid: bid, + data: params // content + }); + }); + + return requests; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @param {*} bidRequest + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, bidRequest) { + serverResponse = serverResponse && serverResponse.body; + const bidResponses = []; + + let errorMessage; + + if (!serverResponse) errorMessage = LOG_ERROR_MESS.emptyResponse; + else if (serverResponse.seatbid && !serverResponse.seatbid.length) { + errorMessage = LOG_ERROR_MESS.hasEmptySeatbidArray; + } + + if (!errorMessage && serverResponse.seatbid) { + const serverBid = _getBidFromResponse(serverResponse.seatbid[0]); + if (serverBid) { + if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); + else if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); + else if (!serverBid.price) errorMessage = LOG_ERROR_MESS.noPrice + JSON.stringify(serverBid); + else if (!serverBid.w || !serverBid.h) errorMessage = LOG_ERROR_MESS.noSizes + JSON.stringify(serverBid); + else if (serverBid.content_type !== 'video') errorMessage = LOG_ERROR_MESS.wrongContentType + serverBid.content_type; + if (!errorMessage) { + const bid = bidRequest.bid; + const bidResponse = { + requestId: bid.bidderRequestId, + bidderCode: spec.code, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + creativeId: serverBid.auid, // bid.bidId, + currency: 'USD', + netRevenue: false, + ttl: TIME_TO_LIVE, + dealId: serverBid.dealid, + vastXml: serverBid.adm, + mediaType: VIDEO, + adResponse: { + content: serverBid.adm + } + }; + + if (!bid.renderer && (!bid.mediaTypes || !bid.mediaTypes.video || bid.mediaTypes.video.context === 'outstream')) { + bidResponse.renderer = createRenderer(bidResponse, { + id: bid.bidId, + url: RENDERER_URL + }); + } + bidResponses.push(bidResponse); + } + } + } + if (errorMessage) utils.logError(errorMessage); + return bidResponses; + }, + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { + if (!hasSynced && syncOptions.pixelEnabled) { + let params = ''; + + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + params += `&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent) { + params += `&us_privacy=${uspConsent}`; + } + + hasSynced = true; + return { + type: 'image', + url: SYNC_URL + params + }; + } + } +}; + +function _getBidFromResponse(respItem) { + if (!respItem) { + utils.logError(LOG_ERROR_MESS.emptySeatbid); + } else if (!respItem.bid) { + utils.logError(LOG_ERROR_MESS.hasNoArrayOfBids + JSON.stringify(respItem)); + } else if (!respItem.bid[0]) { + utils.logError(LOG_ERROR_MESS.noBid); + } + return respItem && respItem.bid && respItem.bid[0]; +} + +function outstreamRender (bid) { + bid.renderer.push(() => { + window.ANOutstreamVideo.renderAd({ + targetId: bid.adUnitCode, + adResponse: bid.adResponse + }); + }); +} + +function createRenderer (bid, rendererParams) { + const renderer = Renderer.install({ + id: rendererParams.id, + url: rendererParams.url, + loaded: false + }); + + try { + renderer.setRender(outstreamRender); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; +} + +export function resetUserSync() { + hasSynced = false; +} + +export function getSyncUrl() { + return SYNC_URL; +} + +registerBidder(spec); diff --git a/modules/gridNMBidAdapter.md b/modules/gridNMBidAdapter.md new file mode 100644 index 00000000000..e81cb04d3a4 --- /dev/null +++ b/modules/gridNMBidAdapter.md @@ -0,0 +1,33 @@ +# Overview + +Module Name: The Grid Media Bidder Adapter +Module Type: Bidder Adapter +Maintainer: grid-tech@themediagrid.com + +# Description + +Module that connects to Grid demand source to fetch bids. +Grid bid adapter supports Banner and Video (instream and outstream). + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[728, 90]], + mediaTypes: { video: {} }, + bids: [ + { + bidder: "gridNM", + params: { + source: 'jwp', + video: { + mimes: ['video/mp4', 'video/x-ms-wmv'], + protocols: [1,2,3,4,5,6] + } + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js new file mode 100644 index 00000000000..db46dd89015 --- /dev/null +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -0,0 +1,376 @@ +import { expect } from 'chai'; +import { spec, resetUserSync, getSyncUrl } from 'modules/gridNMBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; + +describe('TheMediaGridNM Adapter', function () { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'gridNM', + 'params': { + 'source': 'jwp', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': [1, 2, 3, 4, 5, 6] + } + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'source': 'jwp', + 'video': { + 'protocols': [1, 2, 3, 4, 5, 6] + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required params has invalid values', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'source': 'jwp', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': '1,2,3,4,5' + } + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + function parseRequestUrl(url) { + const res = {}; + url.replace(/^[^\?]+\?/, '').split('&').forEach((it) => { + const couple = it.split('='); + res[couple[0]] = decodeURIComponent(couple[1]); + }); + return res; + } + const bidderRequest = {refererInfo: {referer: 'https://example.com'}}; + const referrer = bidderRequest.refererInfo.referer; + let bidRequests = [ + { + 'bidder': 'gridNM', + 'params': { + 'source': 'jwp', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': [1, 2, 3, 4, 5, 6] + } + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'gridNM', + 'params': { + 'source': 'jwp', + 'pubid': 22, + 'video': { + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 3], + 'skip': 1 + } + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('should attach valid params to the tag', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + const requestsSizes = ['300x250,300x600', '728x90']; + requests.forEach((req, i) => { + expect(req.url).to.be.an('string'); + const payload = parseRequestUrl(req.url); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('r', '22edbae2733bf6'); + expect(payload).to.have.property('wrapperType', 'Prebid_js'); + expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); + expect(payload).to.have.property('sizes', requestsSizes[i]); + expect(req.data).to.deep.equal(bidRequests[i].params); + }); + }); + + it('if gdprConsent is present payload must have gdpr params', function () { + const [request] = spec.buildRequests([bidRequests[0]], {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); + expect(request.url).to.be.an('string'); + const payload = parseRequestUrl(request.url); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '1'); + }); + + it('if gdprApplies is false gdpr_applies must be 0', function () { + const [request] = spec.buildRequests([bidRequests[0]], {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); + expect(request.url).to.be.an('string'); + const payload = parseRequestUrl(request.url); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '0'); + }); + + it('if gdprApplies is undefined gdpr_applies must be 1', function () { + const [request] = spec.buildRequests([bidRequests[0]], {gdprConsent: {consentString: 'AAA'}}); + expect(request.url).to.be.an('string'); + const payload = parseRequestUrl(request.url); + expect(payload).to.have.property('gdpr_consent', 'AAA'); + expect(payload).to.have.property('gdpr_applies', '1'); + }); + + it('if usPrivacy is present payload must have us_privacy param', function () { + const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const [request] = spec.buildRequests([bidRequests[0]], bidderRequestWithUSP); + expect(request.url).to.be.an('string'); + const payload = parseRequestUrl(request.url); + expect(payload).to.have.property('us_privacy', '1YNN'); + }); + }); + + describe('interpretResponse', function () { + const responses = [ + {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'auid': 11, 'h': 250, 'w': 300, dealid: 11}], 'seat': '2'}, + {'bid': [{'price': 0.5, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'auid': 12, 'h': 600, 'w': 300}], 'seat': '2'}, + {'bid': [{'price': 0, 'auid': 3, 'h': 250, 'w': 300}], 'seat': '2'}, + {'bid': [{'price': 0, 'adm': '\n<\/Ad>\n<\/VAST>', 'h': 250, 'w': 300}], 'seat': '2'}, + undefined, + {'bid': [], 'seat': '2'}, + {'seat': '2'}, + ]; + + it('should get correct video bid response', function () { + const bidRequests = [ + { + 'bidder': 'gridNM', + 'params': { + 'source': 'jwp', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': [1, 2, 3, 4, 5, 6] + } + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '659423fff799cb', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + }, + { + 'bidder': 'gridNM', + 'params': { + 'source': 'jwp', + 'video': { + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 3, 4, 5], + 'skip': 1 + } + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '2bc598e42b6a', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + } + ]; + const requests = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '5f2009617a7c0a', + 'cpm': 1.15, + 'creativeId': 11, + 'dealId': 11, + 'width': 300, + 'height': 250, + 'bidderCode': 'gridNM', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': false, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } + }, + { + 'requestId': '5f2009617a7c0a', + 'cpm': 0.5, + 'creativeId': 12, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'bidderCode': 'gridNM', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': false, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } + } + ]; + + requests.forEach((req, i) => { + const result = spec.interpretResponse({'body': {'seatbid': [responses[i]]}}, req); + expect(result[0]).to.deep.equal(expectedResponse[i]); + }); + }); + + it('handles wrong and nobid responses', function () { + responses.slice(2).forEach((resp) => { + const request = spec.buildRequests([{ + 'bidder': 'gridNM', + 'params': { + 'source': 'jwp', + 'video': { + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 3, 4, 5], + 'skip': 1 + } + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '2bc598e42b6a', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + }]); + const result = spec.interpretResponse({'body': {'seatbid': [resp]}}, request[0]); + expect(result.length).to.equal(0); + }); + }); + }); + + describe('user sync', function () { + const syncUrl = getSyncUrl(); + + beforeEach(function () { + resetUserSync(); + }); + + it('should register the Emily iframe', function () { + let syncs = spec.getUserSyncs({ + pixelEnabled: true + }); + + expect(syncs).to.deep.equal({type: 'image', url: syncUrl}); + }); + + it('should not register the Emily iframe more than once', function () { + let syncs = spec.getUserSyncs({ + pixelEnabled: true + }); + expect(syncs).to.deep.equal({type: 'image', url: syncUrl}); + + // when called again, should still have only been called once + syncs = spec.getUserSyncs(); + expect(syncs).to.equal(undefined); + }); + + it('should pass gdpr params if consent is true', function () { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { + gdprApplies: true, consentString: 'foo' + })).to.deep.equal({ + type: 'image', url: `${syncUrl}&gdpr=1&gdpr_consent=foo` + }); + }); + + it('should pass gdpr params if consent is false', function () { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { + gdprApplies: false, consentString: 'foo' + })).to.deep.equal({ + type: 'image', url: `${syncUrl}&gdpr=0&gdpr_consent=foo` + }); + }); + + it('should pass gdpr param gdpr_consent only when gdprApplies is undefined', function () { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { + consentString: 'foo' + })).to.deep.equal({ + type: 'image', url: `${syncUrl}&gdpr_consent=foo` + }); + }); + + it('should pass no params if gdpr consentString is not defined', function () { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {})).to.deep.equal({ + type: 'image', url: syncUrl + }); + }); + + it('should pass no params if gdpr consentString is a number', function () { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { + consentString: 0 + })).to.deep.equal({ + type: 'image', url: syncUrl + }); + }); + + it('should pass no params if gdpr consentString is null', function () { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { + consentString: null + })).to.deep.equal({ + type: 'image', url: syncUrl + }); + }); + + it('should pass no params if gdpr consentString is a object', function () { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { + consentString: {} + })).to.deep.equal({ + type: 'image', url: syncUrl + }); + }); + + it('should pass no params if gdpr is not defined', function () { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined)).to.deep.equal({ + type: 'image', url: syncUrl + }); + }); + + it('should pass usPrivacy param if it is available', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {}, '1YNN')).to.deep.equal({ + type: 'image', url: `${syncUrl}&us_privacy=1YNN` + }); + }); + }); +}); diff --git a/test/spec/modules/vdoaiBidAdapter_spec.js b/test/spec/modules/vdoaiBidAdapter_spec.js index 507f3ffa3ac..2c53647e126 100644 --- a/test/spec/modules/vdoaiBidAdapter_spec.js +++ b/test/spec/modules/vdoaiBidAdapter_spec.js @@ -1,105 +1,105 @@ -import {assert, expect} from 'chai'; -import {spec} from 'modules/vdoaiBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory'; - -const ENDPOINT_URL = 'https://prebid.vdo.ai/auction'; - -describe('vdoaiBidAdapter', function () { - const adapter = newBidder(spec); - describe('isBidRequestValid', function () { - let bid = { - 'bidder': 'vdo.ai', - 'params': { - placementId: 'testPlacementId' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250] - ], - 'bidId': '1234asdf1234', - 'bidderRequestId': '1234asdf1234asdf', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf120' - }; - it('should return true where required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - }); - describe('buildRequests', function () { - let bidRequests = [ - { - 'bidder': 'vdo.ai', - 'params': { - placementId: 'testPlacementId' - }, - 'sizes': [ - [300, 250] - ], - 'bidId': '23beaa6af6cdde', - 'bidderRequestId': '19c0c1efdf37e7', - 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', - } - ]; - - let bidderRequests = { - 'refererInfo': { - 'numIframes': 0, - 'reachedTop': true, - 'referer': 'https://example.com', - 'stack': ['https://example.com'] - } - }; - - const request = spec.buildRequests(bidRequests, bidderRequests); - it('sends bid request to our endpoint via POST', function () { - expect(request[0].method).to.equal('POST'); - }); - it('attaches source and version to endpoint URL as query params', function () { - expect(request[0].url).to.equal(ENDPOINT_URL); - }); - }); - - describe('interpretResponse', function () { - let bidRequest = [ - { - 'method': 'POST', - 'url': ENDPOINT_URL, - 'data': { - 'placementId': 'testPlacementId', - 'width': '300', - 'height': '200', - 'bidId': 'bidId123', - 'referer': 'www.example.com' - } - - } - ]; - let serverResponse = { - body: { - 'vdoCreative': '

I am an ad

', - 'price': 4.2, - 'adid': '12345asdfg', - 'currency': 'EUR', - 'statusMessage': 'Bid available', - 'requestId': 'bidId123', - 'width': 300, - 'height': 250, - 'netRevenue': true - } - }; - it('should get the correct bid response', function () { - let expectedResponse = [{ - 'requestId': 'bidId123', - 'cpm': 4.2, - 'width': 300, - 'height': 250, - 'creativeId': '12345asdfg', - 'currency': 'EUR', - 'netRevenue': true, - 'ttl': 3000, - 'ad': '

I am an ad

' - }]; - let result = spec.interpretResponse(serverResponse, bidRequest[0]); - expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); - }); - }); -}); +import {assert, expect} from 'chai'; +import {spec} from 'modules/vdoaiBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; + +const ENDPOINT_URL = 'https://prebid.vdo.ai/auction'; + +describe('vdoaiBidAdapter', function () { + const adapter = newBidder(spec); + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'vdo.ai', + 'params': { + placementId: 'testPlacementId' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [ + [300, 250] + ], + 'bidId': '1234asdf1234', + 'bidderRequestId': '1234asdf1234asdf', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf120' + }; + it('should return true where required params found', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'vdo.ai', + 'params': { + placementId: 'testPlacementId' + }, + 'sizes': [ + [300, 250] + ], + 'bidId': '23beaa6af6cdde', + 'bidderRequestId': '19c0c1efdf37e7', + 'auctionId': '61466567-d482-4a16-96f0-fe5f25ffbdf1', + } + ]; + + let bidderRequests = { + 'refererInfo': { + 'numIframes': 0, + 'reachedTop': true, + 'referer': 'https://example.com', + 'stack': ['https://example.com'] + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequests); + it('sends bid request to our endpoint via POST', function () { + expect(request[0].method).to.equal('POST'); + }); + it('attaches source and version to endpoint URL as query params', function () { + expect(request[0].url).to.equal(ENDPOINT_URL); + }); + }); + + describe('interpretResponse', function () { + let bidRequest = [ + { + 'method': 'POST', + 'url': ENDPOINT_URL, + 'data': { + 'placementId': 'testPlacementId', + 'width': '300', + 'height': '200', + 'bidId': 'bidId123', + 'referer': 'www.example.com' + } + + } + ]; + let serverResponse = { + body: { + 'vdoCreative': '

I am an ad

', + 'price': 4.2, + 'adid': '12345asdfg', + 'currency': 'EUR', + 'statusMessage': 'Bid available', + 'requestId': 'bidId123', + 'width': 300, + 'height': 250, + 'netRevenue': true + } + }; + it('should get the correct bid response', function () { + let expectedResponse = [{ + 'requestId': 'bidId123', + 'cpm': 4.2, + 'width': 300, + 'height': 250, + 'creativeId': '12345asdfg', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 3000, + 'ad': '

I am an ad

' + }]; + let result = spec.interpretResponse(serverResponse, bidRequest[0]); + expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); + }); + }); +}); From 3ba8fd846c1f5b58c98d77a1c11810615f4dc26c Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Thu, 12 Mar 2020 17:08:03 +0300 Subject: [PATCH 02/57] Updated required params for TheMediaGridNM Bid Adapter --- modules/gridNMBidAdapter.js | 12 +- test/spec/modules/gridNMBidAdapter_spec.js | 121 ++++++++++++++++++--- 2 files changed, 112 insertions(+), 21 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index 550f2392cb4..ba2698fdfab 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -39,14 +39,20 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { - let invalid = !bid.params.source || !utils.isStr(bid.params.source); + let invalid = + !bid.params.source || !utils.isStr(bid.params.source) || + !bid.params.secid || !utils.isStr(bid.params.secid) || + !bid.params.pubid || !utils.isStr(bid.params.pubid); + if (!invalid) { invalid = !bid.params.video || !bid.params.video.protocols || !bid.params.video.mimes; } if (!invalid) { const {protocols, mimes} = bid.params.video; - invalid = !utils.isArray(mimes) || mimes.filter((it) => !(it && utils.isStr(it))).length; - invalid = invalid || !utils.isArray(protocols) || protocols.filter((it) => !(utils.isNumber(it) && it > 0 && !(it % 1))).length; + invalid = !utils.isArray(mimes) || !mimes.length || mimes.filter((it) => !(it && utils.isStr(it))).length; + if (!invalid) { + invalid = !utils.isArray(protocols) || !protocols.length || protocols.filter((it) => !(utils.isNumber(it) && it > 0 && !(it % 1))).length; + } } return !invalid; }, diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index db46dd89015..a7a1c02de5f 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -16,6 +16,8 @@ describe('TheMediaGridNM Adapter', function () { 'bidder': 'gridNM', 'params': { 'source': 'jwp', + 'secid': '11', + 'pubid': '22', 'video': { 'mimes': ['video/mp4', 'video/x-ms-wmv'], 'protocols': [1, 2, 3, 4, 5, 6] @@ -33,28 +35,102 @@ describe('TheMediaGridNM Adapter', function () { }); it('should return false when required params are not passed', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'source': 'jwp', - 'video': { - 'protocols': [1, 2, 3, 4, 5, 6] + const paramsList = [ + { + 'source': 'jwp', + 'secid': '11', + 'pubid': '22', + 'video': { + 'protocols': [1, 2, 3, 4, 5, 6] + } + }, + { + 'source': 'jwp', + 'secid': '11', + 'pubid': '22', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + } + }, + { + 'secid': '11', + 'pubid': '22', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': [1, 2, 3, 4, 5, 6] + } + }, + { + 'source': 'jwp', + 'pubid': '22', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': [1, 2, 3, 4, 5, 6] + } + }, + { + 'source': 'jwp', + 'secid': '11', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': [1, 2, 3, 4, 5, 6] + } } - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); + ]; + paramsList.forEach((params) => { + const invalidBid = Object.assign({}, bid); + delete invalidBid.params; + invalidBid.params = params; + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); }); it('should return false when required params has invalid values', function () { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - 'source': 'jwp', - 'video': { - 'mimes': ['video/mp4', 'video/x-ms-wmv'], - 'protocols': '1,2,3,4,5' + const paramsList = [ + { + 'source': 'jwp', + 'secid': '11', + 'pubid': '22', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': '1,2,3,4,5' + } + }, + { + 'source': 'jwp', + 'secid': '11', + 'pubid': '22', + 'video': { + 'mimes': [1, 2], + 'protocols': [1, 2, 3, 4, 5] + } + }, + { + 'source': 'jwp', + 'secid': 11, + 'pubid': '22', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': [1, 2, 3, 4, 5] + } + }, + { + 'source': 111, + 'secid': '11', + 'pubid': '22', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': [1, 2, 3, 4, 5] + } } - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); + ]; + + paramsList.forEach((params) => { + const invalidBid = Object.assign({}, bid); + delete invalidBid.params; + invalidBid.params = params; + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); }); }); @@ -74,6 +150,8 @@ describe('TheMediaGridNM Adapter', function () { 'bidder': 'gridNM', 'params': { 'source': 'jwp', + 'secid': '11', + 'pubid': '22', 'video': { 'mimes': ['video/mp4', 'video/x-ms-wmv'], 'protocols': [1, 2, 3, 4, 5, 6] @@ -89,7 +167,8 @@ describe('TheMediaGridNM Adapter', function () { 'bidder': 'gridNM', 'params': { 'source': 'jwp', - 'pubid': 22, + 'secid': '11', + 'pubid': '22', 'video': { 'mimes': ['video/mp4'], 'protocols': [1, 2, 3], @@ -170,6 +249,8 @@ describe('TheMediaGridNM Adapter', function () { 'bidder': 'gridNM', 'params': { 'source': 'jwp', + 'secid': '11', + 'pubid': '22', 'video': { 'mimes': ['video/mp4', 'video/x-ms-wmv'], 'protocols': [1, 2, 3, 4, 5, 6] @@ -190,6 +271,8 @@ describe('TheMediaGridNM Adapter', function () { 'bidder': 'gridNM', 'params': { 'source': 'jwp', + 'secid': '11', + 'pubid': '22', 'video': { 'mimes': ['video/mp4'], 'protocols': [1, 2, 3, 4, 5], @@ -258,6 +341,8 @@ describe('TheMediaGridNM Adapter', function () { 'bidder': 'gridNM', 'params': { 'source': 'jwp', + 'secid': '11', + 'pubid': '22', 'video': { 'mimes': ['video/mp4'], 'protocols': [1, 2, 3, 4, 5], From b830678150bf3b3672557d17ca1e9f5cc6c78b3e Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 17 Mar 2020 13:43:45 +0300 Subject: [PATCH 03/57] Update TheMediGridNM Bid Adapter --- modules/gridNMBidAdapter.js | 15 ++++++++------- modules/gridNMBidAdapter.md | 9 +++++++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index ba2698fdfab..bc4a2652f03 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -12,10 +12,8 @@ const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js let hasSynced = false; const LOG_ERROR_MESS = { - noAuid: 'Bid from response has no auid parameter - ', noAdm: 'Bid from response has no adm parameter - ', noPrice: 'Bid from response has no price parameter - ', - noSizes: 'Bid from response has no w or h parameter - ', wrongContentType: 'Bid from response has wrong content_type parameter - ', noBid: 'Array of bid objects is empty', noPlacementCode: 'Can\'t find in requested bids the bid with auid - ', @@ -145,20 +143,23 @@ export const spec = { if (!errorMessage && serverResponse.seatbid) { const serverBid = _getBidFromResponse(serverResponse.seatbid[0]); if (serverBid) { - if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); - else if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); + if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else if (!serverBid.price) errorMessage = LOG_ERROR_MESS.noPrice + JSON.stringify(serverBid); - else if (!serverBid.w || !serverBid.h) errorMessage = LOG_ERROR_MESS.noSizes + JSON.stringify(serverBid); else if (serverBid.content_type !== 'video') errorMessage = LOG_ERROR_MESS.wrongContentType + serverBid.content_type; if (!errorMessage) { const bid = bidRequest.bid; + if (!serverBid.w || !serverBid.h) { + const size = utils.parseSizesInput(bid.sizes)[0].split('x'); + serverBid.w = size[0]; + serverBid.h = size[1]; + } const bidResponse = { - requestId: bid.bidderRequestId, + requestId: bid.bidId, bidderCode: spec.code, cpm: serverBid.price, width: serverBid.w, height: serverBid.h, - creativeId: serverBid.auid, // bid.bidId, + creativeId: serverBid.auid || bid.bidderRequestId, currency: 'USD', netRevenue: false, ttl: TIME_TO_LIVE, diff --git a/modules/gridNMBidAdapter.md b/modules/gridNMBidAdapter.md index e81cb04d3a4..f06c5775064 100644 --- a/modules/gridNMBidAdapter.md +++ b/modules/gridNMBidAdapter.md @@ -14,13 +14,18 @@ Grid bid adapter supports Banner and Video (instream and outstream). var adUnits = [ { code: 'test-div', - sizes: [[728, 90]], - mediaTypes: { video: {} }, + mediaTypes: { + video: { + playerSize: [728, 90] + } + }, bids: [ { bidder: "gridNM", params: { source: 'jwp', + secid: '11', + pubid: '22', video: { mimes: ['video/mp4', 'video/x-ms-wmv'], protocols: [1,2,3,4,5,6] From 9675d0289f7dd7efd3822aff9df328faf2186a85 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 17 Mar 2020 16:59:54 +0300 Subject: [PATCH 04/57] Fix tests for TheMediaGridNM Bid Adapter --- test/spec/modules/gridNMBidAdapter_spec.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index a7a1c02de5f..0dbaac0c526 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -234,9 +234,9 @@ describe('TheMediaGridNM Adapter', function () { describe('interpretResponse', function () { const responses = [ - {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'auid': 11, 'h': 250, 'w': 300, dealid: 11}], 'seat': '2'}, - {'bid': [{'price': 0.5, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'auid': 12, 'h': 600, 'w': 300}], 'seat': '2'}, - {'bid': [{'price': 0, 'auid': 3, 'h': 250, 'w': 300}], 'seat': '2'}, + {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 250, 'w': 300, 'dealid': 11}], 'seat': '2'}, + {'bid': [{'price': 0.5, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 600, 'w': 300}], 'seat': '2'}, + {'bid': [{'price': 0, 'h': 250, 'w': 300}], 'seat': '2'}, {'bid': [{'price': 0, 'adm': '\n<\/Ad>\n<\/VAST>', 'h': 250, 'w': 300}], 'seat': '2'}, undefined, {'bid': [], 'seat': '2'}, @@ -282,7 +282,7 @@ describe('TheMediaGridNM Adapter', function () { 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], 'bidId': '2bc598e42b6a', - 'bidderRequestId': '5f2009617a7c0a', + 'bidderRequestId': '1e8b5a465f404', 'auctionId': '1cbd2feafe5e8b', 'mediaTypes': { 'video': { @@ -294,9 +294,9 @@ describe('TheMediaGridNM Adapter', function () { const requests = spec.buildRequests(bidRequests); const expectedResponse = [ { - 'requestId': '5f2009617a7c0a', + 'requestId': '659423fff799cb', 'cpm': 1.15, - 'creativeId': 11, + 'creativeId': '5f2009617a7c0a', 'dealId': 11, 'width': 300, 'height': 250, @@ -311,9 +311,9 @@ describe('TheMediaGridNM Adapter', function () { } }, { - 'requestId': '5f2009617a7c0a', + 'requestId': '2bc598e42b6a', 'cpm': 0.5, - 'creativeId': 12, + 'creativeId': '1e8b5a465f404', 'dealId': undefined, 'width': 300, 'height': 600, @@ -352,7 +352,7 @@ describe('TheMediaGridNM Adapter', function () { 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], 'bidId': '2bc598e42b6a', - 'bidderRequestId': '5f2009617a7c0a', + 'bidderRequestId': '39d74f5b71464', 'auctionId': '1cbd2feafe5e8b', 'mediaTypes': { 'video': { From 52e593cbad6b272172d2417c3a80be9c53bd4002 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 31 Mar 2020 20:30:49 +0300 Subject: [PATCH 05/57] Fixes after review for TheMediaGridNM Bid Adapter --- modules/gridNMBidAdapter.js | 21 --------------------- modules/gridNMBidAdapter.md | 3 ++- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index bc4a2652f03..7e244f8293c 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -24,9 +24,6 @@ const LOG_ERROR_MESS = { hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' }; -// const KEYS_FOR_PARAMS = ['secid','pubid','pubdata','floorcpm','source','video']; -// const KEYS_FOR_VIDEO = ['mimes','mind','maxd','protocols','size','linearity','skip','skipmin','skipafter','api','startdelay','placement','playbackmethod']; - export const spec = { code: BIDDER_CODE, supportedMediaTypes: [ VIDEO ], @@ -94,24 +91,6 @@ export const spec = { } } - /* const content = {}; - KEYS_FOR_PARAMS.forEach((key) => { - if (params.hasOwnProperty(key) && params[key]) { - if (key === 'video') { - const paramsVideo = params.video; - const video = {}; - KEYS_FOR_VIDEO.forEach((vKey) => { - if (paramsVideo.hasOwnProperty(vKey)) { - video[vKey] = paramsVideo[vKey]; - } - }); - content.video = video; - } else { - content[key] = params[key]; - } - } - }); */ - requests.push({ method: 'POST', url: ENDPOINT_URL + '?' + utils.parseQueryStringParameters(payload).replace(/\&$/, ''), diff --git a/modules/gridNMBidAdapter.md b/modules/gridNMBidAdapter.md index f06c5775064..6decdde7f4c 100644 --- a/modules/gridNMBidAdapter.md +++ b/modules/gridNMBidAdapter.md @@ -16,7 +16,8 @@ Grid bid adapter supports Banner and Video (instream and outstream). code: 'test-div', mediaTypes: { video: { - playerSize: [728, 90] + playerSize: [728, 90], + context: 'outstream' } }, bids: [ From 83fb2b4127232125106c18327fd643c751fc90d7 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 12 May 2020 19:19:39 +0300 Subject: [PATCH 06/57] Add support of multi-format in TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 15 ++++++++++++- test/spec/modules/gridBidAdapter_spec.js | 28 +++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index e19d8cc68a7..4ca631b23e5 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -51,10 +51,23 @@ export const spec = { bids.forEach(bid => { reqId = bid.bidderRequestId; - const {params: {uid}, adUnitCode} = bid; + const {params: {uid}, adUnitCode, mediaTypes} = bid; auids.push(uid); const sizesId = utils.parseSizesInput(bid.sizes); + const addedSizes = {}; + sizesId.forEach((sizeId) => { + addedSizes[sizeId] = true; + }); + const bannerSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'banner.sizes')); + const videoSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'video.playerSize')); + bannerSizesId.concat(videoSizesId).forEach((sizeId) => { + if (!addedSizes[sizeId]) { + addedSizes[sizeId] = true; + sizesId.push(sizeId); + } + }); + if (!slotsMapByUid[uid]) { slotsMapByUid[uid] = {}; } diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 26ab27f5273..4f53d9dcb25 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -68,6 +68,14 @@ describe('TheMediaGrid Adapter', function () { }, 'adUnitCode': 'adunit-code-2', 'sizes': [[728, 90]], + 'mediaTypes': { + 'video': { + 'playerSize': [400, 600] + }, + 'banner': { + 'sizes': [[728, 90]] + } + }, 'bidId': '3150ccb55da321', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', @@ -79,6 +87,14 @@ describe('TheMediaGrid Adapter', function () { }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], + 'mediaTypes': { + 'video': { + 'playerSize': [400, 600] + }, + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, 'bidId': '42dbe3a7168a6a', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', @@ -97,13 +113,23 @@ describe('TheMediaGrid Adapter', function () { expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); }); + it('sizes must be added from mediaTypes', function () { + const request = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('auids', '1,1'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90,400x600'); + expect(payload).to.have.property('r', '22edbae2733bf6'); + }); + it('sizes must not be duplicated', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); expect(payload).to.have.property('auids', '1,1,2'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90'); + expect(payload).to.have.property('sizes', '300x250,300x600,728x90,400x600'); expect(payload).to.have.property('r', '22edbae2733bf6'); }); From 66f2ac8ca587c02ef37f0b842a0882bd36c8994f Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 26 May 2020 19:00:07 +0300 Subject: [PATCH 07/57] Update sync url for grid and gridNM Bid Adapters --- modules/gridBidAdapter.js | 2 +- modules/gridNMBidAdapter.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 4ca631b23e5..3a78a5fcf20 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -5,7 +5,7 @@ import { VIDEO, BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hb'; -const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=iow_labs'; +const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index 7e244f8293c..ffd6c1b250c 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -5,7 +5,7 @@ import { VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'gridNM'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbnm'; -const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=iponweblabs'; +const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; From 4fcf52289cd4a0ceb248f87fdfe896acc66e0d3d Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 9 Jun 2020 18:23:01 +0300 Subject: [PATCH 08/57] TheMediaGrid Bid Adapter: added keywords adUnit parameter --- modules/gridBidAdapter.js | 24 ++++++++++++ modules/gridBidAdapter.md | 8 +++- test/spec/modules/gridBidAdapter_spec.js | 49 ++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 3a78a5fcf20..308100ede71 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -47,6 +47,7 @@ export const spec = { const slotsMapByUid = {}; const sizeMap = {}; const bids = validBidRequests || []; + let pageKeywords; let reqId; bids.forEach(bid => { @@ -55,6 +56,15 @@ export const spec = { auids.push(uid); const sizesId = utils.parseSizesInput(bid.sizes); + if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { + const keywords = utils.transformBidderParamKeywords(bid.params.keywords); + + if (keywords.length > 0) { + keywords.forEach(deleteValues); + } + pageKeywords = keywords; + } + const addedSizes = {}; sizesId.forEach((sizeId) => { addedSizes[sizeId] = true; @@ -102,6 +112,10 @@ export const spec = { wrapperVersion: '$prebid.version$' }; + if (pageKeywords) { + payload.keywords = JSON.stringify(pageKeywords); + } + if (bidderRequest) { if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { payload.u = bidderRequest.refererInfo.referer; @@ -180,6 +194,16 @@ export const spec = { } }; +function isPopulatedArray(arr) { + return !!(utils.isArray(arr) && arr.length > 0); +} + +function deleteValues(keyPairObj) { + if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { + delete keyPairObj.value; + } +} + function _getBidFromResponse(respItem) { if (!respItem) { utils.logError(LOG_ERROR_MESS.emptySeatbid); diff --git a/modules/gridBidAdapter.md b/modules/gridBidAdapter.md index 4720ee3d808..77b9bbf0f36 100644 --- a/modules/gridBidAdapter.md +++ b/modules/gridBidAdapter.md @@ -32,7 +32,11 @@ Grid bid adapter supports Banner and Video (instream and outstream). bidder: "grid", params: { uid: 2, - priceType: 'gross' + priceType: 'gross', + keywords: { + brandsafety: ['disaster'], + topic: ['stress', 'fear'] + } } } ] @@ -51,4 +55,4 @@ Grid bid adapter supports Banner and Video (instream and outstream). ] } ]; -``` \ No newline at end of file +``` diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 4f53d9dcb25..2d33cde15ba 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -165,6 +165,55 @@ describe('TheMediaGrid Adapter', function () { const payload = parseRequest(request.data); expect(payload).to.have.property('us_privacy', '1YNN'); }); + + it('should convert keyword params to proper form and attaches to request', function () { + const bidRequestWithKeywords = [].concat(bidRequests); + bidRequestWithKeywords[1] = Object.assign({}, + bidRequests[1], + { + params: { + uid: '1', + keywords: { + single: 'val', + singleArr: ['val'], + singleArrNum: [3], + multiValMixed: ['value1', 2, 'value3'], + singleValNum: 123, + emptyStr: '', + emptyArr: [''], + badValue: {'foo': 'bar'} // should be dropped + } + } + } + ); + + const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.keywords).to.be.an('string'); + payload.keywords = JSON.parse(payload.keywords); + + expect(payload.keywords).to.deep.equal([{ + 'key': 'single', + 'value': ['val'] + }, { + 'key': 'singleArr', + 'value': ['val'] + }, { + 'key': 'singleArrNum', + 'value': ['3'] + }, { + 'key': 'multiValMixed', + 'value': ['value1', '2', 'value3'] + }, { + 'key': 'singleValNum', + 'value': ['123'] + }, { + 'key': 'emptyStr' + }, { + 'key': 'emptyArr' + }]); + }); }); describe('interpretResponse', function () { From bfe4f5ee0d1a7677369e5fc4d06ef79fab4c3bac Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 16 Jun 2020 00:20:55 +0300 Subject: [PATCH 09/57] Update TheMediaGrid Bid Adapter to support keywords from config --- modules/gridBidAdapter.js | 23 ++++++++---- test/spec/modules/gridBidAdapter_spec.js | 46 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 308100ede71..d18effa349b 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -2,6 +2,7 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hb'; @@ -47,7 +48,7 @@ export const spec = { const slotsMapByUid = {}; const sizeMap = {}; const bids = validBidRequests || []; - let pageKeywords; + let pageKeywords = null; let reqId; bids.forEach(bid => { @@ -57,12 +58,7 @@ export const spec = { const sizesId = utils.parseSizesInput(bid.sizes); if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { - const keywords = utils.transformBidderParamKeywords(bid.params.keywords); - - if (keywords.length > 0) { - keywords.forEach(deleteValues); - } - pageKeywords = keywords; + pageKeywords = utils.transformBidderParamKeywords(bid.params.keywords); } const addedSizes = {}; @@ -104,6 +100,19 @@ export const spec = { }); }); + const configKeywords = utils.transformBidderParamKeywords({ + 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, + 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null + }); + + if (configKeywords.length) { + pageKeywords = (pageKeywords || []).concat(configKeywords); + } + + if (pageKeywords && pageKeywords.length > 0) { + pageKeywords.forEach(deleteValues); + } + const payload = { auids: auids.join(','), sizes: utils.getKeys(sizeMap).join(','), diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 2d33cde15ba..c8d04113aeb 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec, resetUserSync, getSyncUrl } from 'modules/gridBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; describe('TheMediaGrid Adapter', function () { const adapter = newBidder(spec); @@ -214,6 +215,51 @@ describe('TheMediaGrid Adapter', function () { 'key': 'emptyArr' }]); }); + + it('should mix keyword param with keywords from config', function () { + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'fpd.user' ? {'keywords': ['a', 'b']} : arg === 'fpd.context' ? {'keywords': ['any words']} : null); + + const bidRequestWithKeywords = [].concat(bidRequests); + bidRequestWithKeywords[1] = Object.assign({}, + bidRequests[1], + { + params: { + uid: '1', + keywords: { + single: 'val', + singleArr: ['val'], + multiValMixed: ['value1', 2, 'value3'] + } + } + } + ); + + const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.keywords).to.be.an('string'); + payload.keywords = JSON.parse(payload.keywords); + + expect(payload.keywords).to.deep.equal([{ + 'key': 'single', + 'value': ['val'] + }, { + 'key': 'singleArr', + 'value': ['val'] + }, { + 'key': 'multiValMixed', + 'value': ['value1', '2', 'value3'] + }, { + 'key': 'user', + 'value': ['a', 'b'] + }, { + 'key': 'context', + 'value': ['any words'] + }]); + + getConfigStub.restore(); + }); }); describe('interpretResponse', function () { From 64b5b8d2fe15612606057589514a79273cafa118 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Fri, 10 Jul 2020 16:34:49 +0300 Subject: [PATCH 10/57] Implement new request format for TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 543 ++++++++++++++++------- test/spec/modules/gridBidAdapter_spec.js | 301 ++++++++++++- 2 files changed, 669 insertions(+), 175 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index d18effa349b..1bd3cc822fb 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -6,6 +6,7 @@ import {config} from '../src/config.js'; const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hb'; +const NEW_ENDPOINT_URL = 'http://dev.verona.iponweb.net/hbjson'; const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -40,117 +41,22 @@ export const spec = { * * @param {BidRequest[]} validBidRequests - an array of bids * @param {bidderRequest} bidderRequest bidder request object - * @return ServerRequest Info describing the request to the server. + * @return {ServerRequest[]} Info describing the request to the server. */ buildRequests: function(validBidRequests, bidderRequest) { - const auids = []; - const bidsMap = {}; - const slotsMapByUid = {}; - const sizeMap = {}; - const bids = validBidRequests || []; - let pageKeywords = null; - let reqId; - - bids.forEach(bid => { - reqId = bid.bidderRequestId; - const {params: {uid}, adUnitCode, mediaTypes} = bid; - auids.push(uid); - const sizesId = utils.parseSizesInput(bid.sizes); - - if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { - pageKeywords = utils.transformBidderParamKeywords(bid.params.keywords); - } - - const addedSizes = {}; - sizesId.forEach((sizeId) => { - addedSizes[sizeId] = true; - }); - const bannerSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'banner.sizes')); - const videoSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'video.playerSize')); - bannerSizesId.concat(videoSizesId).forEach((sizeId) => { - if (!addedSizes[sizeId]) { - addedSizes[sizeId] = true; - sizesId.push(sizeId); - } - }); - - if (!slotsMapByUid[uid]) { - slotsMapByUid[uid] = {}; - } - const slotsMap = slotsMapByUid[uid]; - if (!slotsMap[adUnitCode]) { - slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; - } else { - slotsMap[adUnitCode].bids.push(bid); - } - const slot = slotsMap[adUnitCode]; - - sizesId.forEach((sizeId) => { - sizeMap[sizeId] = true; - if (!bidsMap[uid]) { - bidsMap[uid] = {}; - } - - if (!bidsMap[uid][sizeId]) { - bidsMap[uid][sizeId] = [slot]; - } else { - bidsMap[uid][sizeId].push(slot); - } - slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); - }); - }); - - const configKeywords = utils.transformBidderParamKeywords({ - 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, - 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null + const oldFormatBids = []; + const newFormatBids = []; + validBidRequests.forEach((bid) => { + bid.params.useNewFormat ? newFormatBids.push(bid) : oldFormatBids.push(bid); }); - - if (configKeywords.length) { - pageKeywords = (pageKeywords || []).concat(configKeywords); - } - - if (pageKeywords && pageKeywords.length > 0) { - pageKeywords.forEach(deleteValues); - } - - const payload = { - auids: auids.join(','), - sizes: utils.getKeys(sizeMap).join(','), - r: reqId, - wrapperType: 'Prebid_js', - wrapperVersion: '$prebid.version$' - }; - - if (pageKeywords) { - payload.keywords = JSON.stringify(pageKeywords); + const requests = []; + if (newFormatBids.length) { + requests.push(buildNewRequest(newFormatBids, bidderRequest)); } - - if (bidderRequest) { - if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { - payload.u = bidderRequest.refererInfo.referer; - } - if (bidderRequest.timeout) { - payload.wtimeout = bidderRequest.timeout; - } - if (bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - payload.gdpr_consent = bidderRequest.gdprConsent.consentString; - } - payload.gdpr_applies = - (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') - ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; - } - if (bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent; - } + if (oldFormatBids.length) { + requests.push(buildOldRequest(oldFormatBids, bidderRequest)); } - - return { - method: 'GET', - url: ENDPOINT_URL, - data: utils.parseQueryStringParameters(payload).replace(/\&$/, ''), - bidsMap: bidsMap, - }; + return requests; }, /** * Unpack the response from the server into a list of bids. @@ -162,7 +68,6 @@ export const spec = { interpretResponse: function(serverResponse, bidRequest) { serverResponse = serverResponse && serverResponse.body; const bidResponses = []; - const bidsMap = bidRequest.bidsMap; let errorMessage; @@ -173,7 +78,7 @@ export const spec = { if (!errorMessage && serverResponse.seatbid) { serverResponse.seatbid.forEach(respItem => { - _addBidResponse(_getBidFromResponse(respItem), bidsMap, bidResponses); + _addBidResponse(_getBidFromResponse(respItem), bidRequest, bidResponses); }); } if (errorMessage) utils.logError(errorMessage); @@ -224,68 +129,76 @@ function _getBidFromResponse(respItem) { return respItem && respItem.bid && respItem.bid[0]; } -function _addBidResponse(serverBid, bidsMap, bidResponses) { +function _addBidResponse(serverBid, bidRequest, bidResponses) { if (!serverBid) return; let errorMessage; if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else { - const awaitingBids = bidsMap[serverBid.auid]; - if (awaitingBids) { - const sizeId = `${serverBid.w}x${serverBid.h}`; - if (awaitingBids[sizeId]) { - const slot = awaitingBids[sizeId][0]; - - const bid = slot.bids.shift(); - - const bidResponse = { - requestId: bid.bidId, // bid.bidderRequestId, - bidderCode: spec.code, - cpm: serverBid.price, - width: serverBid.w, - height: serverBid.h, - creativeId: serverBid.auid, // bid.bidId, - currency: 'USD', - netRevenue: false, - ttl: TIME_TO_LIVE, - dealId: serverBid.dealid - }; - - if (serverBid.content_type === 'video') { - bidResponse.vastXml = serverBid.adm; - bidResponse.mediaType = VIDEO; - bidResponse.adResponse = { - content: bidResponse.vastXml - }; - if (!bid.renderer && (!bid.mediaTypes || !bid.mediaTypes.video || bid.mediaTypes.video.context === 'outstream')) { - bidResponse.renderer = createRenderer(bidResponse, { - id: bid.bidId, - url: RENDERER_URL - }); - } - } else { - bidResponse.ad = serverBid.adm; - bidResponse.mediaType = BANNER; + let bid = null; + let slot = null; + const bidsMap = bidRequest.bidsMap; + if (bidRequest.newFormat) { + bid = bidsMap[serverBid.impid]; + } else { + const awaitingBids = bidsMap[serverBid.auid]; + if (awaitingBids) { + const sizeId = `${serverBid.w}x${serverBid.h}`; + if (awaitingBids[sizeId]) { + slot = awaitingBids[sizeId][0]; + bid = slot.bids.shift(); } - bidResponses.push(bidResponse); + } else { + errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; + } + } - if (!slot.bids.length) { - slot.parents.forEach(({parent, key, uid}) => { - const index = parent[key].indexOf(slot); - if (index > -1) { - parent[key].splice(index, 1); - } - if (!parent[key].length) { - delete parent[key]; - if (!utils.getKeys(parent).length) { - delete bidsMap[uid]; - } - } + if (bid) { + const bidResponse = { + requestId: bid.bidId, // bid.bidderRequestId, + bidderCode: spec.code, + cpm: serverBid.price, + width: serverBid.w, + height: serverBid.h, + creativeId: serverBid.auid, // bid.bidId, + currency: 'USD', + netRevenue: false, + ttl: TIME_TO_LIVE, + dealId: serverBid.dealid + }; + + if (serverBid.content_type === 'video') { + bidResponse.vastXml = serverBid.adm; + bidResponse.mediaType = VIDEO; + bidResponse.adResponse = { + content: bidResponse.vastXml + }; + if (!bid.renderer && (!bid.mediaTypes || !bid.mediaTypes.video || bid.mediaTypes.video.context === 'outstream')) { + bidResponse.renderer = createRenderer(bidResponse, { + id: bid.bidId, + url: RENDERER_URL }); } + } else { + bidResponse.ad = serverBid.adm; + bidResponse.mediaType = BANNER; + } + bidResponses.push(bidResponse); + + if (slot && !slot.bids.length) { + slot.parents.forEach(({parent, key, uid}) => { + const index = parent[key].indexOf(slot); + if (index > -1) { + parent[key].splice(index, 1); + } + if (!parent[key].length) { + delete parent[key]; + if (!utils.getKeys(parent).length) { + delete bidsMap[uid]; + } + } + }); } - } else { - errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; } } if (errorMessage) { @@ -293,6 +206,316 @@ function _addBidResponse(serverBid, bidsMap, bidResponses) { } } +function buildOldRequest(validBidRequests, bidderRequest) { + const auids = []; + const bidsMap = {}; + const slotsMapByUid = {}; + const sizeMap = {}; + const bids = validBidRequests || []; + let pageKeywords = null; + let reqId; + + bids.forEach(bid => { + reqId = bid.bidderRequestId; + const {params: {uid}, adUnitCode, mediaTypes} = bid; + auids.push(uid); + const sizesId = utils.parseSizesInput(bid.sizes); + + if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { + pageKeywords = utils.transformBidderParamKeywords(bid.params.keywords); + } + + const addedSizes = {}; + sizesId.forEach((sizeId) => { + addedSizes[sizeId] = true; + }); + const bannerSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'banner.sizes')); + const videoSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'video.playerSize')); + bannerSizesId.concat(videoSizesId).forEach((sizeId) => { + if (!addedSizes[sizeId]) { + addedSizes[sizeId] = true; + sizesId.push(sizeId); + } + }); + + if (!slotsMapByUid[uid]) { + slotsMapByUid[uid] = {}; + } + const slotsMap = slotsMapByUid[uid]; + if (!slotsMap[adUnitCode]) { + slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; + } else { + slotsMap[adUnitCode].bids.push(bid); + } + const slot = slotsMap[adUnitCode]; + + sizesId.forEach((sizeId) => { + sizeMap[sizeId] = true; + if (!bidsMap[uid]) { + bidsMap[uid] = {}; + } + + if (!bidsMap[uid][sizeId]) { + bidsMap[uid][sizeId] = [slot]; + } else { + bidsMap[uid][sizeId].push(slot); + } + slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); + }); + }); + + const configKeywords = utils.transformBidderParamKeywords({ + 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, + 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null + }); + + if (configKeywords.length) { + pageKeywords = (pageKeywords || []).concat(configKeywords); + } + + if (pageKeywords && pageKeywords.length > 0) { + pageKeywords.forEach(deleteValues); + } + + const payload = { + auids: auids.join(','), + sizes: utils.getKeys(sizeMap).join(','), + r: reqId, + wrapperType: 'Prebid_js', + wrapperVersion: '$prebid.version$' + }; + + if (pageKeywords) { + payload.keywords = JSON.stringify(pageKeywords); + } + + if (bidderRequest) { + if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { + payload.u = bidderRequest.refererInfo.referer; + } + if (bidderRequest.timeout) { + payload.wtimeout = bidderRequest.timeout; + } + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.consentString) { + payload.gdpr_consent = bidderRequest.gdprConsent.consentString; + } + payload.gdpr_applies = + (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') + ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; + } + if (bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent; + } + } + + return { + method: 'GET', + url: ENDPOINT_URL, + data: utils.parseQueryStringParameters(payload).replace(/\&$/, ''), + bidsMap: bidsMap + } +} + +function buildNewRequest(validBidRequests, bidderRequest) { + let pageKeywords = null; + let jwpseg = null; + let schain = null; + let userId = null; + let user = null; + let userExt = null; + let {bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo} = bidderRequest; + + const referer = refererInfo ? encodeURIComponent(refererInfo.referer) : ''; + const imp = []; + const bidsMap = {}; + + validBidRequests.forEach((bid) => { + if (!bidderRequestId) { + bidderRequestId = bid.bidderRequestId; + } + if (!auctionId) { + auctionId = bid.auctionId; + } + if (!schain) { + schain = bid.schain; + } + if (!userId) { + userId = bid.userId; + } + const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode} = bid; + bidsMap[bidId] = bid; + if (!pageKeywords && !utils.isEmpty(keywords)) { + pageKeywords = utils.transformBidderParamKeywords(keywords); + } + if (!jwpseg && keywords && keywords.jwpseg) { + jwpseg = keywords.jwpseg; + } + let impObj = { + id: bidId, + tagid: uid.toString(), + ext: { + divid: adUnitCode + } + }; + + if (!mediaTypes || mediaTypes[BANNER]) { + const banner = createBannerRequest(bid, mediaTypes ? mediaTypes[BANNER] : {}); + if (banner) { + impObj.banner = banner; + } + } + if (mediaTypes && mediaTypes[VIDEO]) { + const video = createVideoRequest(bid, mediaTypes[VIDEO]); + if (video) { + impObj.video = video; + } + } + + if (impObj.banner || impObj.video) { + imp.push(impObj); + } + }); + + const source = { + tid: auctionId, + ext: { + wrapper: 'Prebid_js', + wrapper_version: '$prebid.version$' + } + }; + + if (schain) { + source.ext.schain = schain; + } + + const tmax = config.getConfig('bidderTimeout') || timeout; + + let request = { + id: bidderRequestId, + site: { + page: referer + }, + tmax, + source, + imp + }; + + if (jwpseg && jwpseg.length) { + user = { + data: [{ + name: 'iow_labs_pub_data', + segment: jwpseg.map((seg) => { + return {name: 'jwpseg', value: seg}; + }) + }] + }; + } + + if (gdprConsent && gdprConsent.consentString) { + userExt = {consent: gdprConsent.consentString}; + } + + if (userId) { + userExt = userExt || {}; + if (userId.tdid) { + userExt.unifiedid = userId.tdid; + } + if (userId.id5id) { + userExt.id5id = userId.id5id; + } + if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { + userExt.digitrustid = userId.digitrustid.data.id; + } + if (userId.lipb && userId.lipb.lipbid) { + userExt.liveintentid = userId.lipb.lipbid; + } + } + + if (userExt && Object.keys(userExt).length) { + user = user || {}; + user.ext = userExt; + } + + if (user) { + request.user = user; + } + + const configKeywords = utils.transformBidderParamKeywords({ + 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, + 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null + }); + + if (configKeywords.length) { + pageKeywords = (pageKeywords || []).concat(configKeywords); + } + + if (pageKeywords && pageKeywords.length > 0) { + pageKeywords.forEach(deleteValues); + } + + if (pageKeywords) { + request.ext = { + keywords: pageKeywords + }; + } + + if (gdprConsent && gdprConsent.gdprApplies) { + request.regs = { + ext: { + gdpr: !!gdprConsent.gdprApplies + } + } + } + + if (uspConsent) { + if (!request.regs) { + request.regs = {ext: {}}; + } + request.regs.ext.us_privacy = uspConsent; + } + + return { + method: 'POST', + url: NEW_ENDPOINT_URL, + data: JSON.stringify(request), + newFormat: true, + bidsMap + }; +} + +function createVideoRequest(bid, mediaType) { + const {playerSize, mimes, durationRangeSec} = mediaType; + const size = (playerSize || bid.sizes || [])[0]; + if (!size) return; + + let result = utils.parseGPTSingleSizeArrayToRtbSize(size); + + if (mimes) { + result.mimes = mimes; + } + + if (durationRangeSec && durationRangeSec.length === 2) { + result.minduration = durationRangeSec[0]; + result.maxduration = durationRangeSec[1]; + } + + return result; +} + +function createBannerRequest(bid, mediaType) { + const sizes = mediaType.sizes || bid.sizes; + if (!sizes || !sizes.length) return; + + let format = sizes.map((size) => utils.parseGPTSingleSizeArrayToRtbSize(size)); + let result = utils.parseGPTSingleSizeArrayToRtbSize(sizes[0]); + + if (format.length) { + result.format = format + } + return result; +} + function outstreamRender (bid) { bid.renderer.push(() => { window.ANOutstreamVideo.renderAd({ diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index c8d04113aeb..b221d9e58c8 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -103,7 +103,7 @@ describe('TheMediaGrid Adapter', function () { ]; it('should attach valid params to the tag', function () { - const request = spec.buildRequests([bidRequests[0]], bidderRequest); + const [request] = spec.buildRequests([bidRequests[0]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -115,7 +115,7 @@ describe('TheMediaGrid Adapter', function () { }); it('sizes must be added from mediaTypes', function () { - const request = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); + const [request] = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -125,7 +125,7 @@ describe('TheMediaGrid Adapter', function () { }); it('sizes must not be duplicated', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); + const [request] = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -135,7 +135,7 @@ describe('TheMediaGrid Adapter', function () { }); it('if gdprConsent is present payload must have gdpr params', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); + const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('u', referrer); @@ -144,7 +144,7 @@ describe('TheMediaGrid Adapter', function () { }); it('if gdprApplies is false gdpr_applies must be 0', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); + const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); @@ -152,7 +152,7 @@ describe('TheMediaGrid Adapter', function () { }); it('if gdprApplies is undefined gdpr_applies must be 1', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); + const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('gdpr_consent', 'AAA'); @@ -161,7 +161,7 @@ describe('TheMediaGrid Adapter', function () { it('if usPrivacy is present payload must have us_privacy param', function () { const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); + const [request] = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('us_privacy', '1YNN'); @@ -188,7 +188,7 @@ describe('TheMediaGrid Adapter', function () { } ); - const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest); + const [request] = spec.buildRequests(bidRequestWithKeywords, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload.keywords).to.be.an('string'); @@ -235,7 +235,7 @@ describe('TheMediaGrid Adapter', function () { } ); - const request = spec.buildRequests(bidRequestWithKeywords, bidderRequest); + const [request] = spec.buildRequests(bidRequestWithKeywords, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload.keywords).to.be.an('string'); @@ -262,6 +262,277 @@ describe('TheMediaGrid Adapter', function () { }); }); + describe('buildRequests in new format', function () { + function parseRequest(data) { + return JSON.parse(data); + } + const bidderRequest = { + refererInfo: {referer: 'https://example.com'}, + bidderRequestId: '22edbae2733bf6', + auctionId: '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + timeout: 3000 + }; + const referrer = encodeURIComponent(bidderRequest.refererInfo.referer); + let bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '1', + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [300, 600]] + } + }, + 'bidId': '42dbe3a7168a6a', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '2', + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '11', + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'mediaTypes': { + 'video': { + 'playerSize': [[400, 600]], + 'mimes': ['video/mp4', 'video/webm', 'application/javascript', 'video/ogg'] + } + }, + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '3', + 'useNewFormat': true + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'mediaTypes': { + 'video': { + 'playerSize': [[400, 600]] + }, + 'banner': { + 'sizes': [[728, 90]] + } + }, + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '9e2dfbfe-00c7-4f5e-9850-4044df3229c7', + } + ]; + + it('should attach valid params to the tag', function () { + const [request] = spec.buildRequests([bidRequests[0]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.deep.equal({ + 'id': bidderRequest.bidderRequestId, + 'site': { + 'page': referrer + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': bidderRequest.auctionId, + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }] + }); + }); + + it('make possible to process request without mediaTypes', function () { + const [request] = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.deep.equal({ + 'id': bidderRequest.bidderRequestId, + 'site': { + 'page': referrer + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': bidderRequest.auctionId, + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[1].bidId, + 'tagid': bidRequests[1].params.uid, + 'ext': {'divid': bidRequests[1].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }] + }); + }); + + it('should attach valid params to the video tag', function () { + const [request] = spec.buildRequests(bidRequests.slice(0, 3), bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.deep.equal({ + 'id': bidderRequest.bidderRequestId, + 'site': { + 'page': referrer + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': bidderRequest.auctionId, + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[1].bidId, + 'tagid': bidRequests[1].params.uid, + 'ext': {'divid': bidRequests[1].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[2].bidId, + 'tagid': bidRequests[2].params.uid, + 'ext': {'divid': bidRequests[2].adUnitCode}, + 'video': { + 'w': 400, + 'h': 600, + 'mimes': ['video/mp4', 'video/webm', 'application/javascript', 'video/ogg'] + } + }] + }); + }); + + it('should support mixed mediaTypes', function () { + const [request] = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.deep.equal({ + 'id': bidderRequest.bidderRequestId, + 'site': { + 'page': referrer + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': bidderRequest.auctionId, + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'imp': [{ + 'id': bidRequests[0].bidId, + 'tagid': bidRequests[0].params.uid, + 'ext': {'divid': bidRequests[0].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[1].bidId, + 'tagid': bidRequests[1].params.uid, + 'ext': {'divid': bidRequests[1].adUnitCode}, + 'banner': { + 'w': 300, + 'h': 250, + 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + } + }, { + 'id': bidRequests[2].bidId, + 'tagid': bidRequests[2].params.uid, + 'ext': {'divid': bidRequests[2].adUnitCode}, + 'video': { + 'w': 400, + 'h': 600, + 'mimes': ['video/mp4', 'video/webm', 'application/javascript', 'video/ogg'], + } + }, { + 'id': bidRequests[3].bidId, + 'tagid': bidRequests[3].params.uid, + 'ext': {'divid': bidRequests[3].adUnitCode}, + 'banner': { + 'w': 728, + 'h': 90, + 'format': [{'w': 728, 'h': 90}] + }, + 'video': { + 'w': 400, + 'h': 600 + } + }] + }); + }); + + it('if gdprConsent is present payload must have gdpr params', function () { + const gdprBidderRequest = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); + const [request] = spec.buildRequests(bidRequests, gdprBidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('user'); + expect(payload.user).to.have.property('ext'); + expect(payload.user.ext).to.have.property('consent', 'AAA'); + expect(payload).to.have.property('regs'); + expect(payload.regs).to.have.property('ext'); + expect(payload.regs.ext).to.have.property('gdpr', true); + }); + + it('if usPrivacy is present payload must have us_privacy param', function () { + const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const [request] = spec.buildRequests(bidRequests, bidderRequestWithUSP); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('regs'); + expect(payload.regs).to.have.property('ext'); + expect(payload.regs.ext).to.have.property('us_privacy', '1YNN'); + }); + }); + describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, @@ -288,7 +559,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1cbd2feafe5e8b', } ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '659423fff799cb', @@ -346,7 +617,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1fa09aee5c8c99', } ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '300bfeb0d71a5b', @@ -435,7 +706,7 @@ describe('TheMediaGrid Adapter', function () { {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 11, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 12, content_type: 'video'}], 'seat': '2'} ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '659423fff799cb', @@ -496,7 +767,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1fa09aee5c84d34', } ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); expect(result.length).to.equal(0); }); @@ -566,7 +837,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '32a1f276cb87cb8', } ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '2164be6358b9', @@ -670,7 +941,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '35bcbc0f7e79c', } ]; - const request = spec.buildRequests(bidRequests); + const [request] = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '5126e301f4be', From faa9bfb1b7cb5742ce0bc2da30579f42ed202059 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 27 Jul 2020 19:14:32 +0300 Subject: [PATCH 11/57] Fix jwpseg params for TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 1bd3cc822fb..b4b741ac783 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -6,7 +6,7 @@ import {config} from '../src/config.js'; const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hb'; -const NEW_ENDPOINT_URL = 'http://dev.verona.iponweb.net/hbjson'; +const NEW_ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -320,6 +320,7 @@ function buildOldRequest(validBidRequests, bidderRequest) { function buildNewRequest(validBidRequests, bidderRequest) { let pageKeywords = null; let jwpseg = null; + let content = null; let schain = null; let userId = null; let user = null; @@ -343,13 +344,18 @@ function buildNewRequest(validBidRequests, bidderRequest) { if (!userId) { userId = bid.userId; } - const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode} = bid; + const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, realTimeData} = bid; bidsMap[bidId] = bid; if (!pageKeywords && !utils.isEmpty(keywords)) { pageKeywords = utils.transformBidderParamKeywords(keywords); } - if (!jwpseg && keywords && keywords.jwpseg) { - jwpseg = keywords.jwpseg; + if (realTimeData && realTimeData.jwTargeting) { + if (!jwpseg && realTimeData.jwTargeting.segments) { + jwpseg = realTimeData.segments; + } + if (!content && realTimeData.content) { + content = realTimeData.content; + } } let impObj = { id: bidId, @@ -401,6 +407,10 @@ function buildNewRequest(validBidRequests, bidderRequest) { imp }; + if (content) { + request.site.content = content; + } + if (jwpseg && jwpseg.length) { user = { data: [{ @@ -463,7 +473,7 @@ function buildNewRequest(validBidRequests, bidderRequest) { if (gdprConsent && gdprConsent.gdprApplies) { request.regs = { ext: { - gdpr: !!gdprConsent.gdprApplies + gdpr: gdprConsent.gdprApplies ? 1 : 0 } } } From dc645c7e9e27bcfe2b6133ce32aad75deb4361be Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Wed, 29 Jul 2020 19:18:15 +0300 Subject: [PATCH 12/57] Update unit tests for The Media Grid Bid Adapter --- test/spec/modules/gridBidAdapter_spec.js | 49 +++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index b221d9e58c8..344f1764c05 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -519,7 +519,7 @@ describe('TheMediaGrid Adapter', function () { expect(payload.user.ext).to.have.property('consent', 'AAA'); expect(payload).to.have.property('regs'); expect(payload.regs).to.have.property('ext'); - expect(payload.regs.ext).to.have.property('gdpr', true); + expect(payload.regs.ext).to.have.property('gdpr', 1); }); it('if usPrivacy is present payload must have us_privacy param', function () { @@ -531,6 +531,53 @@ describe('TheMediaGrid Adapter', function () { expect(payload.regs).to.have.property('ext'); expect(payload.regs.ext).to.have.property('us_privacy', '1YNN'); }); + + it('if userId is present payload must have user.ext param with right keys', function () { + const bidRequestsWithUserIds = bidRequests.map((bid) => { + return Object.assign({ + userId: { + id5id: 'id5id_1', + tdid: 'tdid_1', + digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, + lipb: {lipbid: 'lipb_1'} + } + }, bid); + }); + const [request] = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('user'); + expect(payload.user).to.have.property('ext'); + expect(payload.user.ext).to.have.property('unifiedid', 'tdid_1'); + expect(payload.user.ext).to.have.property('id5id', 'id5id_1'); + expect(payload.user.ext).to.have.property('digitrustid', 'DTID'); + expect(payload.user.ext).to.have.property('liveintentid', 'lipb_1'); + }); + + it('if schain is present payload must have source.ext.schain param', function () { + const schain = { + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + hp: 1 + } + ] + }; + const bidRequestsWithSChain = bidRequests.map((bid) => { + return Object.assign({ + schain: schain + }, bid); + }); + const [request] = spec.buildRequests(bidRequestsWithSChain, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('source'); + expect(payload.source).to.have.property('ext'); + expect(payload.source.ext).to.have.property('schain'); + expect(payload.source.ext.schain).to.deep.equal(schain); + }); }); describe('interpretResponse', function () { From 340cae4dc6ca2696085874acbd2ff3e93c54bf96 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 10 Aug 2020 18:58:03 +0300 Subject: [PATCH 13/57] Fix typo in TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index b4b741ac783..8b909f1ee3d 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -353,8 +353,8 @@ function buildNewRequest(validBidRequests, bidderRequest) { if (!jwpseg && realTimeData.jwTargeting.segments) { jwpseg = realTimeData.segments; } - if (!content && realTimeData.content) { - content = realTimeData.content; + if (!content && realTimeData.jwTargeting.content) { + content = realTimeData.jwTargeting.content; } } let impObj = { From a4904d666e38b77c5fd2c503d77a3da5f02ce6c0 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Thu, 3 Sep 2020 11:17:09 +0300 Subject: [PATCH 14/57] Added test for jwTargeting in TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 2 +- test/spec/modules/gridBidAdapter_spec.js | 28 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 8b909f1ee3d..32274fed2e6 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -351,7 +351,7 @@ function buildNewRequest(validBidRequests, bidderRequest) { } if (realTimeData && realTimeData.jwTargeting) { if (!jwpseg && realTimeData.jwTargeting.segments) { - jwpseg = realTimeData.segments; + jwpseg = realTimeData.jwTargeting.segments; } if (!content && realTimeData.jwTargeting.content) { content = realTimeData.jwTargeting.content; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 344f1764c05..650712e435f 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -578,6 +578,34 @@ describe('TheMediaGrid Adapter', function () { expect(payload.source.ext).to.have.property('schain'); expect(payload.source.ext.schain).to.deep.equal(schain); }); + + it('if content and segment is present in realTimeData.jwTargeting, payload must have right params', function () { + const jsContent = {id: 'test_jw_content_id'}; + const jsSegments = ['test_seg_1', 'test_seg_2']; + const bidRequestsWithUserIds = bidRequests.map((bid) => { + return Object.assign({ + realTimeData: { + jwTargeting: { + segments: jsSegments, + content: jsContent + } + } + }, bid); + }); + const [request] = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload).to.have.property('user'); + expect(payload.user.data).to.deep.equal([{ + name: 'iow_labs_pub_data', + segment: [ + {name: 'jwpseg', value: jsSegments[0]}, + {name: 'jwpseg', value: jsSegments[1]} + ] + }]); + expect(payload).to.have.property('site'); + expect(payload.site.content).to.deep.equal(jsContent); + }); }); describe('interpretResponse', function () { From 16440215f10ac32eba3b9ef924a8b92c585db7d9 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Thu, 8 Oct 2020 16:04:30 +0300 Subject: [PATCH 15/57] The new request format was made by default in TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 508 ++++++++--------------- test/spec/modules/gridBidAdapter_spec.js | 389 +++-------------- 2 files changed, 239 insertions(+), 658 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 378fc5a7efe..ad2cbb1d047 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -5,8 +5,7 @@ import { VIDEO, BANNER } from '../src/mediaTypes.js'; import {config} from '../src/config.js'; const BIDDER_CODE = 'grid'; -const ENDPOINT_URL = 'https://grid.bidswitch.net/hb'; -const NEW_ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; +const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -44,19 +43,184 @@ export const spec = { * @return {ServerRequest[]} Info describing the request to the server. */ buildRequests: function(validBidRequests, bidderRequest) { - const oldFormatBids = []; - const newFormatBids = []; + if (!validBidRequests.length) { + return null; + } + let pageKeywords = null; + let jwpseg = null; + let content = null; + let schain = null; + let userId = null; + let user = null; + let userExt = null; + let {bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo} = bidderRequest || {}; + + const referer = refererInfo ? encodeURIComponent(refererInfo.referer) : ''; + const imp = []; + const bidsMap = {}; + validBidRequests.forEach((bid) => { - bid.params.useNewFormat ? newFormatBids.push(bid) : oldFormatBids.push(bid); + if (!bidderRequestId) { + bidderRequestId = bid.bidderRequestId; + } + if (!auctionId) { + auctionId = bid.auctionId; + } + if (!schain) { + schain = bid.schain; + } + if (!userId) { + userId = bid.userId; + } + const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, realTimeData} = bid; + bidsMap[bidId] = bid; + if (!pageKeywords && !utils.isEmpty(keywords)) { + pageKeywords = utils.transformBidderParamKeywords(keywords); + } + if (realTimeData && realTimeData.jwTargeting) { + if (!jwpseg && realTimeData.jwTargeting.segments) { + jwpseg = realTimeData.jwTargeting.segments; + } + if (!content && realTimeData.jwTargeting.content) { + content = realTimeData.jwTargeting.content; + } + } + let impObj = { + id: bidId, + tagid: uid.toString(), + ext: { + divid: adUnitCode + } + }; + + if (!mediaTypes || mediaTypes[BANNER]) { + const banner = createBannerRequest(bid, mediaTypes ? mediaTypes[BANNER] : {}); + if (banner) { + impObj.banner = banner; + } + } + if (mediaTypes && mediaTypes[VIDEO]) { + const video = createVideoRequest(bid, mediaTypes[VIDEO]); + if (video) { + impObj.video = video; + } + } + + if (impObj.banner || impObj.video) { + imp.push(impObj); + } + }); + + const source = { + tid: auctionId, + ext: { + wrapper: 'Prebid_js', + wrapper_version: '$prebid.version$' + } + }; + + if (schain) { + source.ext.schain = schain; + } + + const bidderTimeout = config.getConfig('bidderTimeout') || timeout; + const tmax = timeout ? Math.min(bidderTimeout, timeout) : bidderTimeout; + + let request = { + id: bidderRequestId, + site: { + page: referer + }, + tmax, + source, + imp + }; + + if (content) { + request.site.content = content; + } + + if (jwpseg && jwpseg.length) { + user = { + data: [{ + name: 'iow_labs_pub_data', + segment: jwpseg.map((seg) => { + return {name: 'jwpseg', value: seg}; + }) + }] + }; + } + + if (gdprConsent && gdprConsent.consentString) { + userExt = {consent: gdprConsent.consentString}; + } + + if (userId) { + userExt = userExt || {}; + if (userId.tdid) { + userExt.unifiedid = userId.tdid; + } + if (userId.id5id && userId.id5id.uid) { + userExt.id5id = userId.id5id.uid; + } + if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { + userExt.digitrustid = userId.digitrustid.data.id; + } + if (userId.lipb && userId.lipb.lipbid) { + userExt.liveintentid = userId.lipb.lipbid; + } + } + + if (userExt && Object.keys(userExt).length) { + user = user || {}; + user.ext = userExt; + } + + if (user) { + request.user = user; + } + + const configKeywords = utils.transformBidderParamKeywords({ + 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, + 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null }); - const requests = []; - if (newFormatBids.length) { - requests.push(buildNewRequest(newFormatBids, bidderRequest)); + + if (configKeywords.length) { + pageKeywords = (pageKeywords || []).concat(configKeywords); } - if (oldFormatBids.length) { - requests.push(buildOldRequest(oldFormatBids, bidderRequest)); + + if (pageKeywords && pageKeywords.length > 0) { + pageKeywords.forEach(deleteValues); } - return requests; + + if (pageKeywords) { + request.ext = { + keywords: pageKeywords + }; + } + + if (gdprConsent && gdprConsent.gdprApplies) { + request.regs = { + ext: { + gdpr: gdprConsent.gdprApplies ? 1 : 0 + } + } + } + + if (uspConsent) { + if (!request.regs) { + request.regs = {ext: {}}; + } + request.regs.ext.us_privacy = uspConsent; + } + + return { + method: 'POST', + url: ENDPOINT_URL, + data: JSON.stringify(request), + newFormat: true, + bidsMap + }; }, /** * Unpack the response from the server into a list of bids. @@ -135,24 +299,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else { - let bid = null; - let slot = null; - const bidsMap = bidRequest.bidsMap; - if (bidRequest.newFormat) { - bid = bidsMap[serverBid.impid]; - } else { - const awaitingBids = bidsMap[serverBid.auid]; - if (awaitingBids) { - const sizeId = `${serverBid.w}x${serverBid.h}`; - if (awaitingBids[sizeId]) { - slot = awaitingBids[sizeId][0]; - bid = slot.bids.shift(); - } - } else { - errorMessage = LOG_ERROR_MESS.noPlacementCode + serverBid.auid; - } - } - + const bid = bidRequest.bidsMap[serverBid.impid]; if (bid) { const bidResponse = { requestId: bid.bidId, // bid.bidderRequestId, @@ -184,21 +331,6 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { bidResponse.mediaType = BANNER; } bidResponses.push(bidResponse); - - if (slot && !slot.bids.length) { - slot.parents.forEach(({parent, key, uid}) => { - const index = parent[key].indexOf(slot); - if (index > -1) { - parent[key].splice(index, 1); - } - if (!parent[key].length) { - delete parent[key]; - if (!utils.getKeys(parent).length) { - delete bidsMap[uid]; - } - } - }); - } } } if (errorMessage) { @@ -206,294 +338,6 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { } } -function buildOldRequest(validBidRequests, bidderRequest) { - const auids = []; - const bidsMap = {}; - const slotsMapByUid = {}; - const sizeMap = {}; - const bids = validBidRequests || []; - let pageKeywords = null; - let reqId; - - bids.forEach(bid => { - reqId = bid.bidderRequestId; - const {params: {uid}, adUnitCode, mediaTypes} = bid; - auids.push(uid); - const sizesId = utils.parseSizesInput(bid.sizes); - - if (!pageKeywords && !utils.isEmpty(bid.params.keywords)) { - pageKeywords = utils.transformBidderParamKeywords(bid.params.keywords); - } - - const addedSizes = {}; - sizesId.forEach((sizeId) => { - addedSizes[sizeId] = true; - }); - const bannerSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'banner.sizes')); - const videoSizesId = utils.parseSizesInput(utils.deepAccess(mediaTypes, 'video.playerSize')); - bannerSizesId.concat(videoSizesId).forEach((sizeId) => { - if (!addedSizes[sizeId]) { - addedSizes[sizeId] = true; - sizesId.push(sizeId); - } - }); - - if (!slotsMapByUid[uid]) { - slotsMapByUid[uid] = {}; - } - const slotsMap = slotsMapByUid[uid]; - if (!slotsMap[adUnitCode]) { - slotsMap[adUnitCode] = {adUnitCode, bids: [bid], parents: []}; - } else { - slotsMap[adUnitCode].bids.push(bid); - } - const slot = slotsMap[adUnitCode]; - - sizesId.forEach((sizeId) => { - sizeMap[sizeId] = true; - if (!bidsMap[uid]) { - bidsMap[uid] = {}; - } - - if (!bidsMap[uid][sizeId]) { - bidsMap[uid][sizeId] = [slot]; - } else { - bidsMap[uid][sizeId].push(slot); - } - slot.parents.push({parent: bidsMap[uid], key: sizeId, uid}); - }); - }); - - const configKeywords = utils.transformBidderParamKeywords({ - 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, - 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null - }); - - if (configKeywords.length) { - pageKeywords = (pageKeywords || []).concat(configKeywords); - } - - if (pageKeywords && pageKeywords.length > 0) { - pageKeywords.forEach(deleteValues); - } - - const payload = { - auids: auids.join(','), - sizes: utils.getKeys(sizeMap).join(','), - r: reqId, - wrapperType: 'Prebid_js', - wrapperVersion: '$prebid.version$' - }; - - if (pageKeywords) { - payload.keywords = JSON.stringify(pageKeywords); - } - - if (bidderRequest) { - if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { - payload.u = bidderRequest.refererInfo.referer; - } - if (bidderRequest.timeout) { - payload.wtimeout = bidderRequest.timeout; - } - if (bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - payload.gdpr_consent = bidderRequest.gdprConsent.consentString; - } - payload.gdpr_applies = - (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') - ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; - } - if (bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent; - } - } - - return { - method: 'GET', - url: ENDPOINT_URL, - data: utils.parseQueryStringParameters(payload).replace(/\&$/, ''), - bidsMap: bidsMap - } -} - -function buildNewRequest(validBidRequests, bidderRequest) { - let pageKeywords = null; - let jwpseg = null; - let content = null; - let schain = null; - let userId = null; - let user = null; - let userExt = null; - let {bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo} = bidderRequest; - - const referer = refererInfo ? encodeURIComponent(refererInfo.referer) : ''; - const imp = []; - const bidsMap = {}; - - validBidRequests.forEach((bid) => { - if (!bidderRequestId) { - bidderRequestId = bid.bidderRequestId; - } - if (!auctionId) { - auctionId = bid.auctionId; - } - if (!schain) { - schain = bid.schain; - } - if (!userId) { - userId = bid.userId; - } - const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, realTimeData} = bid; - bidsMap[bidId] = bid; - if (!pageKeywords && !utils.isEmpty(keywords)) { - pageKeywords = utils.transformBidderParamKeywords(keywords); - } - if (realTimeData && realTimeData.jwTargeting) { - if (!jwpseg && realTimeData.jwTargeting.segments) { - jwpseg = realTimeData.jwTargeting.segments; - } - if (!content && realTimeData.jwTargeting.content) { - content = realTimeData.jwTargeting.content; - } - } - let impObj = { - id: bidId, - tagid: uid.toString(), - ext: { - divid: adUnitCode - } - }; - - if (!mediaTypes || mediaTypes[BANNER]) { - const banner = createBannerRequest(bid, mediaTypes ? mediaTypes[BANNER] : {}); - if (banner) { - impObj.banner = banner; - } - } - if (mediaTypes && mediaTypes[VIDEO]) { - const video = createVideoRequest(bid, mediaTypes[VIDEO]); - if (video) { - impObj.video = video; - } - } - - if (impObj.banner || impObj.video) { - imp.push(impObj); - } - }); - - const source = { - tid: auctionId, - ext: { - wrapper: 'Prebid_js', - wrapper_version: '$prebid.version$' - } - }; - - if (schain) { - source.ext.schain = schain; - } - - const tmax = config.getConfig('bidderTimeout') || timeout; - - let request = { - id: bidderRequestId, - site: { - page: referer - }, - tmax, - source, - imp - }; - - if (content) { - request.site.content = content; - } - - if (jwpseg && jwpseg.length) { - user = { - data: [{ - name: 'iow_labs_pub_data', - segment: jwpseg.map((seg) => { - return {name: 'jwpseg', value: seg}; - }) - }] - }; - } - - if (gdprConsent && gdprConsent.consentString) { - userExt = {consent: gdprConsent.consentString}; - } - - if (userId) { - userExt = userExt || {}; - if (userId.tdid) { - userExt.unifiedid = userId.tdid; - } - if (userId.id5id && userId.id5id.uid) { - userExt.id5id = userId.id5id.uid; - } - if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { - userExt.digitrustid = userId.digitrustid.data.id; - } - if (userId.lipb && userId.lipb.lipbid) { - userExt.liveintentid = userId.lipb.lipbid; - } - } - - if (userExt && Object.keys(userExt).length) { - user = user || {}; - user.ext = userExt; - } - - if (user) { - request.user = user; - } - - const configKeywords = utils.transformBidderParamKeywords({ - 'user': utils.deepAccess(config.getConfig('fpd.user'), 'keywords') || null, - 'context': utils.deepAccess(config.getConfig('fpd.context'), 'keywords') || null - }); - - if (configKeywords.length) { - pageKeywords = (pageKeywords || []).concat(configKeywords); - } - - if (pageKeywords && pageKeywords.length > 0) { - pageKeywords.forEach(deleteValues); - } - - if (pageKeywords) { - request.ext = { - keywords: pageKeywords - }; - } - - if (gdprConsent && gdprConsent.gdprApplies) { - request.regs = { - ext: { - gdpr: gdprConsent.gdprApplies ? 1 : 0 - } - } - } - - if (uspConsent) { - if (!request.regs) { - request.regs = {ext: {}}; - } - request.regs.ext.us_privacy = uspConsent; - } - - return { - method: 'POST', - url: NEW_ENDPOINT_URL, - data: JSON.stringify(request), - newFormat: true, - bidsMap - }; -} - function createVideoRequest(bid, mediaType) { const {playerSize, mimes, durationRangeSec} = mediaType; const size = (playerSize || bid.sizes || [])[0]; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 1cfca4779ad..f3e73d9ab69 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -40,229 +40,6 @@ describe('TheMediaGrid Adapter', function () { }); describe('buildRequests', function () { - function parseRequest(url) { - const res = {}; - url.split('&').forEach((it) => { - const couple = it.split('='); - res[couple[0]] = decodeURIComponent(couple[1]); - }); - return res; - } - const bidderRequest = {refererInfo: {referer: 'https://example.com'}}; - const referrer = bidderRequest.refererInfo.referer; - let bidRequests = [ - { - 'bidder': 'grid', - 'params': { - 'uid': '1' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }, - { - 'bidder': 'grid', - 'params': { - 'uid': '1' - }, - 'adUnitCode': 'adunit-code-2', - 'sizes': [[728, 90]], - 'mediaTypes': { - 'video': { - 'playerSize': [400, 600] - }, - 'banner': { - 'sizes': [[728, 90]] - } - }, - 'bidId': '3150ccb55da321', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }, - { - 'bidder': 'grid', - 'params': { - 'uid': '2' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'mediaTypes': { - 'video': { - 'playerSize': [400, 600] - }, - 'banner': { - 'sizes': [[300, 250], [300, 600]] - } - }, - 'bidId': '42dbe3a7168a6a', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - - it('should attach valid params to the tag', function () { - const [request] = spec.buildRequests([bidRequests[0]], bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('auids', '1'); - expect(payload).to.have.property('sizes', '300x250,300x600'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('wrapperType', 'Prebid_js'); - expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); - }); - - it('sizes must be added from mediaTypes', function () { - const [request] = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('auids', '1,1'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90,400x600'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - }); - - it('sizes must not be duplicated', function () { - const [request] = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('auids', '1,1,2'); - expect(payload).to.have.property('sizes', '300x250,300x600,728x90,400x600'); - expect(payload).to.have.property('r', '22edbae2733bf6'); - }); - - it('if gdprConsent is present payload must have gdpr params', function () { - const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '1'); - }); - - it('if gdprApplies is false gdpr_applies must be 0', function () { - const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '0'); - }); - - it('if gdprApplies is undefined gdpr_applies must be 1', function () { - const [request] = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'AAA'}}); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '1'); - }); - - it('if usPrivacy is present payload must have us_privacy param', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const [request] = spec.buildRequests(bidRequests, bidderRequestWithUSP); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload).to.have.property('us_privacy', '1YNN'); - }); - - it('should convert keyword params to proper form and attaches to request', function () { - const bidRequestWithKeywords = [].concat(bidRequests); - bidRequestWithKeywords[1] = Object.assign({}, - bidRequests[1], - { - params: { - uid: '1', - keywords: { - single: 'val', - singleArr: ['val'], - singleArrNum: [3], - multiValMixed: ['value1', 2, 'value3'], - singleValNum: 123, - emptyStr: '', - emptyArr: [''], - badValue: {'foo': 'bar'} // should be dropped - } - } - } - ); - - const [request] = spec.buildRequests(bidRequestWithKeywords, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload.keywords).to.be.an('string'); - payload.keywords = JSON.parse(payload.keywords); - - expect(payload.keywords).to.deep.equal([{ - 'key': 'single', - 'value': ['val'] - }, { - 'key': 'singleArr', - 'value': ['val'] - }, { - 'key': 'singleArrNum', - 'value': ['3'] - }, { - 'key': 'multiValMixed', - 'value': ['value1', '2', 'value3'] - }, { - 'key': 'singleValNum', - 'value': ['123'] - }, { - 'key': 'emptyStr' - }, { - 'key': 'emptyArr' - }]); - }); - - it('should mix keyword param with keywords from config', function () { - const getConfigStub = sinon.stub(config, 'getConfig').callsFake( - arg => arg === 'fpd.user' ? {'keywords': ['a', 'b']} : arg === 'fpd.context' ? {'keywords': ['any words']} : null); - - const bidRequestWithKeywords = [].concat(bidRequests); - bidRequestWithKeywords[1] = Object.assign({}, - bidRequests[1], - { - params: { - uid: '1', - keywords: { - single: 'val', - singleArr: ['val'], - multiValMixed: ['value1', 2, 'value3'] - } - } - } - ); - - const [request] = spec.buildRequests(bidRequestWithKeywords, bidderRequest); - expect(request.data).to.be.an('string'); - const payload = parseRequest(request.data); - expect(payload.keywords).to.be.an('string'); - payload.keywords = JSON.parse(payload.keywords); - - expect(payload.keywords).to.deep.equal([{ - 'key': 'single', - 'value': ['val'] - }, { - 'key': 'singleArr', - 'value': ['val'] - }, { - 'key': 'multiValMixed', - 'value': ['value1', '2', 'value3'] - }, { - 'key': 'user', - 'value': ['a', 'b'] - }, { - 'key': 'context', - 'value': ['any words'] - }]); - - getConfigStub.restore(); - }); - }); - - describe('buildRequests in new format', function () { function parseRequest(data) { return JSON.parse(data); } @@ -344,7 +121,7 @@ describe('TheMediaGrid Adapter', function () { ]; it('should attach valid params to the tag', function () { - const [request] = spec.buildRequests([bidRequests[0]], bidderRequest); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.deep.equal({ @@ -371,7 +148,7 @@ describe('TheMediaGrid Adapter', function () { }); it('make possible to process request without mediaTypes', function () { - const [request] = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); + const request = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.deep.equal({ @@ -407,7 +184,7 @@ describe('TheMediaGrid Adapter', function () { }); it('should attach valid params to the video tag', function () { - const [request] = spec.buildRequests(bidRequests.slice(0, 3), bidderRequest); + const request = spec.buildRequests(bidRequests.slice(0, 3), bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.deep.equal({ @@ -452,7 +229,7 @@ describe('TheMediaGrid Adapter', function () { }); it('should support mixed mediaTypes', function () { - const [request] = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.deep.equal({ @@ -511,7 +288,7 @@ describe('TheMediaGrid Adapter', function () { it('if gdprConsent is present payload must have gdpr params', function () { const gdprBidderRequest = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); - const [request] = spec.buildRequests(bidRequests, gdprBidderRequest); + const request = spec.buildRequests(bidRequests, gdprBidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('user'); @@ -524,7 +301,7 @@ describe('TheMediaGrid Adapter', function () { it('if usPrivacy is present payload must have us_privacy param', function () { const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const [request] = spec.buildRequests(bidRequests, bidderRequestWithUSP); + const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('regs'); @@ -543,7 +320,7 @@ describe('TheMediaGrid Adapter', function () { } }, bid); }); - const [request] = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); + const request = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('user'); @@ -570,7 +347,7 @@ describe('TheMediaGrid Adapter', function () { schain: schain }, bid); }); - const [request] = spec.buildRequests(bidRequestsWithSChain, bidderRequest); + const request = spec.buildRequests(bidRequestsWithSChain, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('source'); @@ -592,7 +369,7 @@ describe('TheMediaGrid Adapter', function () { } }, bid); }); - const [request] = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); + const request = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('user'); @@ -606,14 +383,33 @@ describe('TheMediaGrid Adapter', function () { expect(payload).to.have.property('site'); expect(payload.site.content).to.deep.equal(jsContent); }); + + it('shold be right tmax when timeout in config is less then timeout in bidderRequest', function() { + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'bidderTimeout' ? 2000 : null); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.tmax).to.equal(2000); + getConfigStub.restore(); + }); + it('shold be right tmax when timeout in bidderRequest is less then timeout in config', function() { + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'bidderTimeout' ? 5000 : null); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.tmax).to.equal(3000); + getConfigStub.restore(); + }); }); describe('interpretResponse', function () { const responses = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0, 'auid': 3, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'impid': '659423fff799cb', 'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, 'dealid': 11}], 'seat': '1'}, + {'bid': [{'impid': '4dff80cc4ee346', 'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'impid': '5703af74d0472a', 'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'impid': '2344da98f78b42', 'price': 0, 'auid': 3, 'h': 250, 'w': 300}], 'seat': '1'}, {'bid': [{'price': 0, 'adm': '
test content 5
', 'h': 250, 'w': 300}], 'seat': '1'}, undefined, {'bid': [], 'seat': '1'}, @@ -634,7 +430,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1cbd2feafe5e8b', } ]; - const [request] = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '659423fff799cb', @@ -665,7 +461,7 @@ describe('TheMediaGrid Adapter', function () { }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], - 'bidId': '300bfeb0d71a5b', + 'bidId': '659423fff799cb', 'bidderRequestId': '2c2bb1972df9a', 'auctionId': '1fa09aee5c8c99', }, @@ -692,10 +488,10 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1fa09aee5c8c99', } ]; - const [request] = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); const expectedResponse = [ { - 'requestId': '300bfeb0d71a5b', + 'requestId': '659423fff799cb', 'cpm': 1.15, 'creativeId': 1, 'dealId': 11, @@ -778,10 +574,10 @@ describe('TheMediaGrid Adapter', function () { } ]; const response = [ - {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 11, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, - {'bid': [{'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 12, content_type: 'video'}], 'seat': '2'} + {'bid': [{'impid': '659423fff799cb', 'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 11, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, + {'bid': [{'impid': '2bc598e42b6a', 'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 12, content_type: 'video'}], 'seat': '2'} ]; - const [request] = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '659423fff799cb', @@ -799,6 +595,23 @@ describe('TheMediaGrid Adapter', function () { 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' } + }, + { + 'requestId': '2bc598e42b6a', + 'cpm': 1.00, + 'creativeId': 12, + 'dealId': undefined, + 'width': undefined, + 'height': undefined, + 'bidderCode': 'grid', + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': false, + 'ttl': 360, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } } ]; @@ -842,18 +655,18 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '1fa09aee5c84d34', } ]; - const [request] = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); expect(result.length).to.equal(0); }); it('complicated case', function () { const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300, dealid: 12}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'price': 0.15, 'adm': '
test content 4
', 'auid': 1, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 5
', 'auid': 2, 'h': 600, 'w': 350}], 'seat': '1'}, + {'bid': [{'impid': '2164be6358b9', 'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11}], 'seat': '1'}, + {'bid': [{'impid': '4e111f1b66e4', 'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300, dealid: 12}], 'seat': '1'}, + {'bid': [{'impid': '26d6f897b516', 'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, + {'bid': [{'impid': '326bde7fbf69', 'price': 0.15, 'adm': '
test content 4
', 'auid': 1, 'h': 600, 'w': 300}], 'seat': '1'}, + {'bid': [{'impid': '2234f233b22a', 'price': 0.5, 'adm': '
test content 5
', 'auid': 2, 'h': 600, 'w': 350}], 'seat': '1'}, ]; const bidRequests = [ { @@ -912,7 +725,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': '32a1f276cb87cb8', } ]; - const [request] = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); const expectedResponse = [ { 'requestId': '2164be6358b9', @@ -975,82 +788,6 @@ describe('TheMediaGrid Adapter', function () { const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); expect(result).to.deep.equal(expectedResponse); }); - - it('dublicate uids and sizes in one slot', function () { - const fullResponse = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 1, 'h': 250, 'w': 300}], 'seat': '1'}, - ]; - const bidRequests = [ - { - 'bidder': 'grid', - 'params': { - 'uid': '1' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '5126e301f4be', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - }, - { - 'bidder': 'grid', - 'params': { - 'uid': '1' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '57b2ebe70e16', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - }, - { - 'bidder': 'grid', - 'params': { - 'uid': '1' - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '225fcd44b18c', - 'bidderRequestId': '171c5405a390', - 'auctionId': '35bcbc0f7e79c', - } - ]; - const [request] = spec.buildRequests(bidRequests); - const expectedResponse = [ - { - 'requestId': '5126e301f4be', - 'cpm': 1.15, - 'creativeId': 1, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 1
', - 'bidderCode': 'grid', - 'currency': 'USD', - 'mediaType': 'banner', - 'netRevenue': false, - 'ttl': 360, - }, - { - 'requestId': '57b2ebe70e16', - 'cpm': 0.5, - 'creativeId': 1, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '
test content 2
', - 'bidderCode': 'grid', - 'currency': 'USD', - 'mediaType': 'banner', - 'netRevenue': false, - 'ttl': 360, - } - ]; - - const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); - expect(result).to.deep.equal(expectedResponse); - }); }); describe('user sync', function () { From 54237d7f469d269c29b486fdde8fd64fbe6e2dfa Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 12 Oct 2020 18:18:49 +0300 Subject: [PATCH 16/57] Update userId format in ad request for TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 64 +++++++++++++++++++++--- test/spec/modules/gridBidAdapter_spec.js | 55 +++++++++++++++++--- 2 files changed, 106 insertions(+), 13 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index ad2cbb1d047..989fe472540 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -156,18 +156,68 @@ export const spec = { } if (userId) { - userExt = userExt || {}; if (userId.tdid) { - userExt.unifiedid = userId.tdid; + userExt = userExt || {}; + userExt.eids = userExt.eids || []; + userExt.eids.push({ + source: 'adserver.org', // Unified ID + uids: [{ + id: userId.tdid, + ext: { + rtiPartner: 'TDID' + } + }] + }); + // userExt.unifiedid = userId.tdid; } if (userId.id5id && userId.id5id.uid) { - userExt.id5id = userId.id5id.uid; - } - if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { - userExt.digitrustid = userId.digitrustid.data.id; + userExt = userExt || {}; + userExt.eids = userExt.eids || []; + userExt.eids.push({ + source: 'id5-sync.com', + uids: [{ + id: userId.id5id.uid + }], + ext: userId.id5id.ext + }); + // userExt.id5id = userId.id5id.uid; } if (userId.lipb && userId.lipb.lipbid) { - userExt.liveintentid = userId.lipb.lipbid; + userExt = userExt || {}; + userExt.eids = userExt.eids || []; + userExt.eids.push({ + source: 'liveintent.com', + uids: [{ + id: userId.lipb.lipbid + }] + }); + // userExt.liveintentid = userId.lipb.lipbid; + } + if (userId.idl_env) { + userExt = userExt || {}; + userExt.eids = userExt.eids || []; + userExt.eids.push({ + source: 'identityLink', + uids: [{ + id: userId.idl_env + }] + }); + } + if (userId.criteoId) { + userExt = userExt || {}; + userExt.eids = userExt.eids || []; + userExt.eids.push({ + source: 'criteo.com', + uids: [{ + id: userId.criteoId + }] + }); + } + + if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { + userExt = userExt || {}; + userExt.digitrust = Object.assign({}, userId.digitrustid.data); + // userExt.digitrustid = userId.digitrustid.data.id; } } diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index f3e73d9ab69..d1402739de2 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -313,10 +313,12 @@ describe('TheMediaGrid Adapter', function () { const bidRequestsWithUserIds = bidRequests.map((bid) => { return Object.assign({ userId: { - id5id: { uid: 'id5id_1' }, + id5id: { uid: 'id5id_1', ext: { linkType: 2 } }, tdid: 'tdid_1', digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - lipb: {lipbid: 'lipb_1'} + lipb: {lipbid: 'lipb_1'}, + idl_env: 'idl_env_1', + criteoId: 'criteoId_1' } }, bid); }); @@ -325,10 +327,51 @@ describe('TheMediaGrid Adapter', function () { const payload = parseRequest(request.data); expect(payload).to.have.property('user'); expect(payload.user).to.have.property('ext'); - expect(payload.user.ext).to.have.property('unifiedid', 'tdid_1'); - expect(payload.user.ext).to.have.property('id5id', 'id5id_1'); - expect(payload.user.ext).to.have.property('digitrustid', 'DTID'); - expect(payload.user.ext).to.have.property('liveintentid', 'lipb_1'); + expect(payload.user.ext.digitrust).to.deep.equal({ + id: 'DTID', + keyv: 4, + privacy: { + optout: false + }, + producer: 'ABC', + version: 2 + }); + expect(payload.user.ext.eids).to.deep.equal([ + { + source: 'adserver.org', + uids: [{ + id: 'tdid_1', + ext: { + rtiPartner: 'TDID' + } + }] + }, + { + source: 'id5-sync.com', + uids: [{ + id: 'id5id_1' + }], + ext: { linkType: 2 } + }, + { + source: 'liveintent.com', + uids: [{ + id: 'lipb_1' + }] + }, + { + source: 'identityLink', + uids: [{ + id: 'idl_env_1' + }] + }, + { + source: 'criteo.com', + uids: [{ + id: 'criteoId_1' + }] + } + ]); }); it('if schain is present payload must have source.ext.schain param', function () { From a8c709670370b27a83634cd9f843e485ac5d88fe Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Wed, 14 Oct 2020 15:29:00 +0300 Subject: [PATCH 17/57] Added bidFloor parameter for TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 5 ++++- modules/gridBidAdapter.md | 3 +-- test/spec/modules/gridBidAdapter_spec.js | 15 ++++++++------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 989fe472540..f585c14375b 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -72,7 +72,7 @@ export const spec = { if (!userId) { userId = bid.userId; } - const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, realTimeData} = bid; + const {params: {uid, keywords, bidFloor}, mediaTypes, bidId, adUnitCode, realTimeData} = bid; bidsMap[bidId] = bid; if (!pageKeywords && !utils.isEmpty(keywords)) { pageKeywords = utils.transformBidderParamKeywords(keywords); @@ -93,6 +93,9 @@ export const spec = { } }; + if (bidFloor) { + impObj.bidfloor = bidFloor; + } if (!mediaTypes || mediaTypes[BANNER]) { const banner = createBannerRequest(bid, mediaTypes ? mediaTypes[BANNER] : {}); if (banner) { diff --git a/modules/gridBidAdapter.md b/modules/gridBidAdapter.md index 77b9bbf0f36..6a7075ccb00 100644 --- a/modules/gridBidAdapter.md +++ b/modules/gridBidAdapter.md @@ -20,7 +20,7 @@ Grid bid adapter supports Banner and Video (instream and outstream). bidder: "grid", params: { uid: '1', - priceType: 'gross' // by default is 'net' + bidFloor: 0.5 } } ] @@ -32,7 +32,6 @@ Grid bid adapter supports Banner and Video (instream and outstream). bidder: "grid", params: { uid: 2, - priceType: 'gross', keywords: { brandsafety: ['disaster'], topic: ['stress', 'fear'] diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index d1402739de2..9b76c450fa5 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -55,7 +55,7 @@ describe('TheMediaGrid Adapter', function () { 'bidder': 'grid', 'params': { 'uid': '1', - 'useNewFormat': true + 'bidFloor': 1.25 }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], @@ -71,8 +71,7 @@ describe('TheMediaGrid Adapter', function () { { 'bidder': 'grid', 'params': { - 'uid': '2', - 'useNewFormat': true + 'uid': '2' }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], @@ -83,8 +82,7 @@ describe('TheMediaGrid Adapter', function () { { 'bidder': 'grid', 'params': { - 'uid': '11', - 'useNewFormat': true + 'uid': '11' }, 'adUnitCode': 'adunit-code-2', 'sizes': [[728, 90]], @@ -101,8 +99,7 @@ describe('TheMediaGrid Adapter', function () { { 'bidder': 'grid', 'params': { - 'uid': '3', - 'useNewFormat': true + 'uid': '3' }, 'adUnitCode': 'adunit-code-2', 'sizes': [[728, 90]], @@ -138,6 +135,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, @@ -165,6 +163,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, @@ -201,6 +200,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, @@ -246,6 +246,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, 'ext': {'divid': bidRequests[0].adUnitCode}, + 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, From e9fb25d0b12aa4ba3ee4418b92ef0442ae52e4d3 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 20 Oct 2020 19:53:22 +0300 Subject: [PATCH 18/57] Fix for review TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index f585c14375b..af4c7f7f176 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -171,7 +171,6 @@ export const spec = { } }] }); - // userExt.unifiedid = userId.tdid; } if (userId.id5id && userId.id5id.uid) { userExt = userExt || {}; @@ -183,7 +182,6 @@ export const spec = { }], ext: userId.id5id.ext }); - // userExt.id5id = userId.id5id.uid; } if (userId.lipb && userId.lipb.lipbid) { userExt = userExt || {}; @@ -194,7 +192,6 @@ export const spec = { id: userId.lipb.lipbid }] }); - // userExt.liveintentid = userId.lipb.lipbid; } if (userId.idl_env) { userExt = userExt || {}; @@ -220,7 +217,6 @@ export const spec = { if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { userExt = userExt || {}; userExt.digitrust = Object.assign({}, userId.digitrustid.data); - // userExt.digitrustid = userId.digitrustid.data.id; } } From 9039a7943d7cdcb9b3d6723d491f252b33f629de Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 20 Oct 2020 20:42:02 +0300 Subject: [PATCH 19/57] Support floorModule in TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 34 +++++++++++++++++--- test/spec/modules/gridBidAdapter_spec.js | 41 ++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index af4c7f7f176..a7cde559f12 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -90,12 +90,10 @@ export const spec = { tagid: uid.toString(), ext: { divid: adUnitCode - } + }, + bidfloor: _getFloor(mediaTypes || {}, bidFloor, bid) }; - if (bidFloor) { - impObj.bidfloor = bidFloor; - } if (!mediaTypes || mediaTypes[BANNER]) { const banner = createBannerRequest(bid, mediaTypes ? mediaTypes[BANNER] : {}); if (banner) { @@ -321,6 +319,34 @@ export const spec = { } }; +/** + * Gets bidfloor + * @param {Object} mediaTypes + * @param {Number} bidfloor + * @param {Object} bid + * @returns {Number} floor + */ +function _getFloor (mediaTypes, bidfloor, bid) { + const curMediaType = mediaTypes.video ? 'video' : 'banner'; + let floor = bidfloor || 0; + + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: curMediaType, + size: bid.sizes.map(([w, h]) => ({w, h})) + }); + + if (typeof floorInfo === 'object' && + floorInfo.currency === 'USD' && + !isNaN(parseFloat(floorInfo.floor))) { + floor = Math.max(floor, parseFloat(floorInfo.floor)); + } + } + + return floor; +} + function isPopulatedArray(arr) { return !!(utils.isArray(arr) && arr.length > 0); } diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 9b76c450fa5..3a67a87e019 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -173,6 +173,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, + 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -210,6 +211,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, + 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -219,6 +221,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[2].bidId, 'tagid': bidRequests[2].params.uid, 'ext': {'divid': bidRequests[2].adUnitCode}, + 'bidfloor': 0, 'video': { 'w': 400, 'h': 600, @@ -256,6 +259,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, + 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -265,6 +269,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[2].bidId, 'tagid': bidRequests[2].params.uid, 'ext': {'divid': bidRequests[2].adUnitCode}, + 'bidfloor': 0, 'video': { 'w': 400, 'h': 600, @@ -274,6 +279,7 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[3].bidId, 'tagid': bidRequests[3].params.uid, 'ext': {'divid': bidRequests[3].adUnitCode}, + 'bidfloor': 0, 'banner': { 'w': 728, 'h': 90, @@ -446,6 +452,41 @@ describe('TheMediaGrid Adapter', function () { expect(payload.tmax).to.equal(3000); getConfigStub.restore(); }); + describe('floorModule', function () { + const floorTestData = { + 'currency': 'USD', + 'floor': 1.50 + }; + const bidRequest = Object.assign({ + getFloor: _ => { + return floorTestData; + } + }, bidRequests[1]); + it('should return the value from getFloor if present', function () { + const request = spec.buildRequests([bidRequest], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].bidfloor).to.equal(floorTestData.floor); + }); + it('should return the getFloor.floor value if it is greater than bidfloor', function () { + const bidfloor = 0.80; + const bidRequestsWithFloor = { ...bidRequest }; + bidRequestsWithFloor.params = Object.assign({bidFloor: bidfloor}, bidRequestsWithFloor.params); + const request = spec.buildRequests([bidRequestsWithFloor], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].bidfloor).to.equal(floorTestData.floor); + }); + it('should return the bidfloor value if it is greater than getFloor.floor', function () { + const bidfloor = 1.80; + const bidRequestsWithFloor = { ...bidRequest }; + bidRequestsWithFloor.params = Object.assign({bidFloor: bidfloor}, bidRequestsWithFloor.params); + const request = spec.buildRequests([bidRequestsWithFloor], bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].bidfloor).to.equal(bidfloor); + }); + }); }); describe('interpretResponse', function () { From 2347817167349e52c325122a00b2e315fd3a7a33 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Wed, 25 Nov 2020 12:00:50 +0300 Subject: [PATCH 20/57] Fix empty bidfloor for TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 15 +++++++++------ test/spec/modules/gridBidAdapter_spec.js | 6 ------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index db1402ea9ad..3f9d4fba31d 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -72,11 +72,12 @@ export const spec = { if (!userId) { userId = bid.userId; } - const {params: {uid, keywords, bidFloor}, mediaTypes, bidId, adUnitCode, rtd} = bid; + const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd} = bid; bidsMap[bidId] = bid; if (!pageKeywords && !utils.isEmpty(keywords)) { pageKeywords = utils.transformBidderParamKeywords(keywords); } + const bidFloor = _getFloor(mediaTypes || {}, bid); const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; if (jwTargeting) { if (!jwpseg && jwTargeting.segments) { @@ -91,10 +92,13 @@ export const spec = { tagid: uid.toString(), ext: { divid: adUnitCode - }, - bidfloor: _getFloor(mediaTypes || {}, bidFloor, bid) + } }; + if (bidFloor) { + impObj.bidfloor = bidFloor; + } + if (!mediaTypes || mediaTypes[BANNER]) { const banner = createBannerRequest(bid, mediaTypes ? mediaTypes[BANNER] : {}); if (banner) { @@ -323,13 +327,12 @@ export const spec = { /** * Gets bidfloor * @param {Object} mediaTypes - * @param {Number} bidfloor * @param {Object} bid * @returns {Number} floor */ -function _getFloor (mediaTypes, bidfloor, bid) { +function _getFloor (mediaTypes, bid) { const curMediaType = mediaTypes.video ? 'video' : 'banner'; - let floor = bidfloor || 0; + let floor = bid.params.bidFloor || 0; if (typeof bid.getFloor === 'function') { const floorInfo = bid.getFloor({ diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 640bfda1fee..ce03543a6df 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -173,7 +173,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, - 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -211,7 +210,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, - 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -221,7 +219,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[2].bidId, 'tagid': bidRequests[2].params.uid, 'ext': {'divid': bidRequests[2].adUnitCode}, - 'bidfloor': 0, 'video': { 'w': 400, 'h': 600, @@ -259,7 +256,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, 'ext': {'divid': bidRequests[1].adUnitCode}, - 'bidfloor': 0, 'banner': { 'w': 300, 'h': 250, @@ -269,7 +265,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[2].bidId, 'tagid': bidRequests[2].params.uid, 'ext': {'divid': bidRequests[2].adUnitCode}, - 'bidfloor': 0, 'video': { 'w': 400, 'h': 600, @@ -279,7 +274,6 @@ describe('TheMediaGrid Adapter', function () { 'id': bidRequests[3].bidId, 'tagid': bidRequests[3].params.uid, 'ext': {'divid': bidRequests[3].adUnitCode}, - 'bidfloor': 0, 'banner': { 'w': 728, 'h': 90, From a7c3733ffbe9ceef450da49c885d0f7634dfad2c Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 30 Nov 2020 18:15:16 +0300 Subject: [PATCH 21/57] Some change to restart autotests --- test/spec/modules/gridBidAdapter_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index ce03543a6df..e884df40c5e 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -454,7 +454,7 @@ describe('TheMediaGrid Adapter', function () { 'floor': 1.50 }; const bidRequest = Object.assign({ - getFloor: _ => { + getFloor: (_) => { return floorTestData; } }, bidRequests[1]); From 77ce1999302113152b4883924336fffed223d584 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Fri, 18 Dec 2020 18:24:05 +0300 Subject: [PATCH 22/57] Fix userIds format for TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 70 ++++-------------------- test/spec/modules/gridBidAdapter_spec.js | 68 ++++++++--------------- 2 files changed, 35 insertions(+), 103 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 3f9d4fba31d..68c8f43be24 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -51,6 +51,7 @@ export const spec = { let content = null; let schain = null; let userId = null; + let userIdAsEids = null; let user = null; let userExt = null; let {bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo} = bidderRequest || {}; @@ -72,6 +73,9 @@ export const spec = { if (!userId) { userId = bid.userId; } + if (!userIdAsEids) { + userIdAsEids = bid.userIdAsEids; + } const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd} = bid; bidsMap[bidId] = bid; if (!pageKeywords && !utils.isEmpty(keywords)) { @@ -161,66 +165,14 @@ export const spec = { userExt = {consent: gdprConsent.consentString}; } - if (userId) { - if (userId.tdid) { - userExt = userExt || {}; - userExt.eids = userExt.eids || []; - userExt.eids.push({ - source: 'adserver.org', // Unified ID - uids: [{ - id: userId.tdid, - ext: { - rtiPartner: 'TDID' - } - }] - }); - } - if (userId.id5id && userId.id5id.uid) { - userExt = userExt || {}; - userExt.eids = userExt.eids || []; - userExt.eids.push({ - source: 'id5-sync.com', - uids: [{ - id: userId.id5id.uid - }], - ext: userId.id5id.ext - }); - } - if (userId.lipb && userId.lipb.lipbid) { - userExt = userExt || {}; - userExt.eids = userExt.eids || []; - userExt.eids.push({ - source: 'liveintent.com', - uids: [{ - id: userId.lipb.lipbid - }] - }); - } - if (userId.idl_env) { - userExt = userExt || {}; - userExt.eids = userExt.eids || []; - userExt.eids.push({ - source: 'identityLink', - uids: [{ - id: userId.idl_env - }] - }); - } - if (userId.criteoId) { - userExt = userExt || {}; - userExt.eids = userExt.eids || []; - userExt.eids.push({ - source: 'criteo.com', - uids: [{ - id: userId.criteoId - }] - }); - } + if (userIdAsEids && userIdAsEids.length) { + userExt = userExt || {}; + userExt.eids = [...userIdAsEids]; + } - if (userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { - userExt = userExt || {}; - userExt.digitrust = Object.assign({}, userId.digitrustid.data); - } + if (userId && userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { + userExt = userExt || {}; + userExt.digitrust = Object.assign({}, userId.digitrustid.data); } if (userExt && Object.keys(userExt).length) { diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index e884df40c5e..14c635f3918 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -311,15 +311,30 @@ describe('TheMediaGrid Adapter', function () { }); it('if userId is present payload must have user.ext param with right keys', function () { + const eids = [ + { + source: 'pubcid.org', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }, + { + source: 'adserver.org', + uids: [{ + id: 'some-random-id-value', + atype: 1, + ext: { + rtiPartner: 'TDID' + } + }] + } + ]; const bidRequestsWithUserIds = bidRequests.map((bid) => { return Object.assign({ + userIdAsEids: eids, userId: { - id5id: { uid: 'id5id_1', ext: { linkType: 2 } }, - tdid: 'tdid_1', - digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - lipb: {lipbid: 'lipb_1'}, - idl_env: 'idl_env_1', - criteoId: 'criteoId_1' + digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}} } }, bid); }); @@ -337,42 +352,7 @@ describe('TheMediaGrid Adapter', function () { producer: 'ABC', version: 2 }); - expect(payload.user.ext.eids).to.deep.equal([ - { - source: 'adserver.org', - uids: [{ - id: 'tdid_1', - ext: { - rtiPartner: 'TDID' - } - }] - }, - { - source: 'id5-sync.com', - uids: [{ - id: 'id5id_1' - }], - ext: { linkType: 2 } - }, - { - source: 'liveintent.com', - uids: [{ - id: 'lipb_1' - }] - }, - { - source: 'identityLink', - uids: [{ - id: 'idl_env_1' - }] - }, - { - source: 'criteo.com', - uids: [{ - id: 'criteoId_1' - }] - } - ]); + expect(payload.user.ext.eids).to.deep.equal(eids); }); it('if schain is present payload must have source.ext.schain param', function () { @@ -403,7 +383,7 @@ describe('TheMediaGrid Adapter', function () { it('if content and segment is present in jwTargeting, payload must have right params', function () { const jsContent = {id: 'test_jw_content_id'}; const jsSegments = ['test_seg_1', 'test_seg_2']; - const bidRequestsWithUserIds = bidRequests.map((bid) => { + const bidRequestsWithJwTargeting = bidRequests.map((bid) => { return Object.assign({ rtd: { jwplayer: { @@ -415,7 +395,7 @@ describe('TheMediaGrid Adapter', function () { } }, bid); }); - const request = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); + const request = spec.buildRequests(bidRequestsWithJwTargeting, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('user'); From 4b944e84d92cf129378157075c432cbc53a18dcf Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 21 Dec 2020 11:37:58 +0300 Subject: [PATCH 23/57] Remove digitrust userId from TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 5 ----- test/spec/modules/gridBidAdapter_spec.js | 14 +------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 68c8f43be24..6e610b67e0e 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -170,11 +170,6 @@ export const spec = { userExt.eids = [...userIdAsEids]; } - if (userId && userId.digitrustid && userId.digitrustid.data && userId.digitrustid.data.id) { - userExt = userExt || {}; - userExt.digitrust = Object.assign({}, userId.digitrustid.data); - } - if (userExt && Object.keys(userExt).length) { user = user || {}; user.ext = userExt; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 14c635f3918..084c67562e6 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -332,10 +332,7 @@ describe('TheMediaGrid Adapter', function () { ]; const bidRequestsWithUserIds = bidRequests.map((bid) => { return Object.assign({ - userIdAsEids: eids, - userId: { - digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}} - } + userIdAsEids: eids }, bid); }); const request = spec.buildRequests(bidRequestsWithUserIds, bidderRequest); @@ -343,15 +340,6 @@ describe('TheMediaGrid Adapter', function () { const payload = parseRequest(request.data); expect(payload).to.have.property('user'); expect(payload.user).to.have.property('ext'); - expect(payload.user.ext.digitrust).to.deep.equal({ - id: 'DTID', - keyv: 4, - privacy: { - optout: false - }, - producer: 'ABC', - version: 2 - }); expect(payload.user.ext.eids).to.deep.equal(eids); }); From 01e4c8e0e7524ee2397b1013eddac5ca0cb0fc68 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Thu, 11 Feb 2021 13:39:00 +0300 Subject: [PATCH 24/57] Protocols was added in video section in ad request for TheMediaGrid Bid Adapter --- modules/gridBidAdapter.js | 6 +++++- test/spec/modules/gridBidAdapter_spec.js | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 6e610b67e0e..b296fe39ae4 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -365,7 +365,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { } function createVideoRequest(bid, mediaType) { - const {playerSize, mimes, durationRangeSec} = mediaType; + const {playerSize, mimes, durationRangeSec, protocols} = mediaType; const size = (playerSize || bid.sizes || [])[0]; if (!size) return; @@ -380,6 +380,10 @@ function createVideoRequest(bid, mediaType) { result.maxduration = durationRangeSec[1]; } + if (protocols && protocols.length) { + result.protocols = protocols; + } + return result; } diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 084c67562e6..b4e87119111 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -105,7 +105,8 @@ describe('TheMediaGrid Adapter', function () { 'sizes': [[728, 90]], 'mediaTypes': { 'video': { - 'playerSize': [[400, 600]] + 'playerSize': [[400, 600]], + 'protocols': [1, 2, 3] }, 'banner': { 'sizes': [[728, 90]] @@ -281,7 +282,8 @@ describe('TheMediaGrid Adapter', function () { }, 'video': { 'w': 400, - 'h': 600 + 'h': 600, + 'protocols': [1, 2, 3] } }] }); From 97e1c5ea8cbb65406e8ace1a9cab224ae1812b33 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Fri, 26 Feb 2021 18:42:18 +0300 Subject: [PATCH 25/57] TheMediaGrid: fix trouble with alias using --- modules/gridBidAdapter.js | 1 - test/spec/modules/gridBidAdapter_spec.js | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index b296fe39ae4..8ab2e2b8a95 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -329,7 +329,6 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { if (bid) { const bidResponse = { requestId: bid.bidId, // bid.bidderRequestId, - bidderCode: spec.code, cpm: serverBid.price, width: serverBid.w, height: serverBid.h, diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index b4e87119111..df119bb689d 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -491,7 +491,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 250, 'ad': '
test content 1
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -549,7 +548,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 250, 'ad': '
test content 1
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -563,7 +561,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 600, 'ad': '
test content 2
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -577,7 +574,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 728, 'height': 90, 'ad': '
test content 3
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -637,7 +633,6 @@ describe('TheMediaGrid Adapter', function () { 'dealId': undefined, 'width': 300, 'height': 600, - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': false, @@ -654,7 +649,6 @@ describe('TheMediaGrid Adapter', function () { 'dealId': undefined, 'width': undefined, 'height': undefined, - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': false, @@ -786,7 +780,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 250, 'ad': '
test content 1
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -800,7 +793,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 600, 'ad': '
test content 2
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -814,7 +806,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 728, 'height': 90, 'ad': '
test content 3
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, @@ -828,7 +819,6 @@ describe('TheMediaGrid Adapter', function () { 'width': 300, 'height': 600, 'ad': '
test content 4
', - 'bidderCode': 'grid', 'currency': 'USD', 'mediaType': 'banner', 'netRevenue': false, From 21cad2be3cd45a8616495ebbe62ce982d5252c32 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 1 Mar 2021 18:50:49 +0300 Subject: [PATCH 26/57] TheMediaGridNM: fix trouble with alias --- modules/gridNMBidAdapter.js | 1 - test/spec/modules/gridNMBidAdapter_spec.js | 2 -- 2 files changed, 3 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index ffd6c1b250c..af1e9f84f43 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -134,7 +134,6 @@ export const spec = { } const bidResponse = { requestId: bid.bidId, - bidderCode: spec.code, cpm: serverBid.price, width: serverBid.w, height: serverBid.h, diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index 0dbaac0c526..2aec9713000 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -300,7 +300,6 @@ describe('TheMediaGridNM Adapter', function () { 'dealId': 11, 'width': 300, 'height': 250, - 'bidderCode': 'gridNM', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': false, @@ -317,7 +316,6 @@ describe('TheMediaGridNM Adapter', function () { 'dealId': undefined, 'width': 300, 'height': 600, - 'bidderCode': 'gridNM', 'currency': 'USD', 'mediaType': 'video', 'netRevenue': false, From da4e7a3408874a91de32289b49fbffbdba9cb2b4 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Fri, 16 Apr 2021 19:33:02 +0300 Subject: [PATCH 27/57] TheMediaGrid Bid Adapter: added support of PBAdSlot module --- modules/gridBidAdapter.js | 8 ++++- test/spec/modules/gridBidAdapter_spec.js | 42 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 964b34dcfa2..994244b8a50 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -76,7 +76,7 @@ export const spec = { if (!userIdAsEids) { userIdAsEids = bid.userIdAsEids; } - const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd} = bid; + const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd, ortb2Imp} = bid; bidsMap[bidId] = bid; if (!pageKeywords && !utils.isEmpty(keywords)) { pageKeywords = utils.transformBidderParamKeywords(keywords); @@ -98,6 +98,12 @@ export const spec = { divid: adUnitCode } }; + if (ortb2Imp && ortb2Imp.ext && ortb2Imp.ext.data) { + impObj.ext.data = ortb2Imp.ext.data; + if (impObj.ext.data.adserver && impObj.ext.data.adserver.adslot) { + impObj.ext.gpid = impObj.ext.data.adserver.adslot; + } + } if (bidFloor) { impObj.bidfloor = bidFloor; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 2e8601bddf6..d78bcf225d1 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -428,6 +428,48 @@ describe('TheMediaGrid Adapter', function () { expect(payload.tmax).to.equal(3000); getConfigStub.restore(); }); + it('should contain imp[].ext.data.adserver if availabel', function() { + const ortb2Imp = [{ + ext: { + data: { + adserver: { + name: 'ad_server_name', + adslot: '/111111/slot' + }, + pbadslot: '/111111/slot' + } + } + }, { + ext: { + data: { + adserver: { + name: 'ad_server_name', + adslot: '/222222/slot' + }, + pbadslot: '/222222/slot' + } + } + }]; + const bidRequestsWithOrtb2Imp = bidRequests.slice(0, 3).map((bid, ind) => { + return Object.assign(ortb2Imp[ind] ? { ortb2Imp: ortb2Imp[ind] } : {}, bid); + }); + const request = spec.buildRequests(bidRequestsWithOrtb2Imp, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].ext).to.deep.equal({ + divid: bidRequests[0].adUnitCode, + data: ortb2Imp[0].ext.data, + gpid: ortb2Imp[0].ext.data.adserver.adslot + }); + expect(payload.imp[1].ext).to.deep.equal({ + divid: bidRequests[1].adUnitCode, + data: ortb2Imp[1].ext.data, + gpid: ortb2Imp[1].ext.data.adserver.adslot + }); + expect(payload.imp[2].ext).to.deep.equal({ + divid: bidRequests[2].adUnitCode + }); + }); describe('floorModule', function () { const floorTestData = { 'currency': 'USD', From 419fd897011200ba74c53672c4d8324d34b27814 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Wed, 21 Apr 2021 11:02:36 +0300 Subject: [PATCH 28/57] TheMediaGrid Bid Adapter: fix typo --- test/spec/modules/gridBidAdapter_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index d78bcf225d1..6cdef43b150 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -428,7 +428,7 @@ describe('TheMediaGrid Adapter', function () { expect(payload.tmax).to.equal(3000); getConfigStub.restore(); }); - it('should contain imp[].ext.data.adserver if availabel', function() { + it('should contain imp[].ext.data.adserver if available', function() { const ortb2Imp = [{ ext: { data: { From d76839fcb705bcea96b6d0cb89fb6e8cca8fbabd Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 24 May 2021 08:58:45 +0300 Subject: [PATCH 29/57] GridNM Bid Adapter: use absent in params data from mediaTypes --- modules/gridNMBidAdapter.js | 32 ++++++++-- test/spec/modules/gridNMBidAdapter_spec.js | 70 ++++++++++++++++++++++ 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index af1e9f84f43..a216522c620 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -24,6 +24,8 @@ const LOG_ERROR_MESS = { hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' }; +const VIDEO_KEYS = ['mimes', 'protocols', 'startdelay', 'placement', 'linearity', 'skip', 'skipmin', 'skipafter', 'sequence', 'battr', 'maxextended', 'minbitrate', 'maxbitrate', 'boxingallowed', 'playbackmethod', 'playbackend', 'delivery', 'pos', 'companionad', 'api', 'companiontype']; + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [ VIDEO ], @@ -39,11 +41,12 @@ export const spec = { !bid.params.secid || !utils.isStr(bid.params.secid) || !bid.params.pubid || !utils.isStr(bid.params.pubid); + const video = utils.deepAccess(bid, 'mediaTypes.video') || {}; + const { protocols = video.protocols, mimes = video.mimes } = utils.deepAccess(bid, 'params.video') || {}; if (!invalid) { - invalid = !bid.params.video || !bid.params.video.protocols || !bid.params.video.mimes; + invalid = !protocols || !mimes; } if (!invalid) { - const {protocols, mimes} = bid.params.video; invalid = !utils.isArray(mimes) || !mimes.length || mimes.filter((it) => !(it && utils.isStr(it))).length; if (!invalid) { invalid = !utils.isArray(protocols) || !protocols.length || protocols.filter((it) => !(utils.isNumber(it) && it > 0 && !(it % 1))).length; @@ -63,7 +66,7 @@ export const spec = { const requests = []; bids.forEach(bid => { - const {params, bidderRequestId, sizes} = bid; + const { params, bidderRequestId, sizes } = bid; const payload = { sizes: utils.parseSizesInput(sizes).join(','), r: bidderRequestId, @@ -91,11 +94,32 @@ export const spec = { } } + const video = utils.deepAccess(bid, 'mediaTypes.video') || {}; + const paramsVideo = Object.assign({}, params.video); + VIDEO_KEYS.forEach((key) => { + if (!(key in paramsVideo) && key in video) { + paramsVideo[key] = video[key]; + } + }); + + if (!paramsVideo.size && video.playerSize && video.playerSize.length === 2) { + paramsVideo.size = video.playerSize.join('x'); + } + + if (!('mind' in paramsVideo) && 'minduration' in video) { + paramsVideo.mind = video.minduration; + } + if (!('maxd' in paramsVideo) && 'maxduration' in video) { + paramsVideo.maxd = video.maxduration; + } + + const paramsToSend = Object.assign({}, params, {video: paramsVideo}); + requests.push({ method: 'POST', url: ENDPOINT_URL + '?' + utils.parseQueryStringParameters(payload).replace(/\&$/, ''), bid: bid, - data: params // content + data: paramsToSend // content }); }); diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index 2aec9713000..ed30d7caab2 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -132,6 +132,43 @@ describe('TheMediaGridNM Adapter', function () { expect(spec.isBidRequestValid(invalidBid)).to.equal(false); }); }); + + it('should return true when required params is absent, but available in mediaTypes', function () { + const paramsList = [ + { + 'source': 'jwp', + 'secid': '11', + 'pubid': '22', + 'video': { + 'protocols': [1, 2, 3, 4, 5, 6] + } + }, + { + 'source': 'jwp', + 'secid': '11', + 'pubid': '22', + 'video': { + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + } + } + ]; + + const mediaTypes = { + video: { + mimes: ['video/mp4', 'video/x-ms-wmv'], + playerSize: [200, 300], + protocols: [1, 2, 3, 4, 5, 6] + } + }; + + paramsList.forEach((params) => { + const validBid = Object.assign({}, bid); + delete validBid.params; + validBid.params = params; + validBid.mediaTypes = mediaTypes; + expect(spec.isBidRequestValid(validBid)).to.equal(true); + }); + }); }); describe('buildRequests', function () { @@ -198,6 +235,39 @@ describe('TheMediaGridNM Adapter', function () { }); }); + it('should attach valid params from mediaTypes', function () { + const mediaTypes = { + video: { + skipafter: 10, + minduration: 10, + maxduration: 100, + protocols: [1, 3, 4], + playerSize: [300, 250] + } + }; + const bidRequest = Object.assign({ mediaTypes }, bidRequests[0]); + const req = spec.buildRequests([bidRequest], bidderRequest)[0]; + const expectedVideo = { + 'skipafter': 10, + 'mind': 10, + 'maxd': 100, + 'mimes': ['video/mp4', 'video/x-ms-wmv'], + 'protocols': [1, 2, 3, 4, 5, 6], + 'size': '300x250' + }; + const expectedParams = Object.assign({}, bidRequest.params); + expectedParams.video = Object.assign(expectedParams.video, expectedVideo); + + expect(req.url).to.be.an('string'); + const payload = parseRequestUrl(req.url); + expect(payload).to.have.property('u', referrer); + expect(payload).to.have.property('r', '22edbae2733bf6'); + expect(payload).to.have.property('wrapperType', 'Prebid_js'); + expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); + expect(payload).to.have.property('sizes', '300x250,300x600'); + expect(req.data).to.deep.equal(expectedParams); + }); + it('if gdprConsent is present payload must have gdpr params', function () { const [request] = spec.buildRequests([bidRequests[0]], {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); expect(request.url).to.be.an('string'); From 126b9afc70d119b06fece63def0edb89b25858a4 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 24 May 2021 21:44:39 +0300 Subject: [PATCH 30/57] GridNM Bid Adapter: fix md file + add advertiserDomains support --- modules/gridNMBidAdapter.js | 3 +++ modules/gridNMBidAdapter.md | 10 ++++------ test/spec/modules/gridNMBidAdapter_spec.js | 11 ++++++++++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index a216522c620..6876cd4f83c 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -168,6 +168,9 @@ export const spec = { dealId: serverBid.dealid, vastXml: serverBid.adm, mediaType: VIDEO, + meta: { + advertiserDomains: serverBid && serverBid.adomain ? serverBid.adomain : [] + }, adResponse: { content: serverBid.adm } diff --git a/modules/gridNMBidAdapter.md b/modules/gridNMBidAdapter.md index 6decdde7f4c..57583c93fba 100644 --- a/modules/gridNMBidAdapter.md +++ b/modules/gridNMBidAdapter.md @@ -17,7 +17,9 @@ Grid bid adapter supports Banner and Video (instream and outstream). mediaTypes: { video: { playerSize: [728, 90], - context: 'outstream' + context: 'outstream', + mimes: ['video/mp4', 'video/x-ms-wmv'], + protocols: [1,2,3,4,5,6] } }, bids: [ @@ -26,11 +28,7 @@ Grid bid adapter supports Banner and Video (instream and outstream). params: { source: 'jwp', secid: '11', - pubid: '22', - video: { - mimes: ['video/mp4', 'video/x-ms-wmv'], - protocols: [1,2,3,4,5,6] - } + pubid: '22' } } ] diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index ed30d7caab2..3b5740cb045 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -305,7 +305,7 @@ describe('TheMediaGridNM Adapter', function () { describe('interpretResponse', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 250, 'w': 300, 'dealid': 11}], 'seat': '2'}, - {'bid': [{'price': 0.5, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 600, 'w': 300}], 'seat': '2'}, + {'bid': [{'price': 0.5, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 600, 'w': 300, adomain: ['my_domain.ru']}], 'seat': '2'}, {'bid': [{'price': 0, 'h': 250, 'w': 300}], 'seat': '2'}, {'bid': [{'price': 0, 'adm': '\n<\/Ad>\n<\/VAST>', 'h': 250, 'w': 300}], 'seat': '2'}, undefined, @@ -375,6 +375,9 @@ describe('TheMediaGridNM Adapter', function () { 'netRevenue': false, 'ttl': 360, 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'meta': { + 'advertiserDomains': [] + }, 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' } @@ -391,6 +394,9 @@ describe('TheMediaGridNM Adapter', function () { 'netRevenue': false, 'ttl': 360, 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'meta': { + 'advertiserDomains': ['my_domain.ru'] + }, 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' } @@ -422,6 +428,9 @@ describe('TheMediaGridNM Adapter', function () { 'bidId': '2bc598e42b6a', 'bidderRequestId': '39d74f5b71464', 'auctionId': '1cbd2feafe5e8b', + 'meta': { + 'advertiserDomains': [] + }, 'mediaTypes': { 'video': { 'context': 'instream' From 350429598b5dcc40f172f811c74acab2d8af1ff5 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 31 May 2021 18:59:29 +0300 Subject: [PATCH 31/57] TheMediaGrid and gridNM Bid Adapter: minor netRevenue fixes --- modules/gridBidAdapter.js | 2 +- modules/gridNMBidAdapter.js | 2 +- test/spec/modules/gridBidAdapter_spec.js | 20 ++++++++++---------- test/spec/modules/gridNMBidAdapter_spec.js | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index dbeba27d836..d4aceaea162 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -347,7 +347,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { height: serverBid.h, creativeId: serverBid.auid, // bid.bidId, currency: 'USD', - netRevenue: false, + netRevenue: true, ttl: TIME_TO_LIVE, meta: { advertiserDomains: serverBid.adomain ? serverBid.adomain : [] diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index 6876cd4f83c..0ee076d9ae8 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -163,7 +163,7 @@ export const spec = { height: serverBid.h, creativeId: serverBid.auid || bid.bidderRequestId, currency: 'USD', - netRevenue: false, + netRevenue: true, ttl: TIME_TO_LIVE, dealId: serverBid.dealid, vastXml: serverBid.adm, diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index e161d82fdd6..4f5f62f2cb8 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -555,7 +555,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 1
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -615,7 +615,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 1
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -631,7 +631,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 2
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -647,7 +647,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 3
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -709,7 +709,7 @@ describe('TheMediaGrid Adapter', function () { 'height': 600, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -728,7 +728,7 @@ describe('TheMediaGrid Adapter', function () { 'height': undefined, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -862,7 +862,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 1
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -878,7 +878,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 2
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -894,7 +894,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 3
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] @@ -910,7 +910,7 @@ describe('TheMediaGrid Adapter', function () { 'ad': '
test content 4
', 'currency': 'USD', 'mediaType': 'banner', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'meta': { advertiserDomains: [] diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index 3b5740cb045..6d3a607c0c5 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -372,7 +372,7 @@ describe('TheMediaGridNM Adapter', function () { 'height': 250, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'meta': { @@ -391,7 +391,7 @@ describe('TheMediaGridNM Adapter', function () { 'height': 600, 'currency': 'USD', 'mediaType': 'video', - 'netRevenue': false, + 'netRevenue': true, 'ttl': 360, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'meta': { From 271b24c23e37fd46e001b4e6d3aef0a0361b179c Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Wed, 2 Jun 2021 18:43:43 +0300 Subject: [PATCH 32/57] gridNM Bid Adapter updates after review --- modules/gridNMBidAdapter.js | 2 +- modules/gridNMBidAdapter.md | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index 0ee076d9ae8..4ab8464b115 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -169,7 +169,7 @@ export const spec = { vastXml: serverBid.adm, mediaType: VIDEO, meta: { - advertiserDomains: serverBid && serverBid.adomain ? serverBid.adomain : [] + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] }, adResponse: { content: serverBid.adm diff --git a/modules/gridNMBidAdapter.md b/modules/gridNMBidAdapter.md index 57583c93fba..6decdde7f4c 100644 --- a/modules/gridNMBidAdapter.md +++ b/modules/gridNMBidAdapter.md @@ -17,9 +17,7 @@ Grid bid adapter supports Banner and Video (instream and outstream). mediaTypes: { video: { playerSize: [728, 90], - context: 'outstream', - mimes: ['video/mp4', 'video/x-ms-wmv'], - protocols: [1,2,3,4,5,6] + context: 'outstream' } }, bids: [ @@ -28,7 +26,11 @@ Grid bid adapter supports Banner and Video (instream and outstream). params: { source: 'jwp', secid: '11', - pubid: '22' + pubid: '22', + video: { + mimes: ['video/mp4', 'video/x-ms-wmv'], + protocols: [1,2,3,4,5,6] + } } } ] From e29987400289dccfd19298054f3dc6b931e755ba Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Thu, 1 Jul 2021 19:20:02 +0300 Subject: [PATCH 33/57] TheMediaGrid Bid Adapter: fix keywords workflow --- modules/gridBidAdapter.js | 48 +++++++++++++----------- test/spec/modules/gridBidAdapter_spec.js | 42 +++++++++++++++++++-- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index d4aceaea162..cb13adb1c79 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -78,9 +78,6 @@ export const spec = { } const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd, ortb2Imp} = bid; bidsMap[bidId] = bid; - if (!pageKeywords && !utils.isEmpty(keywords)) { - pageKeywords = utils.transformBidderParamKeywords(keywords); - } const bidFloor = _getFloor(mediaTypes || {}, bid); const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; if (jwTargeting) { @@ -104,6 +101,12 @@ export const spec = { impObj.ext.gpid = impObj.ext.data.adserver.adslot; } } + if (!utils.isEmpty(keywords)) { + if (!pageKeywords) { + pageKeywords = keywords; + } + impObj.ext.bidder = { keywords }; + } if (bidFloor) { impObj.bidfloor = bidFloor; @@ -185,17 +188,28 @@ export const spec = { request.user = user; } - const configKeywords = utils.transformBidderParamKeywords({ - 'user': utils.deepAccess(config.getConfig('ortb2.user'), 'keywords') || null, - 'context': utils.deepAccess(config.getConfig('ortb2.site'), 'keywords') || null - }); + const userKeywords = utils.deepAccess(config.getConfig('ortb2.user'), 'keywords') || null; + const siteKeywords = utils.deepAccess(config.getConfig('ortb2.site'), 'keywords') || null; - if (configKeywords.length) { - pageKeywords = (pageKeywords || []).concat(configKeywords); + if (userKeywords) { + pageKeywords = pageKeywords || {}; + pageKeywords.user = pageKeywords.user || {}; + pageKeywords.user.ortb2 = [ + { + name: 'keywords', + keywords: userKeywords.split(','), + } + ]; } - - if (pageKeywords && pageKeywords.length > 0) { - pageKeywords.forEach(deleteValues); + if (siteKeywords) { + pageKeywords = pageKeywords || {}; + pageKeywords.site = pageKeywords.site || {}; + pageKeywords.site.ortb2 = [ + { + name: 'keywords', + keywords: siteKeywords.split(','), + } + ]; } if (pageKeywords) { @@ -311,16 +325,6 @@ function _getFloor (mediaTypes, bid) { return floor; } -function isPopulatedArray(arr) { - return !!(utils.isArray(arr) && arr.length > 0); -} - -function deleteValues(keyPairObj) { - if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { - delete keyPairObj.value; - } -} - function _getBidFromResponse(respItem) { if (!respItem) { utils.logError(LOG_ERROR_MESS.emptySeatbid); diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 4f5f62f2cb8..2e5d2e63677 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -402,11 +402,47 @@ describe('TheMediaGrid Adapter', function () { it('should contain the keyword values if it present in ortb2.(site/user)', function () { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( - arg => arg === 'ortb2.user' ? {'keywords': 'foo'} : (arg === 'ortb2.site' ? {'keywords': 'bar'} : null)); - const request = spec.buildRequests([bidRequests[0]], bidderRequest); + arg => arg === 'ortb2.user' ? {'keywords': 'foo,any'} : (arg === 'ortb2.site' ? {'keywords': 'bar'} : null)); + const keywords = { + 'site': { + 'somePublisher': [ + { + 'name': 'someName', + 'brandsafety': ['disaster'], + 'topic': ['stress', 'fear'] + } + ] + } + }; + const bidRequestWithKW = { ...bidRequests[0], params: { ...bidRequests[0].params, keywords } } + const request = spec.buildRequests([bidRequestWithKW], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); - expect(payload.ext.keywords).to.deep.equal([{'key': 'user', 'value': ['foo']}, {'key': 'context', 'value': ['bar']}]); + expect(payload.ext.keywords).to.deep.equal({ + 'site': { + 'somePublisher': [ + { + 'name': 'someName', + 'brandsafety': ['disaster'], + 'topic': ['stress', 'fear'] + } + ], + 'ortb2': [ + { + 'name': 'keywords', + 'keywords': ['bar'] + } + ] + }, + 'user': { + 'ortb2': [ + { + 'name': 'keywords', + 'keywords': ['foo', 'any'] + } + ] + } + }); getConfigStub.restore(); }); From dc7263ebbbc86e2fb062cce370bdeac2f03418cb Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 6 Jul 2021 07:45:27 -0700 Subject: [PATCH 34/57] fix testing and kick off lgtm again --- modules/gridBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index cb13adb1c79..16f8ff11574 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -345,11 +345,11 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { const bid = bidRequest.bidsMap[serverBid.impid]; if (bid) { const bidResponse = { - requestId: bid.bidId, // bid.bidderRequestId, + requestId: bid.bidId, // bid.bidderRequestId cpm: serverBid.price, width: serverBid.w, height: serverBid.h, - creativeId: serverBid.auid, // bid.bidId, + creativeId: serverBid.auid, // bid.bidId currency: 'USD', netRevenue: true, ttl: TIME_TO_LIVE, From 2a66d3dfc32ae1e073e1f0b9f82cb26483c80ab7 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Wed, 14 Jul 2021 18:23:01 +0300 Subject: [PATCH 35/57] TheMediaGrid: added ext.bidder.grid.demandSource processing --- modules/gridBidAdapter.js | 4 ++ test/spec/modules/gridBidAdapter_spec.js | 62 ++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 854b82aa84d..52ef1b402e8 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -362,6 +362,10 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { dealId: serverBid.dealid }; + if (serverBid.ext && serverBid.ext.bidder && serverBid.ext.bidder.grid && serverBid.ext.bidder.grid.demandSource) { + bidResponse.adserverTargeting = { 'hb_ds': serverBid.ext.bidder.grid.demandSource }; + } + if (serverBid.content_type === 'video') { bidResponse.vastXml = serverBid.adm; bidResponse.mediaType = VIDEO; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 1ec0200885c..8aa743586f5 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -985,6 +985,68 @@ describe('TheMediaGrid Adapter', function () { const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); expect(result).to.deep.equal(expectedResponse); }); + + it('response with ext.bidder.grid.demandSource', function () { + const bidRequests = [ + { + 'bidder': 'grid', + 'params': { + 'uid': '1' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '26d6f897b516', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + } + ]; + const serverResponse = { + 'bid': [ + { + 'impid': '26d6f897b516', + 'price': 1.15, + 'adm': '
test content 1
', + 'auid': 1, + 'h': 250, + 'w': 300, + 'dealid': 11, + 'ext': { + 'bidder': { + 'grid': { + 'demandSource': 'someValue' + } + } + } + } + ], + 'seat': '1' + }; + const request = spec.buildRequests(bidRequests); + const expectedResponse = [ + { + 'requestId': '26d6f897b516', + 'cpm': 1.15, + 'creativeId': 1, + 'dealId': 11, + 'width': 300, + 'height': 250, + 'ad': '
test content 1
', + 'currency': 'USD', + 'mediaType': 'banner', + 'netRevenue': true, + 'ttl': 360, + 'meta': { + advertiserDomains: [] + }, + 'adserverTargeting': { + 'hb_ds': 'someValue' + } + } + ]; + + const result = spec.interpretResponse({'body': {'seatbid': [serverResponse]}}, request); + expect(result).to.deep.equal(expectedResponse); + }); }); describe('user sync', function () { From aba9510ed42735632a5fdc06ed7f6c11426e84b8 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 9 Aug 2021 19:41:26 +0300 Subject: [PATCH 36/57] TheMediaGrid: added user.id from fpd cookie --- modules/gridBidAdapter.js | 36 +++++++++++++++++----- test/spec/modules/gridBidAdapter_spec.js | 39 +++++++++++++++++++++++- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 0da15499e0c..883d94051e5 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -3,15 +3,17 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; +const USER_ID_COOKIE_EXP = 60 * 60 * 24 * 30 * 12 * 1000; // 1 year +const USER_ID_KEY = 'tmguid'; +const GVLID = 686; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; - -let hasSynced = false; - +export const storage = getStorageManager(GVLID, BIDDER_CODE); const LOG_ERROR_MESS = { noAuid: 'Bid from response has no auid parameter - ', noAdm: 'Bid from response has no adm parameter - ', @@ -23,6 +25,9 @@ const LOG_ERROR_MESS = { hasEmptySeatbidArray: 'Response has empty seatbid array', hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' }; + +let hasSynced = false; + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [ BANNER, VIDEO ], @@ -50,7 +55,6 @@ export const spec = { let jwpseg = null; let content = null; let schain = null; - let userId = null; let userIdAsEids = null; let user = null; let userExt = null; @@ -70,9 +74,6 @@ export const spec = { if (!schain) { schain = bid.schain; } - if (!userId) { - userId = bid.userId; - } if (!userIdAsEids) { userIdAsEids = bid.userIdAsEids; } @@ -99,6 +100,8 @@ export const spec = { impObj.ext.data = ortb2Imp.ext.data; if (impObj.ext.data.adserver && impObj.ext.data.adserver.adslot) { impObj.ext.gpid = impObj.ext.data.adserver.adslot; + } else { + impObj.ext.gpid = ortb2Imp.ext.data.pbadslot; } } if (!utils.isEmpty(keywords)) { @@ -184,6 +187,13 @@ export const spec = { user.ext = userExt; } + const fpdUserId = getUserIdFromFPDCookie(); + + if (fpdUserId) { + user = user || {}; + user.id = fpdUserId; + } + if (user) { request.user = user; } @@ -427,6 +437,18 @@ function createBannerRequest(bid, mediaType) { return result; } +function makeNewUserIdInFPDCookie() { + const value = utils.generateUUID().replace(/-/g, ''); + const expires = new Date(Date.now() + USER_ID_COOKIE_EXP).toISOString(); + + storage.setCookie(USER_ID_KEY, value, expires); + return value; +} + +function getUserIdFromFPDCookie() { + return storage.getCookie(USER_ID_KEY) || makeNewUserIdInFPDCookie(); +} + function reformatKeywords(pageKeywords) { const formatedPageKeywords = {}; Object.keys(pageKeywords).forEach((name) => { diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 1044139a507..71a7b458566 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec, resetUserSync, getSyncUrl } from 'modules/gridBidAdapter.js'; +import { spec, resetUserSync, getSyncUrl, storage } from 'modules/gridBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; @@ -119,6 +119,10 @@ describe('TheMediaGrid Adapter', function () { ]; it('should attach valid params to the tag', function () { + const fpdCookieVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; + const getStorageCookieStub = sinon.stub(storage, 'getCookie').callsFake( + arg => arg === 'tmguid' ? fpdCookieVal : null); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -132,6 +136,9 @@ describe('TheMediaGrid Adapter', function () { 'tid': bidderRequest.auctionId, 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} }, + 'user': { + 'id': fpdCookieVal + }, 'imp': [{ 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, @@ -144,9 +151,15 @@ describe('TheMediaGrid Adapter', function () { } }] }); + + getStorageCookieStub.restore(); }); it('make possible to process request without mediaTypes', function () { + const fpdCookieVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; + const getStorageCookieStub = sinon.stub(storage, 'getCookie').callsFake( + arg => arg === 'tmguid' ? fpdCookieVal : null); + const request = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -160,6 +173,9 @@ describe('TheMediaGrid Adapter', function () { 'tid': bidderRequest.auctionId, 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} }, + 'user': { + 'id': fpdCookieVal + }, 'imp': [{ 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, @@ -181,9 +197,15 @@ describe('TheMediaGrid Adapter', function () { } }] }); + + getStorageCookieStub.restore(); }); it('should attach valid params to the video tag', function () { + const fpdCookieVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; + const getStorageCookieStub = sinon.stub(storage, 'getCookie').callsFake( + arg => arg === 'tmguid' ? fpdCookieVal : null); + const request = spec.buildRequests(bidRequests.slice(0, 3), bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -197,6 +219,9 @@ describe('TheMediaGrid Adapter', function () { 'tid': bidderRequest.auctionId, 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} }, + 'user': { + 'id': fpdCookieVal + }, 'imp': [{ 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, @@ -227,9 +252,15 @@ describe('TheMediaGrid Adapter', function () { } }] }); + + getStorageCookieStub.restore(); }); it('should support mixed mediaTypes', function () { + const fpdCookieVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; + const getStorageCookieStub = sinon.stub(storage, 'getCookie').callsFake( + arg => arg === 'tmguid' ? fpdCookieVal : null); + const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -243,6 +274,9 @@ describe('TheMediaGrid Adapter', function () { 'tid': bidderRequest.auctionId, 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} }, + 'user': { + 'id': fpdCookieVal + }, 'imp': [{ 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, @@ -287,6 +321,8 @@ describe('TheMediaGrid Adapter', function () { } }] }); + + getStorageCookieStub.restore(); }); it('if gdprConsent is present payload must have gdpr params', function () { @@ -544,6 +580,7 @@ describe('TheMediaGrid Adapter', function () { divid: bidRequests[2].adUnitCode }); }); + describe('floorModule', function () { const floorTestData = { 'currency': 'USD', From 10bd4272e7c65eaf7adc46962703c6eb0d729b20 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Wed, 1 Sep 2021 00:16:27 +0300 Subject: [PATCH 37/57] TheMediaGrid: control cookie setting via bidder config --- modules/gridBidAdapter.js | 11 +++++++---- modules/gridBidAdapter.md | 11 +++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 883d94051e5..28c7bd5bfc1 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -438,11 +438,14 @@ function createBannerRequest(bid, mediaType) { } function makeNewUserIdInFPDCookie() { - const value = utils.generateUUID().replace(/-/g, ''); - const expires = new Date(Date.now() + USER_ID_COOKIE_EXP).toISOString(); + if (config.getConfig('cookieSettingAllowed')) { + const value = utils.generateUUID().replace(/-/g, ''); + const expires = new Date(Date.now() + USER_ID_COOKIE_EXP).toISOString(); - storage.setCookie(USER_ID_KEY, value, expires); - return value; + storage.setCookie(USER_ID_KEY, value, expires); + return value; + } + return null; } function getUserIdFromFPDCookie() { diff --git a/modules/gridBidAdapter.md b/modules/gridBidAdapter.md index 6a7075ccb00..25b10c71eab 100644 --- a/modules/gridBidAdapter.md +++ b/modules/gridBidAdapter.md @@ -9,6 +9,17 @@ Maintainer: grid-tech@themediagrid.com Module that connects to Grid demand source to fetch bids. Grid bid adapter supports Banner and Video (instream and outstream). +#Bidder Config +You can allow fpd cookies setting `pbjs.setBidderConfig` for the bidder `grid` +``` +pbjs.setBidderConfig({ + bidders: ["grid"], + config: { + cookieSettingAllowed: true + } + }) +``` + # Test Parameters ``` var adUnits = [ From 199324dd9ffb52acf67300e3944d9cf662efe4df Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Thu, 2 Sep 2021 17:57:48 +0300 Subject: [PATCH 38/57] TheMediaGrid: use localStorage instead cookie --- modules/gridBidAdapter.js | 14 ++++----- modules/gridBidAdapter.md | 4 +-- test/spec/modules/gridBidAdapter_spec.js | 40 ++++++++++++------------ 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 28c7bd5bfc1..b4b39fa16e9 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -9,7 +9,6 @@ const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; -const USER_ID_COOKIE_EXP = 60 * 60 * 24 * 30 * 12 * 1000; // 1 year const USER_ID_KEY = 'tmguid'; const GVLID = 686; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -187,7 +186,7 @@ export const spec = { user.ext = userExt; } - const fpdUserId = getUserIdFromFPDCookie(); + const fpdUserId = getUserIdFromFPDStorage(); if (fpdUserId) { user = user || {}; @@ -437,19 +436,18 @@ function createBannerRequest(bid, mediaType) { return result; } -function makeNewUserIdInFPDCookie() { - if (config.getConfig('cookieSettingAllowed')) { +function makeNewUserIdInFPDStorage() { + if (config.getConfig('localStorageWriteAllowed')) { const value = utils.generateUUID().replace(/-/g, ''); - const expires = new Date(Date.now() + USER_ID_COOKIE_EXP).toISOString(); - storage.setCookie(USER_ID_KEY, value, expires); + storage.setDataInLocalStorage(USER_ID_KEY, value); return value; } return null; } -function getUserIdFromFPDCookie() { - return storage.getCookie(USER_ID_KEY) || makeNewUserIdInFPDCookie(); +function getUserIdFromFPDStorage() { + return storage.getDataFromLocalStorage(USER_ID_KEY) || makeNewUserIdInFPDStorage(); } function reformatKeywords(pageKeywords) { diff --git a/modules/gridBidAdapter.md b/modules/gridBidAdapter.md index 25b10c71eab..8eb8dfc19fb 100644 --- a/modules/gridBidAdapter.md +++ b/modules/gridBidAdapter.md @@ -10,12 +10,12 @@ Module that connects to Grid demand source to fetch bids. Grid bid adapter supports Banner and Video (instream and outstream). #Bidder Config -You can allow fpd cookies setting `pbjs.setBidderConfig` for the bidder `grid` +You can allow writing in localStorage `pbjs.setBidderConfig` for the bidder `grid` ``` pbjs.setBidderConfig({ bidders: ["grid"], config: { - cookieSettingAllowed: true + localStorageWriteAllowed: true } }) ``` diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 71a7b458566..286587b1348 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -119,9 +119,9 @@ describe('TheMediaGrid Adapter', function () { ]; it('should attach valid params to the tag', function () { - const fpdCookieVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; - const getStorageCookieStub = sinon.stub(storage, 'getCookie').callsFake( - arg => arg === 'tmguid' ? fpdCookieVal : null); + const fpdUserIdVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; + const getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage').callsFake( + arg => arg === 'tmguid' ? fpdUserIdVal : null); const request = spec.buildRequests([bidRequests[0]], bidderRequest); expect(request.data).to.be.an('string'); @@ -137,7 +137,7 @@ describe('TheMediaGrid Adapter', function () { 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} }, 'user': { - 'id': fpdCookieVal + 'id': fpdUserIdVal }, 'imp': [{ 'id': bidRequests[0].bidId, @@ -152,13 +152,13 @@ describe('TheMediaGrid Adapter', function () { }] }); - getStorageCookieStub.restore(); + getDataFromLocalStorageStub.restore(); }); it('make possible to process request without mediaTypes', function () { - const fpdCookieVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; - const getStorageCookieStub = sinon.stub(storage, 'getCookie').callsFake( - arg => arg === 'tmguid' ? fpdCookieVal : null); + const fpdUserIdVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; + const getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage').callsFake( + arg => arg === 'tmguid' ? fpdUserIdVal : null); const request = spec.buildRequests([bidRequests[0], bidRequests[1]], bidderRequest); expect(request.data).to.be.an('string'); @@ -174,7 +174,7 @@ describe('TheMediaGrid Adapter', function () { 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} }, 'user': { - 'id': fpdCookieVal + 'id': fpdUserIdVal }, 'imp': [{ 'id': bidRequests[0].bidId, @@ -198,13 +198,13 @@ describe('TheMediaGrid Adapter', function () { }] }); - getStorageCookieStub.restore(); + getDataFromLocalStorageStub.restore(); }); it('should attach valid params to the video tag', function () { - const fpdCookieVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; - const getStorageCookieStub = sinon.stub(storage, 'getCookie').callsFake( - arg => arg === 'tmguid' ? fpdCookieVal : null); + const fpdUserIdVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; + const getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage').callsFake( + arg => arg === 'tmguid' ? fpdUserIdVal : null); const request = spec.buildRequests(bidRequests.slice(0, 3), bidderRequest); expect(request.data).to.be.an('string'); @@ -220,7 +220,7 @@ describe('TheMediaGrid Adapter', function () { 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} }, 'user': { - 'id': fpdCookieVal + 'id': fpdUserIdVal }, 'imp': [{ 'id': bidRequests[0].bidId, @@ -253,13 +253,13 @@ describe('TheMediaGrid Adapter', function () { }] }); - getStorageCookieStub.restore(); + getDataFromLocalStorageStub.restore(); }); it('should support mixed mediaTypes', function () { - const fpdCookieVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; - const getStorageCookieStub = sinon.stub(storage, 'getCookie').callsFake( - arg => arg === 'tmguid' ? fpdCookieVal : null); + const fpdUserIdVal = '0b0f84a1-1596-4165-9742-2e1a7dfac57f'; + const getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage').callsFake( + arg => arg === 'tmguid' ? fpdUserIdVal : null); const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data).to.be.an('string'); @@ -275,7 +275,7 @@ describe('TheMediaGrid Adapter', function () { 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} }, 'user': { - 'id': fpdCookieVal + 'id': fpdUserIdVal }, 'imp': [{ 'id': bidRequests[0].bidId, @@ -322,7 +322,7 @@ describe('TheMediaGrid Adapter', function () { }] }); - getStorageCookieStub.restore(); + getDataFromLocalStorageStub.restore(); }); it('if gdprConsent is present payload must have gdpr params', function () { From 54f0dc0f2687b7285df77026838aa1b109fb1dbc Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Thu, 16 Sep 2021 19:15:02 +0300 Subject: [PATCH 39/57] TheMediaGridNM Bid Adapter: update adapter to use /hbjson endpoint --- modules/gridNMBidAdapter.js | 248 +++++++++++++++++---- test/spec/modules/gridNMBidAdapter_spec.js | 127 +++++++---- 2 files changed, 284 insertions(+), 91 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index 4ab8464b115..1f9a9a98d87 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -1,10 +1,11 @@ import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; import { VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'gridNM'; -const ENDPOINT_URL = 'https://grid.bidswitch.net/hbnm'; +const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -64,62 +65,154 @@ export const spec = { buildRequests: function(validBidRequests, bidderRequest) { const bids = validBidRequests || []; const requests = []; + let { bidderRequestId, auctionId, gdprConsent, uspConsent, timeout, refererInfo } = bidderRequest || {}; + + const referer = refererInfo ? encodeURIComponent(refererInfo.referer) : ''; bids.forEach(bid => { - const { params, bidderRequestId, sizes } = bid; - const payload = { - sizes: utils.parseSizesInput(sizes).join(','), - r: bidderRequestId, - wrapperType: 'Prebid_js', - wrapperVersion: '$prebid.version$' - }; + let user; + let schain; + let userExt; + let siteContent; + let userIdAsEids; - if (bidderRequest) { - if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { - payload.u = bidderRequest.refererInfo.referer; - } - if (bidderRequest.timeout) { - payload.wtimeout = bidderRequest.timeout; - } - if (bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - payload.gdpr_consent = bidderRequest.gdprConsent.consentString; - } - payload.gdpr_applies = - (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') - ? Number(bidderRequest.gdprConsent.gdprApplies) : 1; + if (!bidderRequestId) { + bidderRequestId = bid.bidderRequestId; + } + if (!auctionId) { + auctionId = bid.auctionId; + } + if (!schain) { + schain = bid.schain; + } + if (!userIdAsEids) { + userIdAsEids = bid.userIdAsEids; + } + const { + params: { floorcpm, pubdata, source, secid, pubid, content, video }, + mediaTypes, bidId, adUnitCode, rtd, ortb2Imp, sizes + } = bid; + + const bidFloor = _getFloor(mediaTypes || {}, bid, utils.isNumber(floorcpm) && floorcpm); + const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; + const jwpseg = (pubdata && pubdata.jwpseg) || (jwTargeting && jwTargeting.segments); + + if (!siteContent) { + siteContent = content || (jwTargeting && jwTargeting.content); + } + + const impObj = { + id: bidId.toString(), + tagid: secid.toString(), + video: createVideoForImp(video, sizes, mediaTypes && mediaTypes.video), + ext: { + divid: adUnitCode.toString() } - if (bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent; + }; + + if (ortb2Imp && ortb2Imp.ext && ortb2Imp.ext.data) { + impObj.ext.data = ortb2Imp.ext.data; + if (impObj.ext.data.adserver && impObj.ext.data.adserver.adslot) { + impObj.ext.gpid = impObj.ext.data.adserver.adslot.toString(); + } else { + impObj.ext.gpid = ortb2Imp.ext.data.pbadslot && ortb2Imp.ext.data.pbadslot.toString(); } } - const video = utils.deepAccess(bid, 'mediaTypes.video') || {}; - const paramsVideo = Object.assign({}, params.video); - VIDEO_KEYS.forEach((key) => { - if (!(key in paramsVideo) && key in video) { - paramsVideo[key] = video[key]; + if (bidFloor) { + impObj.bidfloor = bidFloor; + } + + const imp = [impObj]; + + const reqSource = { + tid: auctionId && auctionId.toString(), + ext: { + wrapper: 'Prebid_js', + wrapper_version: '$prebid.version$' } - }); + }; + + if (schain) { + reqSource.ext.schain = schain; + } + + const bidderTimeout = config.getConfig('bidderTimeout') || timeout; + const tmax = timeout ? Math.min(bidderTimeout, timeout) : bidderTimeout; + + const request = { + id: bidderRequestId && bidderRequestId.toString(), + site: { + page: referer, + publisher: { + id: pubid, + }, + }, + source: reqSource, + tmax, + imp, + }; + + if (siteContent) { + request.site.content = siteContent; + } + + if (jwpseg && jwpseg.length) { + user = { + data: [{ + name: 'iow_labs_pub_data', + segment: jwpseg.map((seg) => { + return {name: 'jwpseg', value: seg}; + }) + }] + }; + } + + if (gdprConsent && gdprConsent.consentString) { + userExt = { consent: gdprConsent.consentString }; + } + + if (userIdAsEids && userIdAsEids.length) { + userExt = userExt || {}; + userExt.eids = [...userIdAsEids]; + } + + if (userExt && Object.keys(userExt).length) { + user = user || {}; + user.ext = userExt; + } - if (!paramsVideo.size && video.playerSize && video.playerSize.length === 2) { - paramsVideo.size = video.playerSize.join('x'); + if (user) { + request.user = user; } - if (!('mind' in paramsVideo) && 'minduration' in video) { - paramsVideo.mind = video.minduration; + if (gdprConsent && gdprConsent.gdprApplies) { + request.regs = { + ext: { + gdpr: gdprConsent.gdprApplies ? 1 : 0 + } + } } - if (!('maxd' in paramsVideo) && 'maxduration' in video) { - paramsVideo.maxd = video.maxduration; + + if (uspConsent) { + if (!request.regs) { + request.regs = { ext: {} }; + } + request.regs.ext.us_privacy = uspConsent; } - const paramsToSend = Object.assign({}, params, {video: paramsVideo}); + if (config.getConfig('coppa') === true) { + if (!request.regs) { + request.regs = {}; + } + request.regs.coppa = 1; + } requests.push({ method: 'POST', - url: ENDPOINT_URL + '?' + utils.parseQueryStringParameters(payload).replace(/\&$/, ''), + url: ENDPOINT_URL + '?no_mapping=1&sp=' + source, bid: bid, - data: paramsToSend // content + data: request }); }); @@ -151,11 +244,6 @@ export const spec = { else if (serverBid.content_type !== 'video') errorMessage = LOG_ERROR_MESS.wrongContentType + serverBid.content_type; if (!errorMessage) { const bid = bidRequest.bid; - if (!serverBid.w || !serverBid.h) { - const size = utils.parseSizesInput(bid.sizes)[0].split('x'); - serverBid.w = size[0]; - serverBid.h = size[1]; - } const bidResponse = { requestId: bid.bidId, cpm: serverBid.price, @@ -176,6 +264,10 @@ export const spec = { } }; + if (serverBid.nurl) { + bidResponse.vastUrl = serverBid.nurl; + } + if (!bid.renderer && (!bid.mediaTypes || !bid.mediaTypes.video || bid.mediaTypes.video.context === 'outstream')) { bidResponse.renderer = createRenderer(bidResponse, { id: bid.bidId, @@ -213,6 +305,33 @@ export const spec = { } }; +/** + * Gets bidfloor + * @param {Object} mediaTypes + * @param {Object} bid + * @param {Number} floor + * @returns {Number} floor + */ +function _getFloor (mediaTypes, bid, floor) { + const curMediaType = mediaTypes.video ? 'video' : 'banner'; + + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: curMediaType, + size: bid.sizes.map(([w, h]) => ({w, h})) + }); + + if (typeof floorInfo === 'object' && + floorInfo.currency === 'USD' && + !isNaN(parseFloat(floorInfo.floor))) { + floor = Math.max(floor, parseFloat(floorInfo.floor)); + } + } + + return floor; +} + function _getBidFromResponse(respItem) { if (!respItem) { utils.logError(LOG_ERROR_MESS.emptySeatbid); @@ -249,6 +368,45 @@ function createRenderer (bid, rendererParams) { return renderer; } +function createVideoForImp({ mind, maxd, size, ...paramsVideo }, bidSizes, bidVideo = {}) { + VIDEO_KEYS.forEach((key) => { + if (!(key in paramsVideo) && key in bidVideo) { + paramsVideo[key] = bidVideo[key]; + } + }); + + if (size && utils.isStr(size)) { + const sizeArray = size.split('x'); + if (sizeArray.length === 2 && parseInt(sizeArray[0]) && parseInt(sizeArray[1])) { + paramsVideo.w = parseInt(sizeArray[0]); + paramsVideo.h = parseInt(sizeArray[1]); + } + } + + if (!paramsVideo.w || !paramsVideo.h) { + const playerSizes = bidVideo.playerSize && bidVideo.playerSize.length === 2 ? bidVideo.playerSize : bidSizes; + if (playerSizes) { + const playerSize = playerSizes[0]; + if (playerSize) { + Object.assign(paramsVideo, utils.parseGPTSingleSizeArrayToRtbSize(playerSize)); + } + } + } + + const durationRangeSec = bidVideo.durationRangeSec || []; + const minDur = mind || durationRangeSec[0] || bidVideo.minduration; + const maxDur = maxd || durationRangeSec[1] || bidVideo.maxduration; + + if (minDur) { + paramsVideo.minduration = minDur; + } + if (maxDur) { + paramsVideo.maxduration = maxDur; + } + + return paramsVideo; +} + export function resetUserSync() { hasSynced = false; } diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index 6d3a607c0c5..baff5862fca 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -180,13 +180,19 @@ describe('TheMediaGridNM Adapter', function () { }); return res; } - const bidderRequest = {refererInfo: {referer: 'https://example.com'}}; - const referrer = bidderRequest.refererInfo.referer; + const bidderRequest = { + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + timeout: 3000, + refererInfo: { referer: 'https://example.com' } + }; + const referrer = encodeURIComponent(bidderRequest.refererInfo.referer); let bidRequests = [ { 'bidder': 'gridNM', 'params': { 'source': 'jwp', + 'floorcpm': 2, 'secid': '11', 'pubid': '22', 'video': { @@ -226,12 +232,36 @@ describe('TheMediaGridNM Adapter', function () { requests.forEach((req, i) => { expect(req.url).to.be.an('string'); const payload = parseRequestUrl(req.url); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('wrapperType', 'Prebid_js'); - expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); - expect(payload).to.have.property('sizes', requestsSizes[i]); - expect(req.data).to.deep.equal(bidRequests[i].params); + expect(payload).to.have.property('no_mapping', '1'); + expect(payload).to.have.property('sp', 'jwp'); + + const sizes = { w: bidRequests[i].sizes[0][0], h: bidRequests[i].sizes[0][1] }; + const impObj = { + 'id': bidRequests[i].bidId, + 'tagid': bidRequests[i].params.secid, + 'ext': {'divid': bidRequests[i].adUnitCode}, + 'video': Object.assign(sizes, bidRequests[i].params.video) + }; + + if (bidRequests[i].params.floorcpm) { + impObj.bidfloor = bidRequests[i].params.floorcpm; + } + + expect(req.data).to.deep.equal({ + 'id': bidderRequest.bidderRequestId, + 'site': { + 'page': referrer, + 'publisher': { + 'id': bidRequests[i].params.pubid + } + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': bidderRequest.auctionId, + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'imp': [impObj] + }); }); }); @@ -242,63 +272,68 @@ describe('TheMediaGridNM Adapter', function () { minduration: 10, maxduration: 100, protocols: [1, 3, 4], - playerSize: [300, 250] + playerSize: [[300, 250]] } }; const bidRequest = Object.assign({ mediaTypes }, bidRequests[0]); const req = spec.buildRequests([bidRequest], bidderRequest)[0]; const expectedVideo = { 'skipafter': 10, - 'mind': 10, - 'maxd': 100, + 'minduration': 10, + 'maxduration': 100, 'mimes': ['video/mp4', 'video/x-ms-wmv'], 'protocols': [1, 2, 3, 4, 5, 6], - 'size': '300x250' + 'w': 300, + 'h': 250 }; - const expectedParams = Object.assign({}, bidRequest.params); - expectedParams.video = Object.assign(expectedParams.video, expectedVideo); expect(req.url).to.be.an('string'); const payload = parseRequestUrl(req.url); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('r', '22edbae2733bf6'); - expect(payload).to.have.property('wrapperType', 'Prebid_js'); - expect(payload).to.have.property('wrapperVersion', '$prebid.version$'); - expect(payload).to.have.property('sizes', '300x250,300x600'); - expect(req.data).to.deep.equal(expectedParams); + expect(payload).to.have.property('no_mapping', '1'); + expect(payload).to.have.property('sp', 'jwp'); + expect(req.data).to.deep.equal({ + 'id': bidderRequest.bidderRequestId, + 'site': { + 'page': referrer, + 'publisher': { + 'id': bidRequest.params.pubid + } + }, + 'tmax': bidderRequest.timeout, + 'source': { + 'tid': bidderRequest.auctionId, + 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + }, + 'imp': [{ + 'id': bidRequest.bidId, + 'bidfloor': bidRequest.params.floorcpm, + 'tagid': bidRequest.params.secid, + 'ext': {'divid': bidRequest.adUnitCode}, + 'video': expectedVideo + }] + }); }); it('if gdprConsent is present payload must have gdpr params', function () { - const [request] = spec.buildRequests([bidRequests[0]], {gdprConsent: {consentString: 'AAA', gdprApplies: true}, refererInfo: bidderRequest.refererInfo}); - expect(request.url).to.be.an('string'); - const payload = parseRequestUrl(request.url); - expect(payload).to.have.property('u', referrer); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '1'); - }); - - it('if gdprApplies is false gdpr_applies must be 0', function () { - const [request] = spec.buildRequests([bidRequests[0]], {gdprConsent: {consentString: 'AAA', gdprApplies: false}}); - expect(request.url).to.be.an('string'); - const payload = parseRequestUrl(request.url); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '0'); - }); - - it('if gdprApplies is undefined gdpr_applies must be 1', function () { - const [request] = spec.buildRequests([bidRequests[0]], {gdprConsent: {consentString: 'AAA'}}); - expect(request.url).to.be.an('string'); - const payload = parseRequestUrl(request.url); - expect(payload).to.have.property('gdpr_consent', 'AAA'); - expect(payload).to.have.property('gdpr_applies', '1'); + const gdprBidderRequest = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); + const request = spec.buildRequests([bidRequests[0]], gdprBidderRequest)[0]; + const payload = request.data; + expect(request).to.have.property('data'); + expect(payload).to.have.property('user'); + expect(payload.user).to.have.property('ext'); + expect(payload.user.ext).to.have.property('consent', 'AAA'); + expect(payload).to.have.property('regs'); + expect(payload.regs).to.have.property('ext'); + expect(payload.regs.ext).to.have.property('gdpr', 1); }); it('if usPrivacy is present payload must have us_privacy param', function () { const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); - const [request] = spec.buildRequests([bidRequests[0]], bidderRequestWithUSP); - expect(request.url).to.be.an('string'); - const payload = parseRequestUrl(request.url); - expect(payload).to.have.property('us_privacy', '1YNN'); + const request = spec.buildRequests([bidRequests[0]], bidderRequestWithUSP)[0]; + const payload = request.data; + expect(payload).to.have.property('regs'); + expect(payload.regs).to.have.property('ext'); + expect(payload.regs.ext).to.have.property('us_privacy', '1YNN'); }); }); From 88f2a711cfffc6576f717a4944521fd8ea9d9b52 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Fri, 17 Sep 2021 12:38:35 +0300 Subject: [PATCH 40/57] TheMediaGridNM: fix unnecessary conditions --- modules/gridNMBidAdapter.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index 1f9a9a98d87..66a244e1607 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -71,10 +71,10 @@ export const spec = { bids.forEach(bid => { let user; - let schain; let userExt; - let siteContent; - let userIdAsEids; + + const schain = bid.schain; + const userIdAsEids = bid.userIdAsEids; if (!bidderRequestId) { bidderRequestId = bid.bidderRequestId; @@ -82,12 +82,6 @@ export const spec = { if (!auctionId) { auctionId = bid.auctionId; } - if (!schain) { - schain = bid.schain; - } - if (!userIdAsEids) { - userIdAsEids = bid.userIdAsEids; - } const { params: { floorcpm, pubdata, source, secid, pubid, content, video }, mediaTypes, bidId, adUnitCode, rtd, ortb2Imp, sizes @@ -97,9 +91,7 @@ export const spec = { const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; const jwpseg = (pubdata && pubdata.jwpseg) || (jwTargeting && jwTargeting.segments); - if (!siteContent) { - siteContent = content || (jwTargeting && jwTargeting.content); - } + const siteContent = content || (jwTargeting && jwTargeting.content); const impObj = { id: bidId.toString(), From 5e1b8731a896b45276ab34b2e7bf2d7c8d9c8f15 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 27 Sep 2021 19:16:05 +0300 Subject: [PATCH 41/57] TheMediaGrid: fix bug with nurl field in response --- modules/gridBidAdapter.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 9411f38ebd8..aba840793d4 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -382,8 +382,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { bidResponse.adResponse = { content: bidResponse.vastXml }; - } - if (serverBid.nurl) { + } else if (serverBid.nurl) { bidResponse.vastUrl = serverBid.nurl; } bidResponse.mediaType = VIDEO; From 9d99504e2f81fcb3610ddbc09586b5a62c6b6b49 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 28 Sep 2021 16:02:17 +0300 Subject: [PATCH 42/57] TheMediaGrid: update test --- test/spec/modules/gridBidAdapter_spec.js | 40 ++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 7913dd7b44d..f31b8f16ef7 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -874,6 +874,22 @@ describe('TheMediaGrid Adapter', function () { }, 'adUnitCode': 'adunit-code-2', 'sizes': [[300, 250], [300, 600]], + 'bidId': '112432ab4f34', + 'bidderRequestId': '5f2009617a7c0a', + 'auctionId': '1cbd2feafe5e8b', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } + }, + { + 'bidder': 'grid', + 'params': { + 'uid': '15' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[300, 250], [300, 600]], 'bidId': 'a74b342f8cd', 'bidderRequestId': '5f2009617a7c0a', 'auctionId': '1cbd2feafe5e8b', @@ -888,7 +904,8 @@ describe('TheMediaGrid Adapter', function () { {'bid': [{'impid': '659423fff799cb', 'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 11, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, {'bid': [{'impid': '2bc598e42b6a', 'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 12, content_type: 'video'}], 'seat': '2'}, {'bid': [{'impid': '23312a43bc42', 'price': 2.00, 'nurl': 'https://some_test_vast_url.com', 'auid': 13, content_type: 'video', 'adomain': ['example.com'], w: 300, h: 600}], 'seat': '2'}, - {'bid': [{'impid': 'a74b342f8cd', 'price': 1.50, 'nurl': '', 'auid': 14, content_type: 'video'}], 'seat': '2'} + {'bid': [{'impid': '112432ab4f34', 'price': 1.80, 'adm': '\n<\/Ad>\n<\/VAST>', 'nurl': 'https://wrong_url.com', 'auid': 14, content_type: 'video', 'adomain': ['example.com'], w: 300, h: 600}], 'seat': '2'}, + {'bid': [{'impid': 'a74b342f8cd', 'price': 1.50, 'nurl': '', 'auid': 15, content_type: 'video'}], 'seat': '2'} ]; const request = spec.buildRequests(bidRequests); const expectedResponse = [ @@ -945,7 +962,26 @@ describe('TheMediaGrid Adapter', function () { advertiserDomains: ['example.com'] }, 'vastUrl': 'https://some_test_vast_url.com', - } + }, + { + 'requestId': '112432ab4f34', + 'cpm': 1.80, + 'creativeId': 14, + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'meta': { + advertiserDomains: ['example.com'] + }, + 'vastXml': '\n<\/Ad>\n<\/VAST>', + 'adResponse': { + 'content': '\n<\/Ad>\n<\/VAST>' + } + }, ]; const result = spec.interpretResponse({'body': {'seatbid': response}}, request); From 930abadc0eaef698c514c8f68a604c32238fe892 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 4 Oct 2021 19:38:50 +0300 Subject: [PATCH 43/57] TheMediaGridNM: fix possible bug with nurl --- modules/gridNMBidAdapter.js | 13 +++---- test/spec/modules/gridNMBidAdapter_spec.js | 41 +++++++++++++++++++++- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/modules/gridNMBidAdapter.js b/modules/gridNMBidAdapter.js index 0daddb4ee42..3c46b25b8e1 100644 --- a/modules/gridNMBidAdapter.js +++ b/modules/gridNMBidAdapter.js @@ -231,7 +231,7 @@ export const spec = { if (!errorMessage && serverResponse.seatbid) { const serverBid = _getBidFromResponse(serverResponse.seatbid[0]); if (serverBid) { - if (!serverBid.adm) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); + if (!serverBid.adm && !serverBid.nurl) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else if (!serverBid.price) errorMessage = LOG_ERROR_MESS.noPrice + JSON.stringify(serverBid); else if (serverBid.content_type !== 'video') errorMessage = LOG_ERROR_MESS.wrongContentType + serverBid.content_type; if (!errorMessage) { @@ -246,17 +246,18 @@ export const spec = { netRevenue: true, ttl: TIME_TO_LIVE, dealId: serverBid.dealid, - vastXml: serverBid.adm, mediaType: VIDEO, meta: { advertiserDomains: serverBid.adomain ? serverBid.adomain : [] - }, - adResponse: { - content: serverBid.adm } }; - if (serverBid.nurl) { + if (serverBid.adm) { + bidResponse.vastXml = serverBid.adm; + bidResponse.adResponse = { + content: bidResponse.vastXml + }; + } else if (serverBid.nurl) { bidResponse.vastUrl = serverBid.nurl; } diff --git a/test/spec/modules/gridNMBidAdapter_spec.js b/test/spec/modules/gridNMBidAdapter_spec.js index baff5862fca..89efe942c1f 100644 --- a/test/spec/modules/gridNMBidAdapter_spec.js +++ b/test/spec/modules/gridNMBidAdapter_spec.js @@ -341,6 +341,7 @@ describe('TheMediaGridNM Adapter', function () { const responses = [ {'bid': [{'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 250, 'w': 300, 'dealid': 11}], 'seat': '2'}, {'bid': [{'price': 0.5, 'adm': '\n<\/Ad>\n<\/VAST>', 'content_type': 'video', 'h': 600, 'w': 300, adomain: ['my_domain.ru']}], 'seat': '2'}, + {'bid': [{'price': 2.00, 'nurl': 'https://some_test_vast_url.com', 'content_type': 'video', 'adomain': ['example.com'], 'w': 300, 'h': 600}], 'seat': '2'}, {'bid': [{'price': 0, 'h': 250, 'w': 300}], 'seat': '2'}, {'bid': [{'price': 0, 'adm': '\n<\/Ad>\n<\/VAST>', 'h': 250, 'w': 300}], 'seat': '2'}, undefined, @@ -394,6 +395,28 @@ describe('TheMediaGridNM Adapter', function () { 'context': 'instream' } } + }, + { + 'bidder': 'gridNM', + 'params': { + 'source': 'jwp', + 'secid': '11', + 'pubid': '22', + 'video': { + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 3], + } + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '127f4b12a432c', + 'bidderRequestId': 'a75bc868f32', + 'auctionId': '1cbd2feafe5e8b', + 'mediaTypes': { + 'video': { + 'context': 'instream' + } + } } ]; const requests = spec.buildRequests(bidRequests); @@ -435,6 +458,22 @@ describe('TheMediaGridNM Adapter', function () { 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' } + }, + { + 'requestId': '127f4b12a432c', + 'cpm': 2.00, + 'creativeId': 'a75bc868f32', + 'dealId': undefined, + 'width': 300, + 'height': 600, + 'currency': 'USD', + 'mediaType': 'video', + 'netRevenue': true, + 'ttl': 360, + 'meta': { + advertiserDomains: ['example.com'] + }, + 'vastUrl': 'https://some_test_vast_url.com', } ]; @@ -445,7 +484,7 @@ describe('TheMediaGridNM Adapter', function () { }); it('handles wrong and nobid responses', function () { - responses.slice(2).forEach((resp) => { + responses.slice(3).forEach((resp) => { const request = spec.buildRequests([{ 'bidder': 'gridNM', 'params': { From fad42bb0c094ae3beb737758c322ec010c2450a3 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 19 Oct 2021 19:38:54 +0300 Subject: [PATCH 44/57] TheMediaGrid: added alias as playwire --- modules/gridBidAdapter.js | 1 + modules/playwireBidAdapter.md | 61 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 modules/playwireBidAdapter.md diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 6c862a262b4..f62b62b7a97 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -29,6 +29,7 @@ let hasSynced = false; export const spec = { code: BIDDER_CODE, + aliases: ['playwire'], supportedMediaTypes: [ BANNER, VIDEO ], /** * Determines whether or not the given bid request is valid. diff --git a/modules/playwireBidAdapter.md b/modules/playwireBidAdapter.md new file mode 100644 index 00000000000..dddb57c9bc1 --- /dev/null +++ b/modules/playwireBidAdapter.md @@ -0,0 +1,61 @@ +# Overview + +Module Name: Playwire Bidder Adapter +Module Type: Bidder Adapter +Maintainer: grid-tech@themediagrid.com + +# Description + +Module that connects to Grid demand source to fetch bids. +The adapter is GDPR compliant and supports banner and video (instream and outstream). + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[300, 250]], + bids: [ + { + bidder: "playwire", + params: { + uid: '1', + bidFloor: 0.5 + } + } + ] + },{ + code: 'test-div', + sizes: [[728, 90]], + bids: [ + { + bidder: "playwire", + params: { + uid: 2, + keywords: { + brandsafety: ['disaster'], + topic: ['stress', 'fear'] + } + } + } + ] + }, + { + code: 'test-div', + sizes: [[728, 90]], + mediaTypes: { video: { + context: 'instream', + playerSize: [728, 90], + mimes: ['video/mp4'] + }, + bids: [ + { + bidder: "playwire", + params: { + uid: 11 + } + } + ] + } + ]; +``` From 56bc1a0d0291c51f12b25598baf4ad7ff87fa520 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 1 Nov 2021 19:24:30 +0300 Subject: [PATCH 45/57] TheMediaGrid: added alias as adlivetech --- modules/adlivetechBidAdapter.md | 61 +++++++++++++++++++++++++++++++++ modules/gridBidAdapter.js | 2 +- 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 modules/adlivetechBidAdapter.md diff --git a/modules/adlivetechBidAdapter.md b/modules/adlivetechBidAdapter.md new file mode 100644 index 00000000000..612e669ea1a --- /dev/null +++ b/modules/adlivetechBidAdapter.md @@ -0,0 +1,61 @@ +# Overview + +Module Name: Adlivetech Bidder Adapter +Module Type: Bidder Adapter +Maintainer: grid-tech@themediagrid.com + +# Description + +Module that connects to Grid demand source to fetch bids. +The adapter is GDPR compliant and supports banner and video (instream and outstream). + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[300, 250]], + bids: [ + { + bidder: "adlivetech", + params: { + uid: '1', + bidFloor: 0.5 + } + } + ] + },{ + code: 'test-div', + sizes: [[728, 90]], + bids: [ + { + bidder: "adlivetech", + params: { + uid: 2, + keywords: { + brandsafety: ['disaster'], + topic: ['stress', 'fear'] + } + } + } + ] + }, + { + code: 'test-div', + sizes: [[728, 90]], + mediaTypes: { video: { + context: 'instream', + playerSize: [728, 90], + mimes: ['video/mp4'] + }, + bids: [ + { + bidder: "adlivetech", + params: { + uid: 11 + } + } + ] + } + ]; +``` diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index f62b62b7a97..6d3f8ad2792 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -29,7 +29,7 @@ let hasSynced = false; export const spec = { code: BIDDER_CODE, - aliases: ['playwire'], + aliases: ['playwire', 'adlivetech'], supportedMediaTypes: [ BANNER, VIDEO ], /** * Determines whether or not the given bid request is valid. From 435be758e0900c5b21b8ca65c12d9a046eff303c Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 6 Dec 2021 20:33:00 +0300 Subject: [PATCH 46/57] TheMediaGrid: fix sync url workflow --- modules/gridBidAdapter.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 6d3f8ad2792..df1f4f3b6d7 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -291,10 +291,11 @@ export const spec = { if (!hasSynced && syncOptions.pixelEnabled) { let params = ''; - if (gdprConsent && typeof gdprConsent.consentString === 'string') { + if (gdprConsent) { if (typeof gdprConsent.gdprApplies === 'boolean') { - params += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { + params += `&gdpr=${Number(gdprConsent.gdprApplies)}`; + } + if (typeof gdprConsent.consentString === 'string') { params += `&gdpr_consent=${gdprConsent.consentString}`; } } From 1c9b3bf494f4b9381aebdc228457cf67d7eed75c Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 6 Dec 2021 20:33:46 +0300 Subject: [PATCH 47/57] TrustX: fix sync url worflow + remove old syncurl --- modules/trustxBidAdapter.js | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index b2f0936e3c7..31be2814bba 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -6,9 +6,8 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'trustx'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson?sp=trustx'; -const ADDITIONAL_SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; -const ADAPTER_SYNC_URL = 'https://sofia.trustx.org/push_sync'; +const ADAPTER_SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; const LOG_ERROR_MESS = { @@ -281,12 +280,12 @@ export const spec = { }, getUserSyncs: function(syncOptions, responses, gdprConsent, uspConsent) { if (syncOptions.pixelEnabled) { - const syncsPerBidder = config.getConfig('userSync.syncsPerBidder'); let params = []; - if (gdprConsent && typeof gdprConsent.consentString === 'string') { + if (gdprConsent) { if (typeof gdprConsent.gdprApplies === 'boolean') { - params.push(`gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`); - } else { + params.push(`gdpr=${Number(gdprConsent.gdprApplies)}`); + } + if (typeof gdprConsent.consentString === 'string') { params.push(`gdpr_consent=${gdprConsent.consentString}`); } } @@ -294,17 +293,10 @@ export const spec = { params.push(`us_privacy=${uspConsent}`); } const stringParams = params.join('&'); - const syncs = [{ + return { type: 'image', - url: ADAPTER_SYNC_URL + (stringParams ? `?${stringParams}` : '') - }]; - if (syncsPerBidder > 1) { - syncs.push({ - type: 'image', - url: ADDITIONAL_SYNC_URL + (stringParams ? `&${stringParams}` : '') - }); - } - return syncs; + url: ADAPTER_SYNC_URL + stringParams + }; } } } From 51f7cd5e27ebb9ddbdd71fae3047c9317262a190 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 8 Feb 2022 10:32:52 +0300 Subject: [PATCH 48/57] TheMediaGrid: added instl support --- modules/gridBidAdapter.js | 17 ++++++++++------ test/spec/modules/gridBidAdapter_spec.js | 26 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index ce191a398b9..3e75b666d78 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -96,12 +96,17 @@ export const spec = { divid: adUnitCode.toString() } }; - if (ortb2Imp && ortb2Imp.ext && ortb2Imp.ext.data) { - impObj.ext.data = ortb2Imp.ext.data; - if (impObj.ext.data.adserver && impObj.ext.data.adserver.adslot) { - impObj.ext.gpid = impObj.ext.data.adserver.adslot.toString(); - } else { - impObj.ext.gpid = ortb2Imp.ext.data.pbadslot && ortb2Imp.ext.data.pbadslot.toString(); + if (ortb2Imp) { + if (ortb2Imp.instl) { + impObj.instl = ortb2Imp.instl; + } + if (ortb2Imp.ext && ortb2Imp.ext.data) { + impObj.ext.data = ortb2Imp.ext.data; + if (impObj.ext.data.adserver && impObj.ext.data.adserver.adslot) { + impObj.ext.gpid = impObj.ext.data.adserver.adslot.toString(); + } else { + impObj.ext.gpid = ortb2Imp.ext.data.pbadslot && ortb2Imp.ext.data.pbadslot.toString(); + } } } if (!isEmpty(keywords)) { diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 42ac6e1c560..9a1c0c4721a 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -599,6 +599,32 @@ describe('TheMediaGrid Adapter', function () { }); }); + it('should contain imp[].instl if available', function() { + const ortb2Imp = [{ + instl: 1 + }, { + instl: 2, + ext: { + data: { + adserver: { + name: 'ad_server_name', + adslot: '/222222/slot' + }, + pbadslot: '/222222/slot' + } + } + }]; + const bidRequestsWithOrtb2Imp = bidRequests.slice(0, 3).map((bid, ind) => { + return Object.assign(ortb2Imp[ind] ? { ortb2Imp: ortb2Imp[ind] } : {}, bid); + }); + const request = spec.buildRequests(bidRequestsWithOrtb2Imp, bidderRequest); + expect(request.data).to.be.an('string'); + const payload = parseRequest(request.data); + expect(payload.imp[0].instl).to.equal(1); + expect(payload.imp[1].instl).to.equal(2); + expect(payload.imp[2].instl).to.be.undefined(); + }); + it('all id must be a string', function() { const fpdUserIdNumVal = 2345543345; const getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage').callsFake( From 627093c244ed2b29b603cccd1255e68a869026bf Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 15 Feb 2022 14:00:46 +0300 Subject: [PATCH 49/57] TheMediaGrid: fix test for instl --- test/spec/modules/gridBidAdapter_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 9a1c0c4721a..a22097bdde3 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -622,7 +622,7 @@ describe('TheMediaGrid Adapter', function () { const payload = parseRequest(request.data); expect(payload.imp[0].instl).to.equal(1); expect(payload.imp[1].instl).to.equal(2); - expect(payload.imp[2].instl).to.be.undefined(); + expect(payload.imp[2].instl).to.be.undefined; }); it('all id must be a string', function() { From 2f5f5641c31bab19121aed07027734cb1977e2a7 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Tue, 22 Feb 2022 14:52:31 +0300 Subject: [PATCH 50/57] TheMediaGrid: update md file --- modules/gridBidAdapter.md | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/modules/gridBidAdapter.md b/modules/gridBidAdapter.md index 8eb8dfc19fb..d5ec747aae2 100644 --- a/modules/gridBidAdapter.md +++ b/modules/gridBidAdapter.md @@ -13,11 +13,11 @@ Grid bid adapter supports Banner and Video (instream and outstream). You can allow writing in localStorage `pbjs.setBidderConfig` for the bidder `grid` ``` pbjs.setBidderConfig({ - bidders: ["grid"], - config: { - localStorageWriteAllowed: true - } - }) + bidders: ["grid"], + config: { + localStorageWriteAllowed: true + } +}) ``` # Test Parameters @@ -25,7 +25,11 @@ pbjs.setBidderConfig({ var adUnits = [ { code: 'test-div', - sizes: [[300, 250]], + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]], + } + }, bids: [ { bidder: "grid", @@ -37,15 +41,19 @@ pbjs.setBidderConfig({ ] },{ code: 'test-div', - sizes: [[728, 90]], bids: [ { bidder: "grid", params: { uid: 2, keywords: { - brandsafety: ['disaster'], - topic: ['stress', 'fear'] + site: { + publisher: [{ + name: 'someKeywordsName', + brandsafety: ['disaster'], + topic: ['stress', 'fear'] + }] + } } } } @@ -54,7 +62,12 @@ pbjs.setBidderConfig({ { code: 'test-div', sizes: [[728, 90]], - mediaTypes: { video: {} }, + mediaTypes: { + video: { + playerSize: [1280, 720], + context: 'instream' + } + }, bids: [ { bidder: "grid", From 65c4908c172baea726fd1eb9e18a5b75c58ac2ea Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Fri, 4 Mar 2022 19:20:47 +0300 Subject: [PATCH 51/57] TheMediaGrid: reformat segments for permutive rtd module --- modules/gridBidAdapter.js | 37 +++++++++++++++++++++--- test/spec/modules/gridBidAdapter_spec.js | 35 ++++++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 668c1db9d1d..ab4149d2844 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -25,6 +25,8 @@ const LOG_ERROR_MESS = { hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' }; +const modulesToFix = ['permutive.com']; + let hasSynced = false; export const spec = { @@ -171,9 +173,7 @@ export const spec = { user = { data: [{ name: 'iow_labs_pub_data', - segment: jwpseg.map((seg) => { - return {name: 'jwpseg', value: seg}; - }) + segment: segmentProcessing(jwpseg, 'jwpseg'), }] }; } @@ -183,7 +183,9 @@ export const spec = { if (!user) { user = { data: [] }; } - user = mergeDeep(user, { data: ortb2UserData }); + user = mergeDeep(user, { + data: ortb2UserData.map((ortb2Data) => ortb2Data && modulesToFix.includes(ortb2Data.name) ? fixRTDDataFormat(ortb2Data) : ortb2Data) + }); } if (gdprConsent && gdprConsent.consentString) { @@ -489,6 +491,33 @@ function getUserIdFromFPDStorage() { return storage.getDataFromLocalStorage(USER_ID_KEY) || makeNewUserIdInFPDStorage(); } +function segmentProcessing(segment, forceSegName) { + return segment + .map((seg) => { + const value = seg && (seg.value || seg.id || seg); + if (typeof value === 'string' || typeof value === 'number') { + return { + value: value.toString(), + ...(forceSegName && { name: forceSegName }), + ...(seg.name && { name: seg.name }), + }; + } + return null; + }) + .filter((seg) => !!seg); +} + +function fixRTDDataFormat(data) { + const { name, segment } = data || {}; + if (name && segment && segment.length) { + return { + name, + segment: segmentProcessing(segment), + }; + } + return data; +} + function reformatKeywords(pageKeywords) { const formatedPageKeywords = {}; Object.keys(pageKeywords).forEach((name) => { diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index ff792042aea..1184b1474e8 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -546,6 +546,41 @@ describe('TheMediaGrid Adapter', function () { getConfigStub.restore(); }); + it('shold have user.data filled from config ortb2.user.data', function () { + const userData = [ + { + name: 'someName', + segment: [1, 2, { anyKey: 'anyVal' }, 'segVal', { id: 'segId' }, { value: 'segValue' }, { id: 'segId2', name: 'segName' }] + }, + { + name: 'permutive.com', + segment: [1, 2, 'segVal', { id: 'segId' }, { anyKey: 'anyVal' }, { value: 'segValue' }, { id: 'segId2', name: 'segName' }] + }, + { + someKey: 'another data' + } + ]; + + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'ortb2.user.data' ? userData : null); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + const payload = parseRequest(request.data); + expect(payload.user.data).to.deep.equal([ + { + name: 'someName', + segment: [1, 2, { anyKey: 'anyVal' }, 'segVal', { id: 'segId' }, { value: 'segValue' }, { id: 'segId2', name: 'segName' }] + }, + { + name: 'permutive.com', + segment: [{ value: '1' }, { value: '2' }, { value: 'segVal' }, { value: 'segId' }, { value: 'segValue' }, { value: 'segId2', name: 'segName' }] + }, + { + someKey: 'another data' + } + ]); + getConfigStub.restore(); + }); + it('shold be right tmax when timeout in config is less then timeout in bidderRequest', function() { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'bidderTimeout' ? 2000 : null); From 75dc0f6b1beeb741fff1cb9dbebee26474137284 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Fri, 4 Mar 2022 19:21:54 +0300 Subject: [PATCH 52/57] TrustX: send all ortb2.user.data in user.data --- modules/trustxBidAdapter.js | 75 ++++++++++++---------- test/spec/modules/trustxBidAdapter_spec.js | 55 +++++++++------- 2 files changed, 74 insertions(+), 56 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 70cae0f50f1..7c812e3dd60 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -1,4 +1,4 @@ -import { isEmpty, deepAccess, logError, logWarn, parseGPTSingleSizeArrayToRtbSize } from '../src/utils.js'; +import {isEmpty, deepAccess, logError, logWarn, parseGPTSingleSizeArrayToRtbSize, mergeDeep} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; @@ -21,6 +21,9 @@ const LOG_ERROR_MESS = { hasEmptySeatbidArray: 'Response has empty seatbid array', hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' }; + +const modulesToFix = ['permutive.com']; + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [ BANNER, VIDEO ], @@ -173,16 +176,25 @@ export const spec = { request.site.content = content; } - const userData = []; - addSegments('iow_labs_pub_data', 'jwpseg', jwpseg, userData); - addSegments('permutive', 'p_standard', permutiveseg, userData, 'permutive.com'); - - if (userData.length) { + if (jwpseg && jwpseg.length) { user = { - data: userData + data: [{ + name: 'iow_labs_pub_data', + segment: segmentProcessing(jwpseg, 'jwpseg'), + }] }; } + const ortb2UserData = config.getConfig('ortb2.user.data'); + if (ortb2UserData && ortb2UserData.length) { + if (!user) { + user = { data: [] }; + } + user = mergeDeep(user, { + data: ortb2UserData.map((ortb2Data) => ortb2Data && modulesToFix.includes(ortb2Data.name) ? fixRTDDataFormat(ortb2Data) : ortb2Data) + }); + } + if (gdprConsent && gdprConsent.consentString) { userExt = {consent: gdprConsent.consentString}; } @@ -431,34 +443,31 @@ function createBannerRequest(bid, mediaType) { return result; } -function addSegments(name, segName, segments, data, bidConfigName) { - if (segments && segments.length) { - data.push({ - name: name, - segment: segments - .map((seg) => seg && (seg.id || seg)) - .filter((seg) => seg && (typeof seg === 'string' || typeof seg === 'number')) - .map((seg) => ({ name: segName, value: seg.toString() })) - }); - } else if (bidConfigName) { - const configData = config.getConfig('ortb2.user.data'); - let segData = null; - configData && configData.some(({name, segment}) => { - if (name === bidConfigName) { - segData = segment; - return true; +function segmentProcessing(segment, forceSegName) { + return segment + .map((seg) => { + const value = seg && (seg.value || seg.id || seg); + if (typeof value === 'string' || typeof value === 'number') { + return { + value: value.toString(), + ...(forceSegName && { name: forceSegName }), + ...(seg.name && { name: seg.name }), + }; } - }); - if (segData && segData.length) { - data.push({ - name: name, - segment: segData - .map((seg) => seg && (seg.id || seg)) - .filter((seg) => seg && (typeof seg === 'string' || typeof seg === 'number')) - .map((seg) => ({ name: segName, value: seg.toString() })) - }); - } + return null; + }) + .filter((seg) => !!seg); +} + +function fixRTDDataFormat(data) { + const { name, segment } = data || {}; + if (name && segment && segment.length) { + return { + name, + segment: segmentProcessing(segment), + }; } + return data; } function reformatKeywords(pageKeywords) { diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 2a0832f11e8..023298f39db 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -401,30 +401,39 @@ describe('TrustXAdapter', function () { expect(payload.site.content).to.deep.equal(jsContent); }); - it('if segment is present in permutive targeting, payload must have right params', function () { - const permSegments = [{id: 'test_perm_1'}, {id: 'test_perm_2'}]; - const bidRequestsWithPermutiveTargeting = bidRequests.map((bid) => { - return Object.assign({ - rtd: { - p_standard: { - targeting: { - segments: permSegments - } - } - } - }, bid); - }); - const request = spec.buildRequests(bidRequestsWithPermutiveTargeting, bidderRequest); - expect(request.data).to.be.an('string'); + it('shold have user.data filled from config ortb2.user.data', function () { + const userData = [ + { + name: 'someName', + segment: [1, 2, { anyKey: 'anyVal' }, 'segVal', { id: 'segId' }, { value: 'segValue' }, { id: 'segId2', name: 'segName' }] + }, + { + name: 'permutive.com', + segment: [1, 2, 'segVal', { id: 'segId' }, { anyKey: 'anyVal' }, { value: 'segValue' }, { id: 'segId2', name: 'segName' }] + }, + { + someKey: 'another data' + } + ]; + + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'ortb2.user.data' ? userData : null); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); const payload = parseRequest(request.data); - expect(payload).to.have.property('user'); - expect(payload.user.data).to.deep.equal([{ - name: 'permutive', - segment: [ - {name: 'p_standard', value: permSegments[0].id}, - {name: 'p_standard', value: permSegments[1].id} - ] - }]); + expect(payload.user.data).to.deep.equal([ + { + name: 'someName', + segment: [1, 2, { anyKey: 'anyVal' }, 'segVal', { id: 'segId' }, { value: 'segValue' }, { id: 'segId2', name: 'segName' }] + }, + { + name: 'permutive.com', + segment: [{ value: '1' }, { value: '2' }, { value: 'segVal' }, { value: 'segId' }, { value: 'segValue' }, { value: 'segId2', name: 'segName' }] + }, + { + someKey: 'another data' + } + ]); + getConfigStub.restore(); }); it('should contain the keyword values if it present in ortb2.(site/user)', function () { From ebe6822bae65240d5261bbecdfe324003020cd82 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Wed, 16 Mar 2022 19:01:06 +0300 Subject: [PATCH 53/57] TheMediaGrid: remove permutive segments reformating --- modules/gridBidAdapter.js | 15 +--------- test/spec/modules/gridBidAdapter_spec.js | 37 +++++++++++++++++++++--- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index ab4149d2844..e1090732071 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -25,8 +25,6 @@ const LOG_ERROR_MESS = { hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' }; -const modulesToFix = ['permutive.com']; - let hasSynced = false; export const spec = { @@ -184,7 +182,7 @@ export const spec = { user = { data: [] }; } user = mergeDeep(user, { - data: ortb2UserData.map((ortb2Data) => ortb2Data && modulesToFix.includes(ortb2Data.name) ? fixRTDDataFormat(ortb2Data) : ortb2Data) + data: [...ortb2UserData] }); } @@ -507,17 +505,6 @@ function segmentProcessing(segment, forceSegName) { .filter((seg) => !!seg); } -function fixRTDDataFormat(data) { - const { name, segment } = data || {}; - if (name && segment && segment.length) { - return { - name, - segment: segmentProcessing(segment), - }; - } - return data; -} - function reformatKeywords(pageKeywords) { const formatedPageKeywords = {}; Object.keys(pageKeywords).forEach((name) => { diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 1184b1474e8..4f7f9f1279a 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -546,7 +546,7 @@ describe('TheMediaGrid Adapter', function () { getConfigStub.restore(); }); - it('shold have user.data filled from config ortb2.user.data', function () { + it('should have user.data filled from config ortb2.user.data', function () { const userData = [ { name: 'someName', @@ -565,19 +565,48 @@ describe('TheMediaGrid Adapter', function () { arg => arg === 'ortb2.user.data' ? userData : null); const request = spec.buildRequests([bidRequests[0]], bidderRequest); const payload = parseRequest(request.data); - expect(payload.user.data).to.deep.equal([ + expect(payload.user.data).to.deep.equal(userData); + getConfigStub.restore(); + }); + + it('should have right value in user.data when jwpsegments are present', function () { + const userData = [ { name: 'someName', segment: [1, 2, { anyKey: 'anyVal' }, 'segVal', { id: 'segId' }, { value: 'segValue' }, { id: 'segId2', name: 'segName' }] }, { name: 'permutive.com', - segment: [{ value: '1' }, { value: '2' }, { value: 'segVal' }, { value: 'segId' }, { value: 'segValue' }, { value: 'segId2', name: 'segName' }] + segment: [1, 2, 'segVal', { id: 'segId' }, { anyKey: 'anyVal' }, { value: 'segValue' }, { id: 'segId2', name: 'segName' }] }, { someKey: 'another data' } - ]); + ]; + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'ortb2.user.data' ? userData : null); + + const jsContent = {id: 'test_jw_content_id'}; + const jsSegments = ['test_seg_1', 'test_seg_2']; + const bidRequestsWithJwTargeting = Object.assign({}, bidRequests[0], { + rtd: { + jwplayer: { + targeting: { + segments: jsSegments, + content: jsContent + } + } + } + }); + const request = spec.buildRequests([bidRequestsWithJwTargeting], bidderRequest); + const payload = parseRequest(request.data); + expect(payload.user.data).to.deep.equal([{ + name: 'iow_labs_pub_data', + segment: [ + {name: 'jwpseg', value: jsSegments[0]}, + {name: 'jwpseg', value: jsSegments[1]} + ] + }, ...userData]); getConfigStub.restore(); }); From 70814e539206f77f93fc0fc8becca0989df617ed Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Wed, 16 Mar 2022 19:01:44 +0300 Subject: [PATCH 54/57] TrustX: remove permutive segments reformating --- modules/trustxBidAdapter.js | 15 +--------- test/spec/modules/trustxBidAdapter_spec.js | 35 ++++++++++++++++++++-- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 7c812e3dd60..7d40a0b0452 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -22,8 +22,6 @@ const LOG_ERROR_MESS = { hasNoArrayOfBids: 'Seatbid from response has no array of bid objects - ' }; -const modulesToFix = ['permutive.com']; - export const spec = { code: BIDDER_CODE, supportedMediaTypes: [ BANNER, VIDEO ], @@ -191,7 +189,7 @@ export const spec = { user = { data: [] }; } user = mergeDeep(user, { - data: ortb2UserData.map((ortb2Data) => ortb2Data && modulesToFix.includes(ortb2Data.name) ? fixRTDDataFormat(ortb2Data) : ortb2Data) + data: [...ortb2UserData] }); } @@ -459,17 +457,6 @@ function segmentProcessing(segment, forceSegName) { .filter((seg) => !!seg); } -function fixRTDDataFormat(data) { - const { name, segment } = data || {}; - if (name && segment && segment.length) { - return { - name, - segment: segmentProcessing(segment), - }; - } - return data; -} - function reformatKeywords(pageKeywords) { const formatedPageKeywords = {}; Object.keys(pageKeywords).forEach((name) => { diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index 023298f39db..db02d87d52b 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -420,19 +420,48 @@ describe('TrustXAdapter', function () { arg => arg === 'ortb2.user.data' ? userData : null); const request = spec.buildRequests([bidRequests[0]], bidderRequest); const payload = parseRequest(request.data); - expect(payload.user.data).to.deep.equal([ + expect(payload.user.data).to.deep.equal(userData); + getConfigStub.restore(); + }); + + it('should have right value in user.data when jwpsegments are present', function () { + const userData = [ { name: 'someName', segment: [1, 2, { anyKey: 'anyVal' }, 'segVal', { id: 'segId' }, { value: 'segValue' }, { id: 'segId2', name: 'segName' }] }, { name: 'permutive.com', - segment: [{ value: '1' }, { value: '2' }, { value: 'segVal' }, { value: 'segId' }, { value: 'segValue' }, { value: 'segId2', name: 'segName' }] + segment: [1, 2, 'segVal', { id: 'segId' }, { anyKey: 'anyVal' }, { value: 'segValue' }, { id: 'segId2', name: 'segName' }] }, { someKey: 'another data' } - ]); + ]; + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'ortb2.user.data' ? userData : null); + + const jsContent = {id: 'test_jw_content_id'}; + const jsSegments = ['test_seg_1', 'test_seg_2']; + const bidRequestsWithJwTargeting = Object.assign({}, bidRequests[0], { + rtd: { + jwplayer: { + targeting: { + segments: jsSegments, + content: jsContent + } + } + } + }); + const request = spec.buildRequests([bidRequestsWithJwTargeting], bidderRequest); + const payload = parseRequest(request.data); + expect(payload.user.data).to.deep.equal([{ + name: 'iow_labs_pub_data', + segment: [ + {name: 'jwpseg', value: jsSegments[0]}, + {name: 'jwpseg', value: jsSegments[1]} + ] + }, ...userData]); getConfigStub.restore(); }); From acef4800c3b7e3dce4f94663e2c03db7b185b4da Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Wed, 16 Mar 2022 19:07:14 +0300 Subject: [PATCH 55/57] TheMediaGrid & TrustX: fix typo --- test/spec/modules/gridBidAdapter_spec.js | 4 ++-- test/spec/modules/trustxBidAdapter_spec.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 4f7f9f1279a..9e393a5823d 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -610,7 +610,7 @@ describe('TheMediaGrid Adapter', function () { getConfigStub.restore(); }); - it('shold be right tmax when timeout in config is less then timeout in bidderRequest', function() { + it('should be right tmax when timeout in config is less then timeout in bidderRequest', function() { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'bidderTimeout' ? 2000 : null); const request = spec.buildRequests([bidRequests[0]], bidderRequest); @@ -619,7 +619,7 @@ describe('TheMediaGrid Adapter', function () { expect(payload.tmax).to.equal(2000); getConfigStub.restore(); }); - it('shold be right tmax when timeout in bidderRequest is less then timeout in config', function() { + it('should be right tmax when timeout in bidderRequest is less then timeout in config', function() { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'bidderTimeout' ? 5000 : null); const request = spec.buildRequests([bidRequests[0]], bidderRequest); diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index db02d87d52b..b34813948fc 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -401,7 +401,7 @@ describe('TrustXAdapter', function () { expect(payload.site.content).to.deep.equal(jsContent); }); - it('shold have user.data filled from config ortb2.user.data', function () { + it('should have user.data filled from config ortb2.user.data', function () { const userData = [ { name: 'someName', @@ -539,7 +539,7 @@ describe('TrustXAdapter', function () { getConfigStub.restore(); }); - it('shold be right tmax when timeout in config is less then timeout in bidderRequest', function() { + it('should be right tmax when timeout in config is less then timeout in bidderRequest', function() { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'bidderTimeout' ? 2000 : null); const request = spec.buildRequests([bidRequests[0]], bidderRequest); @@ -548,7 +548,7 @@ describe('TrustXAdapter', function () { expect(payload.tmax).to.equal(2000); getConfigStub.restore(); }); - it('shold be right tmax when timeout in bidderRequest is less then timeout in config', function() { + it('should be right tmax when timeout in bidderRequest is less then timeout in config', function() { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'bidderTimeout' ? 5000 : null); const request = spec.buildRequests([bidRequests[0]], bidderRequest); From 2d3d40bf43269597d9f3fbc70408767041f82c90 Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Wed, 20 Apr 2022 13:37:59 +0300 Subject: [PATCH 56/57] TheMediaGrid: support bid.ortb2.site.content --- modules/gridBidAdapter.js | 16 ++++++++++++++- test/spec/modules/gridBidAdapter_spec.js | 26 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index e1090732071..f035325f783 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -53,6 +53,7 @@ export const spec = { } let pageKeywords = null; let jwpseg = null; + let bidOrtb2 = null; let content = null; let schain = null; let userIdAsEids = null; @@ -77,7 +78,7 @@ export const spec = { if (!userIdAsEids) { userIdAsEids = bid.userIdAsEids; } - const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd, ortb2Imp} = bid; + const {params: {uid, keywords}, mediaTypes, bidId, adUnitCode, rtd, ortb2Imp, ortb2} = bid; bidsMap[bidId] = bid; const bidFloor = _getFloor(mediaTypes || {}, bid); const jwTargeting = rtd && rtd.jwplayer && rtd.jwplayer.targeting; @@ -89,6 +90,14 @@ export const spec = { content = jwTargeting.content; } } + if (ortb2) { + if (!bidOrtb2) { + bidOrtb2 = ortb2; + } else { + bidOrtb2 = mergeDeep(bidOrtb2, ortb2); + } + } + let impObj = { id: bidId.toString(), tagid: uid.toString(), @@ -167,6 +176,11 @@ export const spec = { request.site.content = content; } + const ortb2SiteContent = deepAccess(bidOrtb2, 'site.content'); + if (ortb2SiteContent) { + request.site.content = mergeDeep(request.site.content || {}, ortb2SiteContent); + } + if (jwpseg && jwpseg.length) { user = { data: [{ diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 9e393a5823d..760ab32310f 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -610,6 +610,32 @@ describe('TheMediaGrid Adapter', function () { getConfigStub.restore(); }); + it('should have right value in site.content when jwpsegments are present in bid.ortb2', function () { + const data = [ + { + name: 'jwplayer', + ext: { + segtax: 502 + }, + segment: [{ id: 'test_seg_1', value: 'test_seg_1' }, { id: 'test_seg_2', value: 'test_seg_2' }] + } + ]; + const siteContent = { + id: 'jw_content_id', + data: data + }; + const bidRequestsWithJwData = Object.assign({}, bidRequests[0], { + ortb2: { + site: { + content: siteContent + } + } + }); + const request = spec.buildRequests([bidRequestsWithJwData], bidderRequest); + const payload = parseRequest(request.data); + expect(payload.site.content).to.deep.equal(siteContent); + }); + it('should be right tmax when timeout in config is less then timeout in bidderRequest', function() { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'bidderTimeout' ? 2000 : null); From 7df343dacd25998f54b125b6ab978847185da13e Mon Sep 17 00:00:00 2001 From: TheMediaGrid Date: Mon, 30 May 2022 18:31:41 +0300 Subject: [PATCH 57/57] TheMediaGrid Bid Adapter: support bid.ortb2.site.content.id --- modules/gridBidAdapter.js | 4 ++++ test/spec/modules/gridBidAdapter_spec.js | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index ff74f26a2ba..993ddd927b0 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -289,6 +289,10 @@ export const spec = { const siteContent = request.site.content || {}; request.site.content = mergeDeep(siteContent, { data }); } + const id = deepAccess(site, 'content.id'); + if (id) { + request.site.content = {...request.site.content, id}; + } } return { diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 38e50be9212..61776f58682 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -632,6 +632,17 @@ describe('TheMediaGrid Adapter', function () { getConfigStub.restore(); }); + it('should have site.content.id filled from config ortb2.site.content.id', function () { + const contentId = 'jw_abc'; + + const getConfigStub = sinon.stub(config, 'getConfig').callsFake( + arg => arg === 'ortb2.site' ? { content: { id: contentId } } : null); + const request = spec.buildRequests([bidRequests[0]], bidderRequest); + const payload = parseRequest(request.data); + expect(payload.site.content.id).to.equal(contentId); + getConfigStub.restore(); + }); + it('should be right tmax when timeout in config is less then timeout in bidderRequest', function() { const getConfigStub = sinon.stub(config, 'getConfig').callsFake( arg => arg === 'bidderTimeout' ? 2000 : null);