From 448d4db901ec8e646c2a556785bae29ed4ae294d Mon Sep 17 00:00:00 2001 From: Rich Snapp Date: Thu, 22 Feb 2018 12:30:59 -0700 Subject: [PATCH 01/10] only do video caching if we don't already have a videoCacheKey (#2143) * only do video caching if we don't already have a videoCacheKey * log error for missing vastUrl when videoCacheKey specified --- src/auction.js | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/auction.js b/src/auction.js index 0af6458ac09..b8098d1faf4 100644 --- a/src/auction.js +++ b/src/auction.js @@ -224,25 +224,33 @@ function addBidToAuction(auctionInstance, bidResponse) { } // Video bids may fail if the cache is down, or there's trouble on the network. -function tryAddVideoBid(auctionInstance, bidResponse, bidRequest, vastUrl) { +function tryAddVideoBid(auctionInstance, bidResponse, bidRequest) { + let addBid = true; if (config.getConfig('cache.url')) { - store([bidResponse], function(error, cacheIds) { - if (error) { - utils.logWarn(`Failed to save to the video cache: ${error}. Video bid must be discarded.`); - - doCallbacksIfTimedout(auctionInstance, bidResponse); - } else { - bidResponse.videoCacheKey = cacheIds[0].uuid; - if (!vastUrl) { - bidResponse.vastUrl = getCacheUrl(bidResponse.videoCacheKey); + if (!bidResponse.videoCacheKey) { + addBid = false; + store([bidResponse], function (error, cacheIds) { + if (error) { + utils.logWarn(`Failed to save to the video cache: ${error}. Video bid must be discarded.`); + + doCallbacksIfTimedout(auctionInstance, bidResponse); + } else { + bidResponse.videoCacheKey = cacheIds[0].uuid; + if (!bidResponse.vastUrl) { + bidResponse.vastUrl = getCacheUrl(bidResponse.videoCacheKey); + } + // only set this prop after the bid has been cached to avoid early ending auction early in bidsBackAll + bidRequest.doneCbCallCount += 1; + addBidToAuction(auctionInstance, bidResponse); + auctionInstance.bidsBackAll(); } - // only set this prop after the bid has been cached to avoid early ending auction early in bidsBackAll - bidRequest.doneCbCallCount += 1; - addBidToAuction(auctionInstance, bidResponse); - auctionInstance.bidsBackAll(); - } - }); - } else { + }); + } else if (!bidResponse.vastUrl) { + utils.logError('videoCacheKey specified but not required vastUrl for video bid'); + addBid = false; + } + } + if (addBid) { addBidToAuction(auctionInstance, bidResponse); } } @@ -256,7 +264,7 @@ export const addBidResponse = createHook('asyncSeries', function(adUnitCode, bid let bidResponse = getPreparedBidForAuction({adUnitCode, bid, bidRequest, auctionId}); if (bidResponse.mediaType === 'video') { - tryAddVideoBid(auctionInstance, bidResponse, bidRequest, bid.vastUrl); + tryAddVideoBid(auctionInstance, bidResponse, bidRequest); } else { addBidToAuction(auctionInstance, bidResponse); } From 32dccd4bd28c1762a88a69fe0750ac1f0356fa18 Mon Sep 17 00:00:00 2001 From: NasmediaWebtech <34228109+NasmediaWebtech@users.noreply.github.com> Date: Fri, 23 Feb 2018 06:32:45 +0900 Subject: [PATCH 02/10] Update NasmediaAdmixer adapter (#2164) * add NasmediaAdmixer adapter for Perbid.js 1.0 * add NasmediaAdmixer adapter for Perbid.js 1.0 * add NasmediaAdmixer adapter for Perbid.js 1.0 * add NasmediaAdmixer adapter for Perbid.js 1.0 * add NasmediaAdmixer adapter for Perbid.js 1.0 * add NasmediaAdmixer adapter for Perbid.js 1.0 * update NasmediaAdmixer adapter * update NasmediaAdmixer adapter --- modules/nasmediaAdmixerBidAdapter.js | 10 +++++----- modules/nasmediaAdmixerBidAdapter.md | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/nasmediaAdmixerBidAdapter.js b/modules/nasmediaAdmixerBidAdapter.js index e4892f3d0ed..344bf991610 100644 --- a/modules/nasmediaAdmixerBidAdapter.js +++ b/modules/nasmediaAdmixerBidAdapter.js @@ -59,14 +59,14 @@ export const spec = { function getOsType() { let ua = navigator.userAgent.toLowerCase(); - let os = ['android', 'ios', 'mac', 'linux', 'window', 'etc']; - let regexp_os = [/android/i, /iphone|ipad/i, /mac/i, /linux/i, /window/i, '']; + let os = ['android', 'ios', 'mac', 'linux', 'window']; + let regexp_os = [/android/i, /iphone|ipad/i, /mac/i, /linux/i, /window/i]; - return regexp_os.some((tos, idx) => { - if (ua.match(tos)) { + return os.find((tos, idx) => { + if (ua.match(regexp_os[idx])) { return os[idx]; } - }); + }) || 'etc'; } function getSize(sizes) { diff --git a/modules/nasmediaAdmixerBidAdapter.md b/modules/nasmediaAdmixerBidAdapter.md index aa72b7967f8..44797e413a8 100644 --- a/modules/nasmediaAdmixerBidAdapter.md +++ b/modules/nasmediaAdmixerBidAdapter.md @@ -1,7 +1,7 @@ # Overview ``` -Module Name: NasmeidaAdmixer Bidder Adapter +Module Name: NasmediaAdmixer Bidder Adapter Module Type: Bidder Adapter Maintainer: prebid@nasmedia.co.kr ``` From 27758400f51b2f32f14b81047fcd285dee565fec Mon Sep 17 00:00:00 2001 From: rxRTB <34483140+prebidRxRTB@users.noreply.github.com> Date: Fri, 23 Feb 2018 00:33:19 +0300 Subject: [PATCH 03/10] [Edit BidAdapter] rxrtb adapter for Perbid.js 1.0 (#2182) * Add: rxrtb prebidAdapter * Update: params for test * Update: code format * Update: code format * Update: code format * Fix param check * Update rxrtbBidAdapter.js --- modules/rxrtbBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rxrtbBidAdapter.js b/modules/rxrtbBidAdapter.js index c82f1f9b1db..37aa20b68cd 100644 --- a/modules/rxrtbBidAdapter.js +++ b/modules/rxrtbBidAdapter.js @@ -68,7 +68,7 @@ function getDomain(url) { function makePrebidRequest(req) { let host = req.params.host || DEFAULT_HOST; - let url = window.location.protocol + '//' + host + '/dsp?id=' + req.params.id + '&token=' + req.params.token; + let url = '//' + host + '/dsp?id=' + req.params.id + '&token=' + req.params.token; let reqData = makeRtbRequest(req); return { method: 'POST', From 31b0e926488754e22b940b13f6b72fe66cbe68b5 Mon Sep 17 00:00:00 2001 From: Rich Snapp Date: Thu, 22 Feb 2018 17:05:14 -0700 Subject: [PATCH 04/10] only count bid timeouts if bidder didn't call done. fixes #2146 (#2154) --- src/auction.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/auction.js b/src/auction.js index b8098d1faf4..5f8d364f855 100644 --- a/src/auction.js +++ b/src/auction.js @@ -488,7 +488,7 @@ function groupByPlacement(bidsByPlacement, bid) { } /** - * Returns a list of bids that we haven't received a response yet + * Returns a list of bids that we haven't received a response yet where the bidder did not call done * @param {BidRequest[]} bidderRequests List of bids requested for auction instance * @param {BidReceived[]} bidsReceived List of bids received for auction instance * @@ -501,7 +501,8 @@ function groupByPlacement(bidsByPlacement, bid) { * @return {Array} List of bids that Prebid hasn't received a response for */ function getTimedOutBids(bidderRequests, bidsReceived) { - const bidRequestedCodes = bidderRequests + const bidRequestedWithoutDoneCodes = bidderRequests + .filter(bidderRequest => !bidderRequest.doneCbCallCount) .map(bid => bid.bidderCode) .filter(uniques); @@ -509,7 +510,7 @@ function getTimedOutBids(bidderRequests, bidsReceived) { .map(bid => bid.bidder) .filter(uniques); - const timedOutBidderCodes = bidRequestedCodes + const timedOutBidderCodes = bidRequestedWithoutDoneCodes .filter(bidder => !includes(bidReceivedCodes, bidder)); const timedOutBids = bidderRequests From 737ca041e3bef6b766c3e4f3d2c608a182376bbe Mon Sep 17 00:00:00 2001 From: danmarketplace <34161426+danmarketplace@users.noreply.github.com> Date: Fri, 23 Feb 2018 03:08:55 +0300 Subject: [PATCH 05/10] Change bidderCode for DAN Marketplace Bid Adapter (#2183) * Make Bid Adapter for Dentsu Aegis Network Marketplace * Update DAN_Marketplace adapter * Rename danmarketplaceBidAdapter into danmarketBidAdapter --- ...ceBidAdapter.js => danmarketBidAdapter.js} | 4 ++-- ...ceBidAdapter.md => danmarketBidAdapter.md} | 4 ++-- ...er_spec.js => danmarketBidAdapter_spec.js} | 24 +++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) rename modules/{danmarketplaceBidAdapter.js => danmarketBidAdapter.js} (97%) rename modules/{danmarketplaceBidAdapter.md => danmarketBidAdapter.md} (90%) rename test/spec/modules/{danmarketplaceBidAdapter_spec.js => danmarketBidAdapter_spec.js} (94%) diff --git a/modules/danmarketplaceBidAdapter.js b/modules/danmarketBidAdapter.js similarity index 97% rename from modules/danmarketplaceBidAdapter.js rename to modules/danmarketBidAdapter.js index 24b3682042e..619bd893ea2 100644 --- a/modules/danmarketplaceBidAdapter.js +++ b/modules/danmarketBidAdapter.js @@ -1,6 +1,6 @@ import * as utils from 'src/utils'; import {registerBidder} from 'src/adapters/bidderFactory'; -const BIDDER_CODE = 'danmarketplace'; +const BIDDER_CODE = 'danmarket'; const ENDPOINT_URL = '//ads.danmarketplace.com/hb'; const TIME_TO_LIVE = 360; const ADAPTER_SYNC_URL = '//ads.danmarketplace.com/push_sync'; @@ -24,7 +24,7 @@ const LOG_ERROR_MESS = { export const spec = { code: BIDDER_CODE, - aliases: ['DANMarketplace', 'DAN_Marketplace'], + aliases: ['DANMarketplace', 'DAN_Marketplace', 'danmarketplace'], isBidRequestValid: function(bid) { return !!bid.params.uid; diff --git a/modules/danmarketplaceBidAdapter.md b/modules/danmarketBidAdapter.md similarity index 90% rename from modules/danmarketplaceBidAdapter.md rename to modules/danmarketBidAdapter.md index 263385949bd..8ddc83d2cf6 100755 --- a/modules/danmarketplaceBidAdapter.md +++ b/modules/danmarketBidAdapter.md @@ -16,7 +16,7 @@ Module that connects to DAN Marketplace demand source to fetch bids. sizes: [[300, 250]], bids: [ { - bidder: "danmarketplace", + bidder: "danmarket", params: { uid: '4', priceType: 'gross' // by default is 'net' @@ -28,7 +28,7 @@ Module that connects to DAN Marketplace demand source to fetch bids. sizes: [[728, 90]], bids: [ { - bidder: "danmarketplace", + bidder: "danmarket", params: { uid: 5, priceType: 'gross' diff --git a/test/spec/modules/danmarketplaceBidAdapter_spec.js b/test/spec/modules/danmarketBidAdapter_spec.js similarity index 94% rename from test/spec/modules/danmarketplaceBidAdapter_spec.js rename to test/spec/modules/danmarketBidAdapter_spec.js index ee7f844ae98..d1b86d1bf53 100644 --- a/test/spec/modules/danmarketplaceBidAdapter_spec.js +++ b/test/spec/modules/danmarketBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec } from 'modules/danmarketplaceBidAdapter'; +import { spec } from 'modules/danmarketBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; describe('DAN_Marketplace Adapter', function () { @@ -13,7 +13,7 @@ describe('DAN_Marketplace Adapter', function () { describe('isBidRequestValid', () => { let bid = { - 'bidder': 'danmarketplace', + 'bidder': 'danmarket', 'params': { 'uid': '4' }, @@ -41,7 +41,7 @@ describe('DAN_Marketplace Adapter', function () { describe('buildRequests', () => { let bidRequests = [ { - 'bidder': 'danmarketplace', + 'bidder': 'danmarket', 'params': { 'uid': '5' }, @@ -52,7 +52,7 @@ describe('DAN_Marketplace Adapter', function () { 'auctionId': '1d1a030790a475', }, { - 'bidder': 'danmarketplace', + 'bidder': 'danmarket', 'params': { 'uid': '5' }, @@ -63,7 +63,7 @@ describe('DAN_Marketplace Adapter', function () { 'auctionId': '1d1a030790a475', }, { - 'bidder': 'danmarketplace', + 'bidder': 'danmarket', 'params': { 'uid': '6' }, @@ -130,7 +130,7 @@ describe('DAN_Marketplace Adapter', function () { it('should get correct bid response', () => { const bidRequests = [ { - 'bidder': 'danmarketplace', + 'bidder': 'danmarket', 'params': { 'uid': '4' }, @@ -164,7 +164,7 @@ describe('DAN_Marketplace Adapter', function () { it('should get correct multi bid response', () => { const bidRequests = [ { - 'bidder': 'danmarketplace', + 'bidder': 'danmarket', 'params': { 'uid': '4' }, @@ -175,7 +175,7 @@ describe('DAN_Marketplace Adapter', function () { 'auctionId': '1fa09aee5c8c99', }, { - 'bidder': 'danmarketplace', + 'bidder': 'danmarket', 'params': { 'uid': '5' }, @@ -186,7 +186,7 @@ describe('DAN_Marketplace Adapter', function () { 'auctionId': '1fa09aee5c8c99', }, { - 'bidder': 'danmarketplace', + 'bidder': 'danmarket', 'params': { 'uid': '4' }, @@ -244,7 +244,7 @@ describe('DAN_Marketplace Adapter', function () { it('handles wrong and nobid responses', () => { const bidRequests = [ { - 'bidder': 'danmarketplace', + 'bidder': 'danmarket', 'params': { 'uid': '6' }, @@ -255,7 +255,7 @@ describe('DAN_Marketplace Adapter', function () { 'auctionId': '1fa09aee5c84d34', }, { - 'bidder': 'danmarketplace', + 'bidder': 'danmarket', 'params': { 'uid': '7' }, @@ -266,7 +266,7 @@ describe('DAN_Marketplace Adapter', function () { 'auctionId': '1fa09aee5c84d34', }, { - 'bidder': 'danmarketplace', + 'bidder': 'danmarket', 'params': { 'uid': '8' }, From 0ddebdf7b8033a6a158b8041234fffc6eb125ae7 Mon Sep 17 00:00:00 2001 From: jeremyAdyoulike Date: Fri, 23 Feb 2018 11:09:44 +0100 Subject: [PATCH 06/10] Update Adyoulike Adapter to prebid 1.0 (#2077) * feat(adyoulikeAdapter): update adapter * fix(payload): fix height and width request keys * fix(Request): set credentials to false --- modules/adyoulikeBidAdapter.js | 197 ++++++++++++ modules/adyoulikeBidAdapter.md | 29 ++ test/spec/modules/adyoulikeBidAdapter_spec.js | 300 ++++++++++++++++++ 3 files changed, 526 insertions(+) create mode 100644 modules/adyoulikeBidAdapter.js create mode 100644 modules/adyoulikeBidAdapter.md create mode 100644 test/spec/modules/adyoulikeBidAdapter_spec.js diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js new file mode 100644 index 00000000000..a73421f7ca5 --- /dev/null +++ b/modules/adyoulikeBidAdapter.js @@ -0,0 +1,197 @@ +import * as utils from 'src/utils'; +import { format } from 'src/url'; +// import { config } from 'src/config'; +import { registerBidder } from 'src/adapters/bidderFactory'; + +const VERSION = '1.0'; +const BIDDER_CODE = 'adyoulike'; +const DEFAULT_DC = 'hb-api'; + +export const spec = { + code: BIDDER_CODE, + aliases: ['ayl'], // short code + /** + * 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) { + const sizes = getSize(bid.sizes); + if (!bid.params || !bid.params.placement || !sizes.width || !sizes.height) { + return false; + } + return true; + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {bidderRequest} - bidderRequest.bids[] is an array of AdUnits and bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (bidderRequest) { + let dcHostname = getHostname(bidderRequest); + const payload = { + Version: VERSION, + Bids: bidderRequest.reduce((accumulator, bid) => { + let size = getSize(bid.sizes); + accumulator[bid.bidId] = {}; + accumulator[bid.bidId].PlacementID = bid.params.placement; + accumulator[bid.bidId].TransactionID = bid.transactionId; + accumulator[bid.bidId].Width = size.width; + accumulator[bid.bidId].Height = size.height; + return accumulator; + }, {}), + PageRefreshed: getPageRefreshed() + }; + const data = JSON.stringify(payload); + const options = { + withCredentials: false + }; + + return { + method: 'POST', + url: createEndpoint(dcHostname), + data, + options + }; + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, bidRequest) { + const bidResponses = []; + // For this adapter, serverResponse is a list + serverResponse.body.forEach(response => { + const bid = createBid(response); + if (bid) { + bidResponses.push(bid); + } + }); + return bidResponses; + } +} + +/* Get hostname from bids */ +function getHostname(bidderRequest) { + let dcHostname = bidderRequest.find(bid => bid.params.DC); + if (dcHostname) { + return ('-' + dcHostname.params.DC); + } + return ''; +} + +/* Get current page referrer url */ +function getReferrerUrl() { + let referer = ''; + if (window.self !== window.top) { + try { + referer = window.top.document.referrer; + } catch (e) { } + } else { + referer = document.referrer; + } + return referer; +} + +/* Get current page canonical url */ +function getCanonicalUrl() { + let link; + if (window.self !== window.top) { + try { + link = window.top.document.head.querySelector('link[rel="canonical"][href]'); + } catch (e) { } + } else { + link = document.head.querySelector('link[rel="canonical"][href]'); + } + + if (link) { + return link.href; + } + return ''; +} + +/* Get information on page refresh */ +function getPageRefreshed() { + try { + if (performance && performance.navigation) { + return performance.navigation.type === performance.navigation.TYPE_RELOAD; + } + } catch (e) { } + return false; +} + +/* Create endpoint url */ +function createEndpoint(host) { + return format({ + protocol: (document.location.protocol === 'https:') ? 'https' : 'http', + host: `${DEFAULT_DC}${host}.omnitagjs.com`, + pathname: '/hb-api/prebid/v1', + search: createEndpointQS() + }); +} + +/* Create endpoint query string */ +function createEndpointQS() { + const qs = {}; + + const ref = getReferrerUrl(); + if (ref) { + qs.RefererUrl = encodeURIComponent(ref); + } + + const can = getCanonicalUrl(); + if (can) { + qs.CanonicalUrl = encodeURIComponent(can); + } + + return qs; +} + +/* Get parsed size from request size */ +function getSize(requestSizes) { + const parsed = {}; + const size = utils.parseSizesInput(requestSizes)[0]; + + if (typeof size !== 'string') { + return parsed; + } + + const parsedSize = size.toUpperCase().split('X'); + const width = parseInt(parsedSize[0], 10); + if (width) { + parsed.width = width; + } + + const height = parseInt(parsedSize[1], 10); + if (height) { + parsed.height = height; + } + + return parsed; +} + +/* Create bid from response */ +function createBid(response) { + if (!response || !response.Ad) { + return + } + + return { + requestId: response.BidID, + bidderCode: spec.code, + width: response.Width, + height: response.Height, + ad: response.Ad, + ttl: 3600, + creativeId: response.CreativeID, + cpm: response.Price, + netRevenue: true, + currency: 'USD' + }; +} + +registerBidder(spec); diff --git a/modules/adyoulikeBidAdapter.md b/modules/adyoulikeBidAdapter.md new file mode 100644 index 00000000000..120bee3bcbb --- /dev/null +++ b/modules/adyoulikeBidAdapter.md @@ -0,0 +1,29 @@ +# Overview + +Module Name: Adyoulike Bidder Adapter +Module Type: Bidder Adapter +Maintainer: prebid@adyoulike.com + +# Description + +Module that connects to Adyoulike demand sources. +Banner formats are supported. + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[300, 250]], + bids: [ + { + bidder: "adyoulike", + params: { + placement: 194f787b85c829fb8822cdaf1ae64435, + DC: 'fra01', // Optional for set the data center name + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js new file mode 100644 index 00000000000..cb9126619f2 --- /dev/null +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -0,0 +1,300 @@ +import { expect } from 'chai'; +import { parse } from '../../../src/url'; + +import { spec } from 'modules/adyoulikeBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('Adyoulike Adapter', () => { + const canonicalUrl = 'http://canonical.url/?t=%26'; + const defaultDC = 'hb-api'; + const bidderCode = 'adyoulike'; + const bidRequestWithEmptyPlacement = [ + { + 'bidId': 'bid_id_0', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-0', + 'params': {}, + 'sizes': '300x250' + } + ]; + const bidRequestWithEmptySizes = { + 'bidderCode': 'adyoulike', + 'bids': [ + { + 'bidId': 'bid_id_0', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-0', + 'params': { + 'placement': 'placement_0' + }, + 'transactionId': 'bid_id_0_transaction_id' + } + ], + }; + + const bidRequestWithSinglePlacement = [ + { + 'bidId': 'bid_id_0', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-0', + 'params': { + 'placement': 'placement_0' + }, + 'sizes': '300x250', + 'transactionId': 'bid_id_0_transaction_id' + } + ]; + + const bidRequestWithDCPlacement = [ + { + 'bidId': 'bid_id_0', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-0', + 'params': { + 'placement': 'placement_0', + 'DC': 'fra01' + }, + 'sizes': '300x250', + 'transactionId': 'bid_id_0_transaction_id' + } + ]; + + const bidRequestMultiPlacements = [ + { + 'bidId': 'bid_id_0', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-0', + 'params': { + 'placement': 'placement_0' + }, + 'sizes': '300x250', + 'transactionId': 'bid_id_0_transaction_id' + }, + { + 'bidId': 'bid_id_1', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-1', + 'params': { + 'placement': 'placement_1' + }, + 'sizes': [[300, 600]], + 'transactionId': 'bid_id_1_transaction_id' + }, + { + 'bidId': 'bid_id_2', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-2', + 'params': {}, + 'sizes': '300x400', + 'transactionId': 'bid_id_2_transaction_id' + }, + { + 'bidId': 'bid_id_3', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-3', + 'params': { + 'placement': 'placement_3' + }, + 'transactionId': 'bid_id_3_transaction_id' + } + ]; + + const responseWithEmptyPlacement = [ + { + 'Placement': 'placement_0' + } + ]; + const responseWithSinglePlacement = [ + { + 'BidID': 'bid_id_0', + 'Placement': 'placement_0', + 'Ad': 'placement_0', + 'Price': 0.5, + 'Height': 300, + 'Width': 300, + } + ]; + const responseWithMultiplePlacements = [ + { + 'BidID': 'bid_id_0', + 'Placement': 'placement_0', + 'Ad': 'placement_0', + 'Price': 0.5, + 'Height': 300, + 'Width': 300, + }, + { + 'BidID': 'bid_id_1', + 'Placement': 'placement_1', + 'Ad': 'placement_1', + 'Price': 0.6, + 'Height': 300, + 'Width': 300, + } + ]; + const adapter = newBidder(spec); + + let getEndpoint = (dc = defaultDC) => `http://${dc}.omnitagjs.com/hb-api/prebid`; + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', () => { + let bid = { + 'bidId': 'bid_id_1', + 'bidder': 'adyoulike', + 'placementCode': 'adunit/hb-1', + 'params': { + 'placement': 'placement_1' + }, + 'sizes': [[300, 600]], + 'transactionId': 'bid_id_1_transaction_id' + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.size; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placement': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + let canonicalQuery; + + beforeEach(() => { + let canonical = document.createElement('link'); + canonical.rel = 'canonical'; + canonical.href = canonicalUrl; + canonicalQuery = sinon.stub(window.top.document.head, 'querySelector'); + canonicalQuery.withArgs('link[rel="canonical"][href]').returns(canonical); + }); + + afterEach(() => { + canonicalQuery.restore(); + }); + + it('sends bid request to endpoint with single placement', () => { + const request = spec.buildRequests(bidRequestWithSinglePlacement); + const payload = JSON.parse(request.data); + + expect(request.url).to.contain(getEndpoint()); + expect(request.method).to.equal('POST'); + expect(request.url).to.contains('CanonicalUrl=' + encodeURIComponent(canonicalUrl)); + + expect(payload.Version).to.equal('1.0'); + expect(payload.Bids['bid_id_0'].PlacementID).to.be.equal('placement_0'); + expect(payload.PageRefreshed).to.equal(false); + expect(payload.Bids['bid_id_0'].TransactionID).to.be.equal('bid_id_0_transaction_id'); + }); + + it('sends bid request to endpoint with single placement without canonical', () => { + canonicalQuery.restore(); + const request = spec.buildRequests(bidRequestWithSinglePlacement); + const payload = JSON.parse(request.data); + + expect(request.url).to.contain(getEndpoint()); + expect(request.method).to.equal('POST'); + + expect(request.url).to.not.contains('CanonicalUrl=' + encodeURIComponent(canonicalUrl)); + expect(payload.Version).to.equal('1.0'); + expect(payload.Bids['bid_id_0'].PlacementID).to.be.equal('placement_0'); + expect(payload.PageRefreshed).to.equal(false); + expect(payload.Bids['bid_id_0'].TransactionID).to.be.equal('bid_id_0_transaction_id'); + }); + + it('sends bid request to endpoint with multiple placements', () => { + const request = spec.buildRequests(bidRequestMultiPlacements); + const payload = JSON.parse(request.data); + expect(request.url).to.contain(getEndpoint()); + expect(request.method).to.equal('POST'); + + expect(request.url).to.contains('CanonicalUrl=' + encodeURIComponent(canonicalUrl)); + + expect(payload.Version).to.equal('1.0'); + + expect(payload.Bids['bid_id_0'].PlacementID).to.be.equal('placement_0'); + expect(payload.Bids['bid_id_1'].PlacementID).to.be.equal('placement_1'); + expect(payload.Bids['bid_id_3'].PlacementID).to.be.equal('placement_3'); + + expect(payload.Bids['bid_id_0'].TransactionID).to.be.equal('bid_id_0_transaction_id'); + expect(payload.Bids['bid_id_1'].TransactionID).to.be.equal('bid_id_1_transaction_id'); + expect(payload.Bids['bid_id_3'].TransactionID).to.be.equal('bid_id_3_transaction_id'); + expect(payload.PageRefreshed).to.equal(false); + }); + + it('sends bid request to endpoint setted by parameters', () => { + const request = spec.buildRequests(bidRequestWithDCPlacement); + const payload = JSON.parse(request.data); + + expect(request.url).to.contain(getEndpoint(`${defaultDC}-fra01`)); + }); + }); + // + describe('interpretResponse', () => { + let serverResponse; + + beforeEach(() => { + serverResponse = { + body: {} + } + }); + + it('handles nobid responses', () => { + let response = [{ + BidID: '123dfsdf', + Attempt: '32344fdse1', + Placement: '12df1' + }]; + serverResponse.body = response; + let result = spec.interpretResponse(serverResponse, []); + expect(result).deep.equal([]); + }); + + it('receive reponse with single placement', () => { + serverResponse.body = responseWithSinglePlacement; + let result = spec.interpretResponse(serverResponse, bidRequestWithSinglePlacement); + + expect(result.length).to.equal(1); + expect(result[0].cpm).to.equal(0.5); + expect(result[0].ad).to.equal('placement_0'); + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(300); + }); + + it('receive reponse with multiple placement', () => { + serverResponse.body = responseWithMultiplePlacements; + let result = spec.interpretResponse(serverResponse, bidRequestMultiPlacements); + + expect(result.length).to.equal(2); + + expect(result[0].bidderCode).to.equal(bidderCode); + expect(result[0].cpm).to.equal(0.5); + expect(result[0].ad).to.equal('placement_0'); + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(300); + + expect(result[1].bidderCode).to.equal(bidderCode); + expect(result[1].cpm).to.equal(0.6); + expect(result[1].ad).to.equal('placement_1'); + expect(result[1].width).to.equal(300); + expect(result[1].height).to.equal(300); + }); + }); +}); From 37ead2712e869051c255ed6637b3530f3bb13754 Mon Sep 17 00:00:00 2001 From: Devastator Date: Fri, 23 Feb 2018 22:36:27 +0800 Subject: [PATCH 07/10] Update Innity Adapter to Prebid.js v1.0 (#2180) * Innity Adapter for Prebid.js 1.0 * Fix Innity Adapter Test Spec Error (#2180) * Fix utils.timestamp and remove userSyncs (#2180) --- modules/innityBidAdapter.js | 54 +++++++++++ modules/innityBidAdapter.md | 25 ++++++ test/spec/modules/innityBidAdapter_spec.js | 100 +++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 modules/innityBidAdapter.js create mode 100644 modules/innityBidAdapter.md create mode 100644 test/spec/modules/innityBidAdapter_spec.js diff --git a/modules/innityBidAdapter.js b/modules/innityBidAdapter.js new file mode 100644 index 00000000000..880af2b6112 --- /dev/null +++ b/modules/innityBidAdapter.js @@ -0,0 +1,54 @@ +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'innity'; +const ENDPOINT = location.protocol + '//as.innity.com/synd/'; + +export const spec = { + code: BIDDER_CODE, + isBidRequestValid: function(bid) { + return !!(bid.params && bid.params.pub && bid.params.zone); + }, + buildRequests: function(validBidRequests) { + return validBidRequests.map(bidRequest => { + let parseSized = utils.parseSizesInput(bidRequest.sizes); + let arrSize = parseSized[0].split('x'); + return { + method: 'GET', + url: ENDPOINT, + data: { + cb: utils.timestamp(), + ver: 2, + hb: 1, + output: 'js', + pub: bidRequest.params.pub, + zone: bidRequest.params.zone, + url: encodeURIComponent(utils.getTopWindowUrl()), + width: arrSize[0], + height: arrSize[1], + vpw: window.screen.width, + vph: window.screen.height, + callback: 'json', + callback_uid: bidRequest.bidId, + auction: bidRequest.auctionId, + }, + }; + }); + }, + interpretResponse: function(serverResponse, request) { + const res = serverResponse.body; + const bidResponse = { + requestId: res.callback_uid, + cpm: parseFloat(res.cpm) / 100, + width: res.width, + height: res.height, + creativeId: res.creative_id, + currency: 'USD', + netRevenue: true, + ttl: 60, + ad: '' + res.tag, + }; + return [bidResponse]; + } +} +registerBidder(spec); diff --git a/modules/innityBidAdapter.md b/modules/innityBidAdapter.md new file mode 100644 index 00000000000..b359a591ce8 --- /dev/null +++ b/modules/innityBidAdapter.md @@ -0,0 +1,25 @@ +# Overview + +**Module Name**: Innity Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: engtat@innity.com + +# Description + +Innity Bidder Adapter for Prebid.js. + +# Test Parameters +``` + var adUnits = [{ + code: 'div-gpt-ad-1460505748561-0', + sizes: [[300, 250]], + bids: [{ + bidder: 'innity', + params: { + pub: 267, + zone: 62546 + } + }] + }]; + +``` \ No newline at end of file diff --git a/test/spec/modules/innityBidAdapter_spec.js b/test/spec/modules/innityBidAdapter_spec.js new file mode 100644 index 00000000000..87042ca6a6d --- /dev/null +++ b/test/spec/modules/innityBidAdapter_spec.js @@ -0,0 +1,100 @@ +import { expect } from 'chai'; +import { spec } from 'modules/innityBidAdapter'; + +describe('innityAdapterTest', () => { + describe('bidRequestValidity', () => { + it('bidRequest with pub ID and zone ID param', () => { + expect(spec.isBidRequestValid({ + bidder: 'innity', + params: { + 'pub': 267, + 'zone': 62546 + }, + })).to.equal(true); + }); + + it('bidRequest with no required params', () => { + expect(spec.isBidRequestValid({ + bidder: 'innity', + params: { + }, + })).to.equal(false); + }); + }); + + describe('bidRequest', () => { + const bidRequests = [{ + 'bidder': 'innity', + 'params': { + 'pub': 267, + 'zone': 62546 + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [300, 250], + 'bidId': '51ef8751f9aead', + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + }]; + + it('bidRequest HTTP method', () => { + const requests = spec.buildRequests(bidRequests); + requests.forEach(function(requestItem) { + expect(requestItem.method).to.equal('GET'); + }); + }); + + it('bidRequest data', () => { + const requests = spec.buildRequests(bidRequests); + expect(requests[0].data.pub).to.equal(267); + expect(requests[0].data.zone).to.equal(62546); + expect(requests[0].data.width).to.equal('300'); + expect(requests[0].data.height).to.equal('250'); + expect(requests[0].data.callback_uid).to.equal('51ef8751f9aead'); + }); + }); + + describe('interpretResponse', () => { + const bidRequest = { + 'method': 'GET', + 'url': 'https://as.innity.com/synd/?', + 'data': { + 'ver': 2, + 'hb': 1, + 'output': 'js', + 'pub': 267, + 'zone': 62546, + 'width': '300', + 'height': '250', + 'callback': 'json', + 'callback_uid': '51ef8751f9aead', + 'url': 'https://example.com', + 'cb': '', + } + }; + + const bidResponse = { + body: { + 'cpm': 100, + 'width': '300', + 'height': '250', + 'creative_id': '148186', + 'callback_uid': '51ef8751f9aead', + 'tag': '', + }, + headers: {} + }; + + it('result is correct', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + expect(result[0].requestId).to.equal('51ef8751f9aead'); + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal('300'); + expect(result[0].height).to.equal('250'); + expect(result[0].creativeId).to.equal('148186'); + expect(result[0].currency).to.equal('USD'); + expect(result[0].ttl).to.equal(60); + expect(result[0].ad).to.equal(''); + }); + }); +}); From 324d530aed8582b67aa73d9eac427e8e727b2634 Mon Sep 17 00:00:00 2001 From: piwanczak <36727380+piwanczak@users.noreply.github.com> Date: Fri, 23 Feb 2018 16:43:38 +0100 Subject: [PATCH 08/10] [NEW Adapter] RTBHouseBidAdapter (#2184) * [NEW Adapter] RTBHouseBidAdapter * remove trailing space in a comment --- modules/rtbhouseBidAdapter.js | 117 +++++++++++++++++ modules/rtbhouseBidAdapter.md | 31 +++++ test/spec/modules/rtbhouseBidAdapter_spec.js | 129 +++++++++++++++++++ 3 files changed, 277 insertions(+) create mode 100644 modules/rtbhouseBidAdapter.js create mode 100644 modules/rtbhouseBidAdapter.md create mode 100644 test/spec/modules/rtbhouseBidAdapter_spec.js diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js new file mode 100644 index 00000000000..181cf80d5e7 --- /dev/null +++ b/modules/rtbhouseBidAdapter.js @@ -0,0 +1,117 @@ +import * as utils from 'src/utils'; +import { BANNER } from 'src/mediaTypes'; +import { registerBidder } from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'rtbhouse'; +const REGIONS = ['prebid-eu', 'prebid-us', 'prebid-asia']; +const ENDPOINT_URL = 'creativecdn.com/bidder/prebid/bids'; +const DEFAULT_CURRENCY_ARR = ['USD']; // NOTE - USD is the only supported currency right now; Hardcoded for bids + +/** + * Helpers + */ + +function buildEndpointUrl(region) { + return 'https://' + region + '.' + ENDPOINT_URL; +} + +/** + * Produces an OpenRTBImpression from a slot config. + */ +function mapImpression(slot) { + return { + id: slot.bidId, + banner: mapBanner(slot), + tagid: slot.adUnitCode.toString(), + }; +} + +/** + * Produces an OpenRTB Banner object for the slot given. + */ +function mapBanner(slot) { + return { + w: slot.sizes[0][0], + h: slot.sizes[0][1], + format: mapSizes(slot.sizes) + }; +} + +/** + * Produce openRTB banner.format object + */ +function mapSizes(slot_sizes) { + const format = []; + slot_sizes.forEach(elem => { + format.push({ + w: elem[0], + h: elem[1] + }); + }); + return format; +} + +/** + * Produces an OpenRTB site object. + */ +function mapSite(validRequest) { + const pubId = validRequest && validRequest.length > 0 ? validRequest[0].params.publisherId : 'unknown'; + return { + publisher: { + id: pubId.toString(), + }, + page: utils.getTopWindowUrl(), + name: utils.getOrigin() + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + + isBidRequestValid: function (bid) { + return !!(REGIONS.includes(bid.params.region) && bid.params.publisherId); + }, + + buildRequests: function (validBidRequests) { + const request = { + id: validBidRequests[0].auctionId, + imp: validBidRequests.map(slot => mapImpression(slot)), + site: mapSite(validBidRequests), + cur: DEFAULT_CURRENCY_ARR, + test: validBidRequests[0].params.test || 0 + }; + return { + method: 'POST', + url: buildEndpointUrl(validBidRequests[0].params.region), + data: JSON.stringify(request) + }; + }, + interpretResponse: function (serverResponse, originalRequest) { + serverResponse = serverResponse.body; + const bids = []; + + if (utils.isArray(serverResponse)) { + serverResponse.forEach(serverBid => { + if (serverBid.price !== 0) { + const bid = { + requestId: serverBid.impid, + mediaType: BANNER, + cpm: serverBid.price, + creativeId: serverBid.adid, + ad: serverBid.adm, + width: serverBid.w, + height: serverBid.h, + ttl: 55, + netRevenue: true, + currency: 'USD' + }; + bids.push(bid); + } + }); + } + return bids; + } +}; + +registerBidder(spec); diff --git a/modules/rtbhouseBidAdapter.md b/modules/rtbhouseBidAdapter.md new file mode 100644 index 00000000000..47deed1a277 --- /dev/null +++ b/modules/rtbhouseBidAdapter.md @@ -0,0 +1,31 @@ +# Overview + +Module Name: RTB House Bidder Adapter +Module Type: Bidder Adapter +Maintainer: prebid@rtbhouse.com + +# Description + +Connects to RTB House unique demand. +Banner formats are supported. +Unique publisherId is required. +Please reach out to pmp@rtbhouse.com to receive your own + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[300, 250]], + bids: [ + { + bidder: "rtbhouse", + params: { + region: 'prebid-eu', + publisherId: 'PREBID_TEST_ID' + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/rtbhouseBidAdapter_spec.js b/test/spec/modules/rtbhouseBidAdapter_spec.js new file mode 100644 index 00000000000..69bd3f40f72 --- /dev/null +++ b/test/spec/modules/rtbhouseBidAdapter_spec.js @@ -0,0 +1,129 @@ +import { expect } from 'chai'; +import { spec } from 'modules/rtbhouseBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const REGIONS = ['prebid-eu', 'prebid-us', 'prebid-asia']; +const ENDPOINT_URL = 'creativecdn.com/bidder/prebid/bids'; + +/** + * Helpers + */ + +function buildEndpointUrl(region) { + return 'https://' + region + '.' + ENDPOINT_URL; +} + +/** + * endof Helpers + */ + +describe('RTBHouseAdapter', () => { + const adapter = newBidder(spec); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', () => { + let bid = { + 'bidder': 'rtbhouse', + 'params': { + 'publisherId': 'PREBID_TEST', + 'region': 'prebid-eu' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'someIncorrectParam': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + let bidRequests = [ + { + 'bidder': 'rtbhouse', + 'params': { + 'publisherId': 'PREBID_TEST', + 'region': 'prebid-eu', + 'test': 1 + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475' + } + ]; + + it('should build test param into the request', () => { + let builtTestRequest = spec.buildRequests(bidRequests).data; + expect(JSON.parse(builtTestRequest).test).to.equal(1); + }); + + it('sends bid request to ENDPOINT via POST', () => { + let bidRequest = Object.assign([], bidRequests); + delete bidRequest[0].params.test; + + const request = spec.buildRequests(bidRequest); + expect(request.url).to.equal(buildEndpointUrl(bidRequest[0].params.region)); + expect(request.method).to.equal('POST'); + }); + }) + + describe('interpretResponse', () => { + let response = [{ + 'id': 'bidder_imp_identifier', + 'impid': '552b8922e28f27', + 'price': 0.5, + 'adid': 'Ad_Identifier', + 'adm': '', + 'adomain': ['rtbhouse.com'], + 'cid': 'Ad_Identifier', + 'w': 300, + 'h': 250 + }]; + + it('should get correct bid response', () => { + let expectedResponse = [ + { + 'requestId': '552b8922e28f27', + 'cpm': 0.5, + 'creativeId': 29681110, + 'width': 300, + 'height': 250, + 'ad': '', + 'mediaType': 'banner', + 'currency': 'USD', + 'ttl': 300, + 'netRevenue': true + } + ]; + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', () => { + let response = ''; + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result.length).to.equal(0); + }); + }); +}); From fd7b24a1c08c08078acf6f7aad648085087d3072 Mon Sep 17 00:00:00 2001 From: Miller Date: Fri, 23 Feb 2018 23:19:49 +0200 Subject: [PATCH 09/10] Add multiple bids request (#2136) * Vertamedia prebid.js adapter initial * sync * remove https * add http * remove let * fix typo * fix spaces * add descriptionUrl; remove log * fix getSize * add usege parseSizesInput * Add support for vastTag * Add moulty bid request; set vastUrl for bider; * update for vertamedia adapter to Prebid 1.0 * Add vertamedia md file * Add required fields * Remove defaults; fix md file; * add outstream mediaType support * add outstream mediaType support * Resolve conflicts * fix test * Add Vertamedia display adapter * remove Boolean convert for isBidRequestValid * Remove boolean convert for isBidRequestValid method; fix tests; fix typo * add display support for Vertamedia adapter; documentation update * update tests for VertamediaBidAdapter * add supportedMediaTypes * Add comments * Add moulty mediatypes support * cleanup logs * Add sending multiple sizes * add multiple bids reguest --- modules/vertamediaBidAdapter.js | 92 ++++++++++++------- .../spec/modules/vertamediaBidAdapter_spec.js | 12 +-- 2 files changed, 65 insertions(+), 39 deletions(-) diff --git a/modules/vertamediaBidAdapter.js b/modules/vertamediaBidAdapter.js index 238495f3966..5876f0b2e7e 100644 --- a/modules/vertamediaBidAdapter.js +++ b/modules/vertamediaBidAdapter.js @@ -5,6 +5,7 @@ import {Renderer} from 'src/Renderer'; import findIndex from 'core-js/library/fn/array/find-index'; const URL = '//hb2.vertamedia.com/auction/'; +const OUTSTREAM_SRC = '//player.vertamedia.com/outstream-unit/2.01/outstream.min.js'; const BIDDER_CODE = 'vertamedia'; const OUTSTREAM = 'outstream'; const DISPLAY = 'display'; @@ -22,14 +23,12 @@ export const spec = { * @param bidderRequest */ buildRequests: function (bidRequests, bidderRequest) { - return bidRequests.map((bid) => { - return { - data: prepareRTBRequestParams(bid), - bidderRequest, - method: 'GET', - url: URL - }; - }); + return { + data: bidToTag(bidRequests), + bidderRequest, + method: 'GET', + url: URL + }; }, /** @@ -40,49 +39,76 @@ export const spec = { */ interpretResponse: function (serverResponse, {bidderRequest}) { serverResponse = serverResponse.body; - const isInvalidValidResp = !serverResponse || !serverResponse.bids || !serverResponse.bids.length; - let bids = []; - if (isInvalidValidResp) { - let extMessage = serverResponse && serverResponse.ext && serverResponse.ext.message ? `: ${serverResponse.ext.message}` : ''; - let errorMessage = `in response for ${bidderRequest.bidderCode} adapter ${extMessage}`; + if (!utils.isArray(serverResponse)) { + return parseRTBResponse(serverResponse, bidderRequest); + } - utils.logError(errorMessage); + serverResponse.forEach(serverBidResponse => { + bids = utils.flatten(bids, parseRTBResponse(serverBidResponse, bidderRequest)); + }); - return bids; - } + return bids; + } +}; - serverResponse.bids.forEach(serverBid => { - const requestId = findIndex(bidderRequest.bids, (bidRequest) => { - return bidRequest.bidId === serverBid.requestId; - }); +function parseRTBResponse(serverResponse, bidderRequest) { + const isInvalidValidResp = !serverResponse || !serverResponse.bids || !serverResponse.bids.length; - if (serverBid.cpm !== 0 && requestId !== -1) { - const bid = createBid(serverBid, getMediaType(bidderRequest.bids[requestId])); + let bids = []; - bids.push(bid); - } - }); + if (isInvalidValidResp) { + let extMessage = serverResponse && serverResponse.ext && serverResponse.ext.message ? `: ${serverResponse.ext.message}` : ''; + let errorMessage = `in response for ${bidderRequest.bidderCode} adapter ${extMessage}`; + + utils.logError(errorMessage); return bids; } -}; + + serverResponse.bids.forEach(serverBid => { + const requestId = findIndex(bidderRequest.bids, (bidRequest) => { + return bidRequest.bidId === serverBid.requestId; + }); + + if (serverBid.cpm !== 0 && requestId !== -1) { + const bid = createBid(serverBid, getMediaType(bidderRequest.bids[requestId])); + + bids.push(bid); + } + }); + + return bids; +} + +function bidToTag(bidRequests) { + let tag = { + domain: utils.getTopWindowLocation().hostname + }; + + for (let i = 0, length = bidRequests.length; i < length; i++) { + Object.assign(tag, prepareRTBRequestParams(i, bidRequests[i])); + } + + return tag; +} /** * Parse mediaType + * @param _index {number} * @param bid {object} * @returns {object} */ -function prepareRTBRequestParams(bid) { +function prepareRTBRequestParams(_index, bid) { const mediaType = utils.deepAccess(bid, 'mediaTypes.video') ? VIDEO : DISPLAY; + const index = !_index ? '' : `${_index + 1}`; return { - domain: utils.getTopWindowLocation().hostname, - callbackId: bid.bidId, - aid: bid.params.aid, - ad_type: mediaType, - sizes: utils.parseSizesInput(bid.sizes).join() + ['callbackId' + index]: bid.bidId, + ['aid' + index]: bid.params.aid, + ['ad_type' + index]: mediaType, + ['sizes' + index]: utils.parseSizesInput(bid.sizes).join() }; } @@ -146,7 +172,7 @@ function createBid(bidResponse, mediaType) { function newRenderer(requestId) { const renderer = Renderer.install({ id: requestId, - url: '//player.vertamedia.com/outstream-unit/2.01/outstream.min.js', + url: OUTSTREAM_SRC, loaded: false }); diff --git a/test/spec/modules/vertamediaBidAdapter_spec.js b/test/spec/modules/vertamediaBidAdapter_spec.js index 32353b0097e..271f1f2d04a 100644 --- a/test/spec/modules/vertamediaBidAdapter_spec.js +++ b/test/spec/modules/vertamediaBidAdapter_spec.js @@ -125,17 +125,17 @@ describe('vertamediaBidAdapter', () => { const videoRequest = spec.buildRequests(videoBidRequests, {}); it('sends bid request to ENDPOINT via GET', () => { - expect(videoRequest[0].method).to.equal('GET'); - expect(displayRequest[0].method).to.equal('GET'); + expect(videoRequest.method).to.equal('GET'); + expect(displayRequest.method).to.equal('GET'); }); it('sends bid request to correct ENDPOINT', () => { - expect(videoRequest[0].url).to.equal(ENDPOINT); - expect(displayRequest[0].url).to.equal(ENDPOINT); + expect(videoRequest.url).to.equal(ENDPOINT); + expect(displayRequest.url).to.equal(ENDPOINT); }); it('sends correct video bid parameters', () => { - const bid = Object.assign({}, videoRequest[0].data); + const bid = Object.assign({}, videoRequest.data); delete bid.domain; const eq = { @@ -149,7 +149,7 @@ describe('vertamediaBidAdapter', () => { }); it('sends correct display bid parameters', () => { - const bid = Object.assign({}, displayRequest[0].data); + const bid = Object.assign({}, displayRequest.data); delete bid.domain; const eq = { From 9d557196ca28d6d587313baee969c55f879aa9bf Mon Sep 17 00:00:00 2001 From: Mohit Patil <13765991+mohit546@users.noreply.github.com> Date: Sat, 24 Feb 2018 05:13:35 +0530 Subject: [PATCH 10/10] Update Vertoz adapter for Prebid 1.0 (#2104) --- modules/vertozBidAdapter.js | 86 ++++++++++++++++ modules/vertozBidAdapter.md | 31 ++++++ test/spec/modules/vertozBidAdapter_spec.js | 112 +++++++++++++++++++++ 3 files changed, 229 insertions(+) create mode 100644 modules/vertozBidAdapter.js create mode 100644 modules/vertozBidAdapter.md create mode 100644 test/spec/modules/vertozBidAdapter_spec.js diff --git a/modules/vertozBidAdapter.js b/modules/vertozBidAdapter.js new file mode 100644 index 00000000000..f3727714454 --- /dev/null +++ b/modules/vertozBidAdapter.js @@ -0,0 +1,86 @@ +import * as utils from 'src/utils'; +import { registerBidder } from 'src/adapters/bidderFactory'; +const BIDDER_CODE = 'vertoz'; +const BASE_URI = '//hb.vrtzads.com/vzhbidder/bid?'; + +export const spec = { + code: BIDDER_CODE, + /** + * 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) { + return !!(bid.params.placementId); + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(bidRequestsArr) { + var bidRequests = bidRequestsArr || []; + return bidRequests.map(bid => { + let slotBidId = utils.getValue(bid, 'bidId'); + let cb = Math.round(new Date().getTime() / 1000); + let vzEndPoint = BASE_URI; + let reqParams = bid.params || {}; + let placementId = utils.getValue(reqParams, 'placementId'); + let cpm = utils.getValue(reqParams, 'cpmFloor'); + + if (utils.isEmptyStr(placementId)) { + utils.logError('missing params:', BIDDER_CODE, 'Enter valid vzPlacementId'); + return; + } + + let reqSrc = utils.getTopWindowLocation().href; + var vzReq = { + _vzPlacementId: placementId, + _rqsrc: reqSrc, + _cb: cb, + _slotBidId: slotBidId, + _cpm: cpm, + _cbn: '$$PREBID_GLOBAL$$' + }; + + let queryParamValue = encodeURIComponent(JSON.stringify(vzReq)); + + return { + method: 'POST', + data: {q: queryParamValue}, + url: vzEndPoint + }; + }) + }, + /** + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse) { + var bidRespObj = serverResponse.body; + const bidResponses = []; + + if (bidRespObj.cpm) { + const bidResponse = { + requestId: bidRespObj.slotBidId, + cpm: Number(bidRespObj.cpm), + width: Number(bidRespObj.adWidth), + height: Number(bidRespObj.adHeight), + netRevenue: true, + mediaType: 'banner', + currency: 'USD', + dealId: null, + creativeId: null, + ttl: 300, + ad: bidRespObj.ad + utils.createTrackPixelHtml(decodeURIComponent(bidRespObj.nurl)) + }; + bidResponses.push(bidResponse); + } + return bidResponses; + } +} +registerBidder(spec); diff --git a/modules/vertozBidAdapter.md b/modules/vertozBidAdapter.md new file mode 100644 index 00000000000..100492da58b --- /dev/null +++ b/modules/vertozBidAdapter.md @@ -0,0 +1,31 @@ +# Overview + +``` +Module Name: Vertoz Bidder Adapter +Module Type: Bidder Adapter +Maintainer: prebid-team@vertoz.com +``` + +# Description + +Connects to Vertoz exchange for bids. +Vertoz Bidder adapter supports Banner ads. +Use bidder code ```vertoz``` for all Vertoz traffic. + +# Test Parameters +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + sizes: [[300, 250], [300,600]], // a display size(s) + bids: [{ + bidder: 'vertoz', + params: { + placementId: 'VZ-HB-B784382V6C6G3C' + } + }] + }, +]; +``` + diff --git a/test/spec/modules/vertozBidAdapter_spec.js b/test/spec/modules/vertozBidAdapter_spec.js new file mode 100644 index 00000000000..a84fc4847f5 --- /dev/null +++ b/test/spec/modules/vertozBidAdapter_spec.js @@ -0,0 +1,112 @@ +import { expect } from 'chai'; +import { spec } from 'modules/vertozBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +const BASE_URI = '//hb.vrtzads.com/vzhbidder/bid?'; + +describe('VertozAdapter', () => { + const adapter = newBidder(spec); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', () => { + let bid = { + 'bidder': 'vertoz', + 'params': { + 'placementId': 'VZ-HB-B784382V6C6G3C' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'placementId': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + let bidRequests = [ + { + 'bidder': 'vertoz', + 'params': { + 'placementId': '10433394' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + + it('sends bid request to ENDPOINT via POST', () => { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.url).to.equal(BASE_URI); + expect(request.method).to.equal('POST'); + }); + }); + + describe('interpretResponse', () => { + let response = { + 'vzhPlacementId': 'VZ-HB-B784382V6C6G3C', + 'bid': '76021e56-adaf-4114-b68d-ccacd1b3e551_1', + 'adWidth': '300', + 'adHeight': '250', + 'cpm': '0.16312590000000002', + 'ad': '', + 'slotBidId': '44b3fcfd24aa93', + 'nurl': '', + 'statusText': 'Vertoz:Success' + }; + + it('should get correct bid response', () => { + let expectedResponse = [ + { + 'requestId': '44b3fcfd24aa93', + 'cpm': 0.16312590000000002, + 'width': 300, + 'height': 250, + 'netRevenue': true, + 'mediaType': 'banner', + 'currency': 'USD', + 'dealId': null, + 'creativeId': null, + 'ttl': 300, + 'ad': '' + } + ]; + let bidderRequest; + let result = spec.interpretResponse({body: response}); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + expect(result[0].cpm).to.not.equal(null); + }); + + it('handles nobid responses', () => { + let response = { + 'vzhPlacementId': 'VZ-HB-I617046VBGE3EH', + 'slotBidId': 'f00412ac86b79', + 'statusText': 'NO_BIDS' + }; + let bidderRequest; + + let result = spec.interpretResponse({body: response}); + expect(result.length).to.equal(0); + }); + }); +});