From 2a2ba3441b7f635b6f5b32a506b2e5509c032070 Mon Sep 17 00:00:00 2001 From: adxcgcom <31470944+adxcgcom@users.noreply.github.com> Date: Wed, 21 Apr 2021 11:05:31 +0000 Subject: [PATCH] ADXCG Bid Adapter: add video & parameter validation & other updates (#6577) * Update adxcgBidAdapter.js * Update adxcgcom .js client side adapter * lgtm review update * check for parameter existenct onBidWon * Update adxcgBidAdapter.js * Update adxcgBidAdapter.js * Update adxcgBidAdapter.md * Update adxcgBidAdapter.md --- modules/adxcgBidAdapter.js | 415 ++++++++++++++-------- modules/adxcgBidAdapter.md | 138 +++---- test/spec/modules/adxcgBidAdapter_spec.js | 349 +++++++++++------- 3 files changed, 564 insertions(+), 338 deletions(-) diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js index e61792288edd..e10eeaa3302b 100644 --- a/modules/adxcgBidAdapter.js +++ b/modules/adxcgBidAdapter.js @@ -1,7 +1,7 @@ -import { config } from '../src/config.js' +import {config} from '../src/config.js' import * as utils from '../src/utils.js' -import { registerBidder } from '../src/adapters/bidderFactory.js' -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js' +import {registerBidder} from '../src/adapters/bidderFactory.js' +import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js' import includes from 'core-js-pure/features/array/includes.js' /** @@ -11,16 +11,18 @@ import includes from 'core-js-pure/features/array/includes.js' * updated to pass aditional auction and impression level parameters. added pass for video targeting parameters * updated to fix native support for image width/height and icon 2019.03.17 * updated support for userid - pubcid,ttid 2019.05.28 - * updated to support prebid 3.0 - remove non https, move to banner.xx.sizes, remove utils.getTopWindowLocation,remove utils.getTopWindowUrl(),remove utils.getTopWindowReferrer() + * updated to support prebid 3.0 - remove non https, move to banner.xx.sizes, remove utils.getTopWindowLocation,remove utils.getTopWindowUrl(),remove utils.getTopWindowReferrer() + * updated to support prebid 4.0 - standardized video params, updated video validation, add onBidWon, onTimeOut, use standardized getFloor */ const BIDDER_CODE = 'adxcg' const SUPPORTED_AD_TYPES = [BANNER, VIDEO, NATIVE] const SOURCE = 'pbjs10' -const VIDEO_TARGETING = ['id', 'mimes', 'minduration', 'maxduration', 'startdelay', 'skippable', 'playback_method', 'frameworks'] +const VIDEO_TARGETING = ['id', 'minduration', 'maxduration', 'startdelay', 'skippable', 'playback_method', 'frameworks'] const USER_PARAMS_AUCTION = ['forcedDspIds', 'forcedCampaignIds', 'forcedCreativeIds', 'gender', 'dnt', 'language'] const USER_PARAMS_BID = ['lineparam1', 'lineparam2', 'lineparam3'] -const BIDADAPTERVERSION = 'r20191128PB30' +const BIDADAPTERVERSION = 'r20210330PB40' +const DEFAULT_MIN_FLOOR = 0; export const spec = { code: BIDDER_CODE, @@ -34,28 +36,45 @@ export const spec = { */ isBidRequestValid: function (bid) { if (!bid || !bid.params) { - utils.logWarn(BIDDER_CODE + ': Missing bid parameters') + utils.logWarn(BIDDER_CODE + ': Missing bid parameters'); return false } if (!utils.isStr(bid.params.adzoneid)) { - utils.logWarn(BIDDER_CODE + ': adzoneid must be specified as a string') + utils.logWarn(BIDDER_CODE + ': adzoneid must be specified as a string'); return false } + if (isBannerRequest(bid)) { + const banneroAdUnit = utils.deepAccess(bid, 'mediaTypes.banner'); + if (!banneroAdUnit.sizes) { + utils.logWarn(BIDDER_CODE + ': banner sizes must be specified'); + return false; + } + } + if (isVideoRequest(bid)) { - if (!bid.params.video.mimes) { - // Give a warning but let it pass - utils.logWarn(BIDDER_CODE + ': mimes should be specified for videos') - } else if (!utils.isArray(bid.params.video.mimes) || !bid.params.video.mimes.every(s => utils.isStr(s))) { - utils.logWarn(BIDDER_CODE + ': mimes must be an array of strings') - return false + // prebid 4.0 use standardized Video parameters + const videoAdUnit = utils.deepAccess(bid, 'mediaTypes.video'); + + if (!Array.isArray(videoAdUnit.playerSize)) { + utils.logWarn(BIDDER_CODE + ': video playerSize must be an array of integers'); + return false; } - const context = utils.deepAccess(bid, 'mediaTypes.video.context') - if (context !== 'instream') { - utils.logWarn(BIDDER_CODE + ': video context must be valid - instream') - return false + if (!videoAdUnit.context) { + utils.logWarn(BIDDER_CODE + ': video context must be specified'); + return false; + } + + if (!Array.isArray(videoAdUnit.mimes) || videoAdUnit.mimes.length === 0) { + utils.logWarn(BIDDER_CODE + ': video mimes must be an array of strings'); + return false; + } + + if (!Array.isArray(videoAdUnit.protocols) || videoAdUnit.protocols.length === 0) { + utils.logWarn(BIDDER_CODE + ': video protocols must be an array of integers'); + return false; } } @@ -69,17 +88,18 @@ export const spec = { * Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - utils.logMessage(`buildRequests: ${JSON.stringify(validBidRequests)}`) - - let dt = new Date() - let ratio = window.devicePixelRatio || 1 + let dt = new Date(); + let ratio = window.devicePixelRatio || 1; let iobavailable = window && window.IntersectionObserver && window.IntersectionObserverEntry && window.IntersectionObserverEntry.prototype && 'intersectionRatio' in window.IntersectionObserverEntry.prototype - let bt = config.getConfig('bidderTimeout') + let bt = config.getConfig('bidderTimeout'); if (window.PREBID_TIMEOUT) { - bt = Math.min(window.PREBID_TIMEOUT, bt) + bt = Math.min(window.PREBID_TIMEOUT, bt); } + let referrer = utils.deepAccess(bidderRequest, 'refererInfo.referer'); + let page = utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || config.getConfig('pageUrl') || utils.deepAccess(window, 'location.href'); + // add common parameters let beaconParams = { renderformat: 'javascript', @@ -96,20 +116,33 @@ export const spec = { dt: utils.timestamp(), iob: iobavailable ? '1' : '0', pbjs: '$prebid.version$', - rndid: Math.floor(Math.random() * (999999 - 100000 + 1)) + 100000 + rndid: Math.floor(Math.random() * (999999 - 100000 + 1)) + 100000, + ref: encodeURIComponent(referrer), + url: encodeURIComponent(page) + }; + + if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { + beaconParams.gdpr = bidderRequest.gdprConsent.gdprApplies ? '1' : '0'; + beaconParams.gdpr_consent = bidderRequest.gdprConsent.consentString; + } + + if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.pubcid'))) { + beaconParams.pubcid = validBidRequests[0].userId.pubcid; } - const referrer = utils.deepAccess(bidderRequest, 'refererInfo.referer'); - const page = utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || config.getConfig('pageUrl') || utils.deepAccess(window, 'location.href'); - beaconParams.ref = encodeURIComponent(referrer); - beaconParams.url = encodeURIComponent(page); + if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.tdid'))) { + beaconParams.tdid = validBidRequests[0].userId.tdid; + } - if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { - beaconParams.gdpr = bidderRequest.gdprConsent.gdprApplies ? '1' : '0' - beaconParams.gdpr_consent = bidderRequest.gdprConsent.consentString + if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.id5id.uid'))) { + beaconParams.id5id = validBidRequests[0].userId.id5id.uid; + } + + if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.idl_env'))) { + beaconParams.idl_env = validBidRequests[0].userId.idl_env; } - let biddercustom = config.getConfig(BIDDER_CODE) + let biddercustom = config.getConfig(BIDDER_CODE); if (biddercustom) { Object.keys(biddercustom) .filter(param => includes(USER_PARAMS_AUCTION, param)) @@ -117,80 +150,67 @@ export const spec = { } // per impression parameters - let adZoneIds = [] - let prebidBidIds = [] - let sizes = [] - let bidfloors = [] + let adZoneIds = []; + let prebidBidIds = []; + let sizes = []; + let bidfloors = []; validBidRequests.forEach((bid, index) => { - adZoneIds.push(utils.getBidIdParameter('adzoneid', bid.params)) - prebidBidIds.push(bid.bidId) + adZoneIds.push(utils.getBidIdParameter('adzoneid', bid.params)); + prebidBidIds.push(bid.bidId); + + let bidfloor = getFloor(bid); + bidfloors.push(bidfloor); + + // copy all custom parameters impression level parameters not supported above + let customBidParams = utils.getBidIdParameter('custom', bid.params) || {} + if (customBidParams) { + Object.keys(customBidParams) + .filter(param => includes(USER_PARAMS_BID, param)) + .forEach(param => beaconParams[param + '.' + index] = encodeURIComponent(customBidParams[param])) + } if (isBannerRequest(bid)) { - sizes.push(utils.parseSizesInput(bid.mediaTypes.banner.sizes).join('|')) + sizes.push(utils.parseSizesInput(bid.mediaTypes.banner.sizes).join('|')); } if (isNativeRequest(bid)) { - sizes.push('0x0') + sizes.push('0x0'); } - let bidfloor = utils.getBidIdParameter('bidfloor', bid.params) || 0 - bidfloors.push(bidfloor) - // copy video params if (isVideoRequest(bid)) { if (bid.params.video) { Object.keys(bid.params.video) .filter(param => includes(VIDEO_TARGETING, param)) .forEach(param => beaconParams['video.' + param + '.' + index] = encodeURIComponent(bid.params.video[param])) } - // copy video context params - beaconParams['video.context' + '.' + index] = utils.deepAccess(bid, 'mediaTypes.video.context') - sizes.push(utils.parseSizesInput(bid.mediaTypes.video.playerSize).join('|')) - } - - // copy all custom parameters impression level parameters not supported above - let customBidParams = utils.getBidIdParameter('custom', bid.params) || {} - if (customBidParams) { - Object.keys(customBidParams) - .filter(param => includes(USER_PARAMS_BID, param)) - .forEach(param => beaconParams[param + '.' + index] = encodeURIComponent(customBidParams[param])) + // copy video standarized params + beaconParams['video.context' + '.' + index] = utils.deepAccess(bid, 'mediaTypes.video.context'); + sizes.push(utils.parseSizesInput(bid.mediaTypes.video.playerSize).join('|')); + beaconParams['video.mimes' + '.' + index] = utils.deepAccess(bid, 'mediaTypes.video.mimes').join(','); + beaconParams['video.protocols' + '.' + index] = utils.deepAccess(bid, 'mediaTypes.video.protocols').join(','); } }) - beaconParams.adzoneid = adZoneIds.join(',') - beaconParams.format = sizes.join(',') - beaconParams.prebidBidIds = prebidBidIds.join(',') - beaconParams.bidfloors = bidfloors.join(',') - - if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.pubcid'))) { - beaconParams.pubcid = validBidRequests[0].userId.pubcid; - } - - if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.tdid'))) { - beaconParams.tdid = validBidRequests[0].userId.tdid; - } - - if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.id5id.uid'))) { - beaconParams.id5id = validBidRequests[0].userId.id5id.uid; - } - - if (utils.isStr(utils.deepAccess(validBidRequests, '0.userId.idl_env'))) { - beaconParams.idl_env = validBidRequests[0].userId.idl_env; - } + beaconParams.adzoneid = adZoneIds.join(','); + beaconParams.format = sizes.join(','); + beaconParams.prebidBidIds = prebidBidIds.join(','); + beaconParams.bidfloors = bidfloors.join(','); let adxcgRequestUrl = utils.buildUrl({ protocol: 'https', hostname: 'hbps.adxcg.net', pathname: '/get/adi', search: beaconParams - }) + }); + utils.logMessage(`calling adi adxcg`); return { contentType: 'text/plain', method: 'GET', url: adxcgRequestUrl, withCredentials: true - } + }; }, /** * Unpack the response from the server into a list of bids. @@ -199,105 +219,192 @@ export const spec = { * @return {bidRequests[]} An array of bids which were nested inside the server. */ interpretResponse: + function (serverResponse) { + utils.logMessage(`interpretResponse adxcg`); + let bidsAll = []; - function (serverResponse, bidRequests) { - let bids = [] + if (!serverResponse || !serverResponse.body || !utils.isArray(serverResponse.body.seatbid) || !serverResponse.body.seatbid.length) { + utils.logWarn(BIDDER_CODE + ': empty bid response'); + return bidsAll; + } - serverResponse = serverResponse.body - if (serverResponse) { - serverResponse.forEach(serverResponseOneItem => { + serverResponse.body.seatbid.forEach((bids) => { + bids.bid.forEach((serverResponseOneItem) => { let bid = {} + // parse general fields + bid.requestId = serverResponseOneItem.impid; + bid.cpm = serverResponseOneItem.price; + bid.creativeId = parseInt(serverResponseOneItem.crid); + bid.currency = serverResponseOneItem.currency ? serverResponseOneItem.currency : 'USD'; + bid.netRevenue = serverResponseOneItem.netRevenue ? serverResponseOneItem.netRevenue : true; + bid.ttl = serverResponseOneItem.ttl ? serverResponseOneItem.ttl : 300; + bid.width = serverResponseOneItem.w; + bid.height = serverResponseOneItem.h; + bid.burl = serverResponseOneItem.burl || ''; + + if (serverResponseOneItem.dealid != null && serverResponseOneItem.dealid.trim().length > 0) { + bid.dealId = serverResponseOneItem.dealid; + } - bid.requestId = serverResponseOneItem.bidId - bid.cpm = serverResponseOneItem.cpm - bid.creativeId = parseInt(serverResponseOneItem.creativeId) - bid.currency = serverResponseOneItem.currency ? serverResponseOneItem.currency : 'USD' - bid.netRevenue = serverResponseOneItem.netRevenue ? serverResponseOneItem.netRevenue : true - bid.ttl = serverResponseOneItem.ttl ? serverResponseOneItem.ttl : 300 - - if (serverResponseOneItem.deal_id != null && serverResponseOneItem.deal_id.trim().length > 0) { - bid.dealId = serverResponseOneItem.deal_id + if (serverResponseOneItem.ext.crType === 'banner') { + bid.ad = serverResponseOneItem.adm; + } else if (serverResponseOneItem.ext.crType === 'video') { + bid.vastUrl = serverResponseOneItem.nurl; + bid.vastXml = serverResponseOneItem.adm; + bid.mediaType = 'video'; + } else if (serverResponseOneItem.ext.crType === 'native') { + bid.mediaType = 'native'; + bid.native = parseNative(JSON.parse(serverResponseOneItem.adm)); + } else { + utils.logWarn(BIDDER_CODE + ': unknown or undefined crType'); } - if (serverResponseOneItem.ad) { - bid.ad = serverResponseOneItem.ad - } else if (serverResponseOneItem.vastUrl) { - bid.vastUrl = serverResponseOneItem.vastUrl - bid.mediaType = 'video' - } else if (serverResponseOneItem.nativeResponse) { - bid.mediaType = 'native' - - let nativeResponse = serverResponseOneItem.nativeResponse - - bid['native'] = { - clickUrl: nativeResponse.link.url, - impressionTrackers: nativeResponse.imptrackers, - clickTrackers: nativeResponse.clktrackers, - javascriptTrackers: nativeResponse.jstrackers + // prebid 4.0 meta taxonomy + if (utils.isArray(serverResponseOneItem.adomain)) { + utils.deepSetValue(bid, 'meta.advertiserDomains', serverResponseOneItem.adomain); + } + if (utils.isArray(serverResponseOneItem.cat)) { + utils.deepSetValue(bid, 'meta.secondaryCatIds', serverResponseOneItem.cat); + } + if (utils.isPlainObject(serverResponseOneItem.ext)) { + if (utils.isStr(serverResponseOneItem.ext.advertiser_id)) { + utils.deepSetValue(bid, 'meta.mediaType', serverResponseOneItem.ext.mediaType); + } + if (utils.isStr(serverResponseOneItem.ext.advertiser_id)) { + utils.deepSetValue(bid, 'meta.advertiserId', serverResponseOneItem.ext.advertiser_id); + } + if (utils.isStr(serverResponseOneItem.ext.advertiser_name)) { + utils.deepSetValue(bid, 'meta.advertiserName', serverResponseOneItem.ext.advertiser_name); + } + if (utils.isStr(serverResponseOneItem.ext.agency_name)) { + utils.deepSetValue(bid, 'meta.agencyName', serverResponseOneItem.ext.agency_name); } - - nativeResponse.assets.forEach(asset => { - if (asset.title && asset.title.text) { - bid['native'].title = asset.title.text - } - - if (asset.img && asset.img.url) { - let nativeImage = {} - nativeImage.url = asset.img.url - nativeImage.height = asset.img.h - nativeImage.width = asset.img.w - bid['native'].image = nativeImage - } - - if (asset.icon && asset.icon.url) { - let nativeIcon = {} - nativeIcon.url = asset.icon.url - nativeIcon.height = asset.icon.h - nativeIcon.width = asset.icon.w - bid['native'].icon = nativeIcon - } - - if (asset.data && asset.data.label === 'DESC' && asset.data.value) { - bid['native'].body = asset.data.value - } - - if (asset.data && asset.data.label === 'SPONSORED' && asset.data.value) { - bid['native'].sponsoredBy = asset.data.value - } - }) } - - bid.width = serverResponseOneItem.width - bid.height = serverResponseOneItem.height - utils.logMessage(`submitting bid[${serverResponseOneItem.bidId}]: ${JSON.stringify(bid)}`) - bids.push(bid) + bidsAll.push(bid) }) - } else { - utils.logMessage(`empty bid response`) - } - return bids + }) + return bidsAll }, - getUserSyncs: function (syncOptions) { + onBidWon: (bid) => { + if (bid.burl) { + utils.triggerPixel(utils.replaceAuctionPrice(bid.burl, bid.originalCpm)); + } + }, + + onTimeout(timeoutData) { + if (timeoutData == null) { + return; + } + + let beaconParams = { + A: timeoutData.bidder, + bid: timeoutData.bidId, + a: timeoutData.adUnitCode, + cn: timeoutData.timeout, + aud: timeoutData.auctionId, + }; + let adxcgRequestUrl = utils.buildUrl({ + protocol: 'https', + hostname: 'hbps.adxcg.net', + pathname: '/event/timeout.gif', + search: beaconParams + }); + utils.logWarn(BIDDER_CODE + ': onTimeout called'); + utils.triggerPixel(adxcgRequestUrl); + }, + + getUserSyncs: function (syncOptions, serverResponses, gdprConsent) { + let params = ''; + if (gdprConsent && 'gdprApplies' in gdprConsent) { + if (gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + params += `?gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + } + if (syncOptions.iframeEnabled) { return [{ type: 'iframe', - url: 'https://cdn.adxcg.net/pb-sync.html' - }] + url: 'https://cdn.adxcg.net/pb-sync.html' + params + }]; } } } -function isVideoRequest (bid) { - return bid.mediaType === 'video' || !!utils.deepAccess(bid, 'mediaTypes.video') +function isVideoRequest(bid) { + return bid.mediaType === 'video' || !!utils.deepAccess(bid, 'mediaTypes.video'); +} + +function isBannerRequest(bid) { + return bid.mediaType === 'banner' || !!utils.deepAccess(bid, 'mediaTypes.banner'); +} + +function isNativeRequest(bid) { + return bid.mediaType === 'native' || !!utils.deepAccess(bid, 'mediaTypes.native'); } -function isBannerRequest (bid) { - return bid.mediaType === 'banner' || !!utils.deepAccess(bid, 'mediaTypes.banner') +function getFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return utils.deepAccess(bid, 'params.floor', DEFAULT_MIN_FLOOR); + } + + try { + const floor = bid.getFloor({ + currency: 'EUR', + mediaType: '*', + size: '*', + bidRequest: bid + }); + return floor.floor; + } catch (e) { + utils.logWarn(BIDDER_CODE + ': call to getFloor failed:' + e.message); + return DEFAULT_MIN_FLOOR; + } } -function isNativeRequest (bid) { - return bid.mediaType === 'native' || !!utils.deepAccess(bid, 'mediaTypes.native') +function parseNative(nativeResponse) { + let bidNative = {}; + bidNative = { + clickUrl: nativeResponse.link.url, + impressionTrackers: nativeResponse.imptrackers, + clickTrackers: nativeResponse.clktrackers, + javascriptTrackers: nativeResponse.jstrackers + }; + + nativeResponse.assets.forEach(asset => { + if (asset.title && asset.title.text) { + bidNative.title = asset.title.text; + } + + if (asset.img && asset.img.url) { + bidNative.image = { + url: asset.img.url, + height: asset.img.h, + width: asset.img.w + }; + } + + if (asset.icon && asset.icon.url) { + bidNative.icon = { + url: asset.icon.url, + height: asset.icon.h, + width: asset.icon.w + }; + } + + if (asset.data && asset.data.label === 'DESC' && asset.data.value) { + bidNative.body = asset.data.value; + } + + if (asset.data && asset.data.label === 'SPONSORED' && asset.data.value) { + bidNative.sponsoredBy = asset.data.value; + } + }) + return bidNative; } registerBidder(spec) diff --git a/modules/adxcgBidAdapter.md b/modules/adxcgBidAdapter.md index 39f27adf1637..8eccdb11dee7 100644 --- a/modules/adxcgBidAdapter.md +++ b/modules/adxcgBidAdapter.md @@ -9,74 +9,88 @@ Module that connects to an Adxcg.com zone to fetch bids. # Test Parameters + ``` -var adUnits = [{ - code: 'banner-ad-div', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600] - ] - } - }, - bids: [{ - bidder: 'adxcg', - params: { - adzoneid: '1' - } - }] - }, { - code: 'native-ad-div', - mediaTypes: { - native: { - image: { - sendId: false, - required: true, - sizes: [80, 80] - }, - title: { - required: true, - len: 75 + + + + var adUnits = [{ + code: 'banner-ad-div', + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600] + ] + } }, - body: { - required: true, - len: 200 + bids: [{ + bidder: 'adxcg', + params: { + adzoneid: '1' + } + }] + }, { + code: 'native-ad-div', + mediaTypes: { + native: { + image: { + sendId: false, + required: true, + sizes: [80, 80] + }, + icon: { + sendId: true, + }, + brand: { + sendId: true, + }, + title: { + sendId: false, + required: true, + len: 75 + }, + body: { + sendId: false, + required: true, + len: 200 + }, + sponsoredBy: { + sendId: false, + required: false, + len: 20 + } + } }, - sponsoredBy: { - required: false, - len: 20 - } - } + bids: [{ + bidder: 'adxcg', + params: { + adzoneid: '2379' + } + }] }, - bids: [{ + { + code: 'video-div', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream', + mimes: ['video/mp4'], + protocols: [5, 6, 8], + playback_method: ['auto_play_sound_off'] + } + }, + bids: [{ bidder: 'adxcg', params: { - adzoneid: '2379' + adzoneid: '20', + video: { + maxduration: 100, + skippable: true + } } - } - }] - }, - { - code: 'video-div', - mediaTypes: { - video: { - playerSize: [640, 480], - context: 'instream' + }] } - }, - bids: [{ - bidder: 'adxcg', - params: { - adzoneid: '20', - video: { - maxduration: 100, - mimes: ['video/mp4'], - skippable: true, - playback_method: ['auto_play_sound_off'] - } - } - }] - } - ]; + ]; + ``` diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js index f9aaea308a77..551d50b60e70 100644 --- a/test/spec/modules/adxcgBidAdapter_spec.js +++ b/test/spec/modules/adxcgBidAdapter_spec.js @@ -1,6 +1,7 @@ import {expect} from 'chai'; import {spec} from 'modules/adxcgBidAdapter.js'; import {deepClone, parseUrl} from 'src/utils.js'; +import * as utils from '../../../src/utils.js'; describe('AdxcgAdapter', function () { let bidBanner = { @@ -29,15 +30,15 @@ describe('AdxcgAdapter', function () { adzoneid: '20', video: { api: [2], - protocols: [1, 2], - mimes: ['video/mp4'], maxduration: 30 } }, mediaTypes: { video: { context: 'instream', - playerSize: [[640, 480]] + playerSize: [[640, 480]], + protocols: [1, 2], + mimes: ['video/mp4'], } }, adUnitCode: 'adunit-code', @@ -91,7 +92,7 @@ describe('AdxcgAdapter', function () { expect(spec.isBidRequestValid(bidBanner)).to.equal(true); }); - it('should return true when required params not found', function () { + it('should return false when required params not found', function () { expect(spec.isBidRequestValid({})).to.be.false; }); @@ -106,10 +107,6 @@ describe('AdxcgAdapter', function () { const simpleVideo = JSON.parse(JSON.stringify(bidVideo)); simpleVideo.params.adzoneid = 123; expect(spec.isBidRequestValid(simpleVideo)).to.be.false; - simpleVideo.params.mimes = [1, 2, 3]; - expect(spec.isBidRequestValid(simpleVideo)).to.be.false; - simpleVideo.params.mimes = 'bad type'; - expect(spec.isBidRequestValid(simpleVideo)).to.be.false; }); }); @@ -124,7 +121,7 @@ describe('AdxcgAdapter', function () { let query = parsedRequestUrl.search; expect(query.renderformat).to.equal('javascript'); - expect(query.ver).to.equal('r20191128PB30'); + expect(query.ver).to.equal('r20210330PB40'); expect(query.source).to.equal('pbjs10'); expect(query.pbjs).to.equal('$prebid.version$'); expect(query.adzoneid).to.equal('1'); @@ -158,7 +155,7 @@ describe('AdxcgAdapter', function () { let query = parsedRequestUrl.search; // general part expect(query.renderformat).to.equal('javascript'); - expect(query.ver).to.equal('r20191128PB30'); + expect(query.ver).to.equal('r20210330PB40'); expect(query.source).to.equal('pbjs10'); expect(query.pbjs).to.equal('$prebid.version$'); expect(query.adzoneid).to.equal('20'); @@ -196,7 +193,7 @@ describe('AdxcgAdapter', function () { let query = parsedRequestUrl.search; expect(query.renderformat).to.equal('javascript'); - expect(query.ver).to.equal('r20191128PB30'); + expect(query.ver).to.equal('r20210330PB40'); expect(query.source).to.equal('pbjs10'); expect(query.pbjs).to.equal('$prebid.version$'); expect(query.adzoneid).to.equal('2379'); @@ -327,123 +324,164 @@ describe('AdxcgAdapter', function () { }; let BANNER_RESPONSE = { - body: [ - { - bidId: '84ab500420319d', - bidderCode: 'adxcg', - width: 300, - height: 250, - creativeId: '42', - cpm: 0.45, - currency: 'USD', - netRevenue: true, - ad: '' - } - ], - header: {someheader: 'fakedata'} + body: { + id: 'auctionid', + bidid: '84ab500420319d', + seatbid: [{ + bid: [ + { + impid: '84ab500420319d', + price: 0.45, + crid: '42', + adm: '', + w: 300, + h: 250, + adomain: ['adomain.com'], + cat: ['IAB1-4', 'IAB8-16', 'IAB25-5'], + ext: { + crType: 'banner', + advertiser_id: '777', + advertiser_name: 'advertiser', + agency_name: 'agency' + } + } + ] + }], + cur: 'USD' + }, + headers: {someheader: 'fakedata'} }; let BANNER_RESPONSE_WITHDEALID = { - body: [ - { - bidId: '84ab500420319d', - bidderCode: 'adxcg', - width: 300, - height: 250, - deal_id: '7722', - creativeId: '42', - cpm: 0.45, - currency: 'USD', - netRevenue: true, - ad: '' - } - ], - header: {someheader: 'fakedata'} + body: { + id: 'auctionid', + bidid: '84ab500420319d', + seatbid: [{ + bid: [ + { + impid: '84ab500420319d', + price: 0.45, + crid: '42', + dealid: '7722', + adm: '', + w: 300, + h: 250, + adomain: ['adomain.com'], + ext: { + crType: 'banner' + } + } + ] + }], + cur: 'USD' + } }; let VIDEO_RESPONSE = { - body: [ - { - bidId: '84ab500420319d', - bidderCode: 'adxcg', - width: 640, - height: 360, - creativeId: '42', - cpm: 0.45, - currency: 'USD', - netRevenue: true, - vastUrl: 'vastContentUrl' - } - ], - header: {someheader: 'fakedata'} + body: { + id: 'auctionid', + bidid: '84ab500420319d', + seatbid: [{ + bid: [ + { + impid: '84ab500420319d', + price: 0.45, + crid: '42', + nurl: 'vastContentUrl', + adomain: ['adomain.com'], + w: 640, + h: 360, + ext: { + crType: 'video' + } + } + ] + }], + cur: 'USD' + }, + headers: {someheader: 'fakedata'} }; - let NATIVE_RESPONSE = { - body: [ + let NATIVE_RESPONSEob = { + assets: [ { - bidId: '84ab500420319d', - bidderCode: 'adxcg', - width: 0, - height: 0, - creativeId: '42', - cpm: 0.45, - currency: 'USD', - netRevenue: true, - nativeResponse: { - assets: [ - { - id: 1, - required: 0, - title: { - text: 'titleContent' - } - }, - { - id: 2, - required: 0, - img: { - url: 'imageContent', - w: 600, - h: 600 - } - }, - { - id: 3, - required: 0, - data: { - label: 'DESC', - value: 'descriptionContent' - } - }, - { - id: 0, - required: 0, - data: { - label: 'SPONSORED', - value: 'sponsoredByContent' - } - }, - { - id: 5, - required: 0, - icon: { - url: 'iconContent', - w: 400, - h: 400 - } - } - ], - link: { - url: 'linkContent' - }, - imptrackers: ['impressionTracker1', 'impressionTracker2'] + id: 1, + required: 0, + title: { + text: 'titleContent' + } + }, + { + id: 2, + required: 0, + img: { + url: 'imageContent', + w: 600, + h: 600 + } + }, + { + id: 3, + required: 0, + data: { + label: 'DESC', + value: 'descriptionContent' + } + }, + { + id: 0, + required: 0, + data: { + label: 'SPONSORED', + value: 'sponsoredByContent' + } + }, + { + id: 5, + required: 0, + icon: { + url: 'iconContent', + w: 400, + h: 400 } } ], - header: {someheader: 'fakedata'} + link: { + url: 'linkContent' + }, + imptrackers: ['impressionTracker1', 'impressionTracker2'] + } + + let NATIVE_RESPONSE = { + body: { + id: 'auctionid', + bidid: '84ab500420319d', + seatbid: [{ + bid: [ + { + impid: '84ab500420319d', + price: 0.45, + crid: '42', + w: 0, + h: 0, + adm: JSON.stringify(NATIVE_RESPONSEob), + adomain: ['adomain.com'], + ext: { + crType: 'native' + } + } + ] + }], + cur: 'USD' + }, + headers: {someheader: 'fakedata'} }; it('handles regular responses', function () { + expect(BANNER_RESPONSE).to.exist; + expect(BANNER_RESPONSE.body).to.exist; + expect(BANNER_RESPONSE.body.id).to.exist; + expect(BANNER_RESPONSE.body.seatbid[0]).to.exist; let result = spec.interpretResponse(BANNER_RESPONSE, BIDDER_REQUEST); expect(result).to.have.lengthOf(1); @@ -452,26 +490,30 @@ describe('AdxcgAdapter', function () { expect(result[0].width).to.equal(300); expect(result[0].height).to.equal(250); expect(result[0].creativeId).to.equal(42); - expect(result[0].cpm).to.equal(0.45); + expect(result[0].cpm).to.be.within(0.45, 0.46); expect(result[0].ad).to.equal(''); expect(result[0].currency).to.equal('USD'); expect(result[0].netRevenue).to.equal(true); expect(result[0].ttl).to.equal(300); expect(result[0].dealId).to.not.exist; + expect(result[0].meta.advertiserDomains[0]).to.equal('adomain.com'); + expect(result[0].meta.advertiserId).to.be.eql('777'); + expect(result[0].meta.advertiserName).to.be.eql('advertiser'); + expect(result[0].meta.agencyName).to.be.eql('agency'); + expect(result[0].meta.advertiserDomains).to.be.eql(['adomain.com']); + expect(result[0].meta.secondaryCatIds).to.be.eql(['IAB1-4', 'IAB8-16', 'IAB25-5']); }); it('handles regular responses with dealid', function () { - let result = spec.interpretResponse( - BANNER_RESPONSE_WITHDEALID, - BIDDER_REQUEST - ); + let result = spec.interpretResponse(BANNER_RESPONSE_WITHDEALID); expect(result).to.have.lengthOf(1); expect(result[0].width).to.equal(300); expect(result[0].height).to.equal(250); expect(result[0].creativeId).to.equal(42); - expect(result[0].cpm).to.equal(0.45); + // expect(result[0].cpm).to.equal(0.45); + expect(result[0].cpm).to.be.within(0.45, 0.46); expect(result[0].ad).to.equal(''); expect(result[0].currency).to.equal('USD'); expect(result[0].netRevenue).to.equal(true); @@ -479,7 +521,7 @@ describe('AdxcgAdapter', function () { }); it('handles video responses', function () { - let result = spec.interpretResponse(VIDEO_RESPONSE, BIDDER_REQUEST); + let result = spec.interpretResponse(VIDEO_RESPONSE); expect(result).to.have.lengthOf(1); expect(result[0].width).to.equal(640); @@ -494,17 +536,19 @@ describe('AdxcgAdapter', function () { }); it('handles native responses', function () { - let result = spec.interpretResponse(NATIVE_RESPONSE, BIDDER_REQUEST); + let result = spec.interpretResponse(NATIVE_RESPONSE); expect(result[0].width).to.equal(0); expect(result[0].height).to.equal(0); - expect(result[0].mediaType).to.equal('native'); + expect(result[0].creativeId).to.equal(42); expect(result[0].cpm).to.equal(0.45); expect(result[0].currency).to.equal('USD'); expect(result[0].netRevenue).to.equal(true); expect(result[0].ttl).to.equal(300); + expect(result[0].mediaType).to.equal('native'); + expect(result[0].native.clickUrl).to.equal('linkContent'); expect(result[0].native.impressionTrackers).to.deep.equal([ 'impressionTracker1', @@ -545,4 +589,65 @@ describe('AdxcgAdapter', function () { ); }); }); + + describe('on bidWon', function () { + beforeEach(function () { + sinon.stub(utils, 'triggerPixel'); + }); + afterEach(function () { + utils.triggerPixel.restore(); + }); + it('should replace burl for banner', function () { + const burl = 'burl=${' + 'AUCTION_PRICE}'; + const bid = { + 'bidderCode': 'adxcg', + 'width': 0, + 'height': 0, + 'statusMessage': 'Bid available', + 'adId': '3d0b6ff1dda89', + 'requestId': '2a423489e058a1', + 'mediaType': 'banner', + 'source': 'client', + 'ad': burl, + 'cpm': 0.66, + 'creativeId': '353538_591471', + 'currency': 'USD', + 'dealId': '', + 'netRevenue': true, + 'ttl': 300, + // 'nurl': nurl, + 'burl': burl, + 'isBurl': true, + 'auctionId': 'a92bffce-14d2-4f8f-a78a-7b9b5e4d28fa', + 'responseTimestamp': 1556867386065, + 'requestTimestamp': 1556867385916, + 'bidder': 'adxcg', + 'adUnitCode': 'div-gpt-ad-1555415275793-0', + 'timeToRespond': 149, + 'pbLg': '0.50', + 'pbMg': '0.60', + 'pbHg': '0.66', + 'pbAg': '0.65', + 'pbDg': '0.66', + 'pbCg': '', + 'size': '0x0', + 'adserverTargeting': { + 'hb_bidder': 'mgid', + 'hb_adid': '3d0b6ff1dda89', + 'hb_pb': '0.66', + 'hb_size': '0x0', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_banner_title': 'TITLE', + 'hb_banner_image': 'hb_banner_image:3d0b6ff1dda89', + 'hb_banner_icon': 'IconURL', + 'hb_banner_linkurl': 'hb_banner_linkurl:3d0b6ff1dda89' + }, + 'status': 'targetingSet', + 'params': [{'adzoneid': '20'}] + }; + spec.onBidWon(bid); + expect(bid.burl).to.deep.equal(burl); + }); + }); });