diff --git a/modules/getintentBidAdapter.js b/modules/getintentBidAdapter.js index 72f1c1a0073..f677b107529 100644 --- a/modules/getintentBidAdapter.js +++ b/modules/getintentBidAdapter.js @@ -1,78 +1,143 @@ -import { STATUS } from 'src/constants'; -import adaptermanager from 'src/adaptermanager'; +import { registerBidder } from 'src/adapters/bidderFactory'; -var bidfactory = require('src/bidfactory.js'); -var bidmanager = require('src/bidmanager.js'); -var adloader = require('src/adloader.js'); +const BIDDER_CODE = 'getintent'; +const IS_NET_REVENUE = true; +const BID_HOST = 'px.adhigh.net'; +const BID_BANNER_PATH = '/rtb/direct_banner'; +const BID_VIDEO_PATH = '/rtb/direct_vast'; +const BID_RESPONSE_TTL_SEC = 360; +const VIDEO_PROPERTIES = [ + 'protocols', 'mimes', 'min_dur', 'max_dur', 'min_btr', 'max_btr', 'vi_format', 'api', 'skippable' +]; +const OPTIONAL_PROPERTIES = [ + 'cur', 'floor' +]; -var GetIntentAdapter = function GetIntentAdapter() { - var headerBiddingStaticJS = window.location.protocol + '//cdn.adhigh.net/adserver/hb.js'; +export const spec = { + code: BIDDER_CODE, + aliases: ['getintentAdapter'], + supportedMediaTypes: ['video', 'banner'], - function _callBids(params) { - if (typeof window.gi_hb === 'undefined') { - adloader.loadScript(headerBiddingStaticJS, function() { - bid(params); - }, true); - } else { - bid(params); + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid to validate. + * @return {boolean} True if this is a valid bid, and false otherwise. + * */ + isBidRequestValid: function(bid) { + return !!(bid && bid.params && bid.params.pid && bid.params.tid); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} bidRequests - an array of bids. + * @return ServerRequest[] + */ + buildRequests: function(bidRequests) { + return bidRequests.map(bidRequest => { + let giBidRequest = buildGiBidRequest(bidRequest); + return { + method: 'GET', + url: buildUrl(giBidRequest), + data: giBidRequest, + }; + }); + }, + + /** + * Callback for bids, after the call to DSP completes. + * Parse the response from the server into a list of bids. + * + * @param {object} serverResponse A response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse) { + let responseBody = serverResponse.body; + const bids = []; + if (responseBody && responseBody.no_bid !== 1) { + let size = parseSize(responseBody.size); + let bid = { + requestId: responseBody.bid_id, + ttl: BID_RESPONSE_TTL_SEC, + netRevenue: IS_NET_REVENUE, + currency: responseBody.currency, + creativeId: responseBody.creative_id, + cpm: responseBody.cpm, + width: size[0], + height: size[1] + }; + if (responseBody.vast_url) { + bid.mediaType = 'video'; + bid.vastUrl = responseBody.vast_url; + } else { + bid.mediaType = 'banner'; + bid.ad = responseBody.ad; + } + bids.push(bid); } + return bids; } - function addOptional(params, request, props) { - for (var i = 0; i < props.length; i++) { - if (params.hasOwnProperty(props[i])) { - request[props[i]] = params[props[i]]; +} + +function buildUrl(bid) { + return '//' + BID_HOST + (bid.is_video ? BID_VIDEO_PATH : BID_BANNER_PATH); +} + +/** + * Builds GI bid request from BidRequest. + * + * @param {BidRequest} bidRequest. + * @return {object} GI bid request. + * */ +function buildGiBidRequest(bidRequest) { + let giBidRequest = { + bid_id: bidRequest.bidId, + pid: bidRequest.params.pid, // required + tid: bidRequest.params.tid, // required + known: bidRequest.params.known || 1, + is_video: bidRequest.mediaType === 'video', + resp_type: 'JSON' + }; + if (bidRequest.sizes) { + giBidRequest.size = produceSize(bidRequest.sizes); + } + addVideo(bidRequest.params.video, giBidRequest); + addOptional(bidRequest.params, giBidRequest, OPTIONAL_PROPERTIES); + return giBidRequest; +} + +function addVideo(video, giBidRequest) { + if (giBidRequest.is_video && video) { + for (let i = 0, l = VIDEO_PROPERTIES.length; i < l; i++) { + let key = VIDEO_PROPERTIES[i]; + if (video.hasOwnProperty(key)) { + giBidRequest[key] = Array.isArray(video[key]) ? video[key].join(',') : video[key]; } } } +} - function bid(params) { - var bids = params.bids || []; - for (var i = 0; i < bids.length; i++) { - var bidRequest = bids[i]; - var request = { - pid: bidRequest.params.pid, // required - tid: bidRequest.params.tid, // required - known: bidRequest.params.known || 1, - is_video: bidRequest.mediaType === 'video', - video: bidRequest.params.video || {}, - size: bidRequest.sizes[0].join('x'), - }; - addOptional(bidRequest.params, request, ['cur', 'floor']); - (function (r, br) { - window.gi_hb.makeBid(r, function(bidResponse) { - if (bidResponse.no_bid === 1) { - var nobid = bidfactory.createBid(STATUS.NO_BID); - nobid.bidderCode = br.bidder; - bidmanager.addBidResponse(br.placementCode, nobid); - } else { - var bid = bidfactory.createBid(STATUS.GOOD); - var size = bidResponse.size.split('x'); - bid.bidderCode = br.bidder; - bid.cpm = bidResponse.cpm; - bid.width = size[0]; - bid.height = size[1]; - if (br.mediaType === 'video') { - bid.vastUrl = bidResponse.vast_url; - bid.descriptionUrl = bidResponse.vast_url; - bid.mediaType = 'video'; - } else { - bid.ad = bidResponse.ad; - } - bidmanager.addBidResponse(br.placementCode, bid); - } - }); - })(request, bidRequest); +function addOptional(params, request, props) { + for (let i = 0; i < props.length; i++) { + if (params.hasOwnProperty(props[i])) { + request[props[i]] = params[props[i]]; } } +} - return { - callBids: _callBids - }; -}; +function parseSize(s) { + return s.split('x').map(Number); +} -adaptermanager.registerBidAdapter(new GetIntentAdapter(), 'getintent', { - supportedMediaTypes: ['video'] -}); +function produceSize(sizes) { + // TODO: add support for multiple sizes + if (Array.isArray(sizes[0])) { + return sizes[0].join('x'); + } else { + return sizes.join('x'); + } +} -module.exports = GetIntentAdapter; +registerBidder(spec); diff --git a/modules/getintentBidAdapter.md b/modules/getintentBidAdapter.md new file mode 100644 index 00000000000..7f9b38f6b22 --- /dev/null +++ b/modules/getintentBidAdapter.md @@ -0,0 +1,48 @@ +# Overview + +``` +Module Name: GetIntent Bidder Adapter +Module Type: Bidder Adapter +Maintainer: server-dev@getintent.com +``` + +# Description + +Module that connects to GetIntent's demand sources. +Banner and Video formats are supported. + +# Required parameters +* ```pid``` for Publisher ID +* ```tid``` for Tag ID. + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-ad', + sizes: [[300, 250]], + bids: [ + { + bidder: "getintent", + params: { + pid: "7", + tid: "test01" + } + } + ] + },{ + code: 'test-video-ad', + sizes: [[300, 250]], + bids: [ + { + bidder: "getintent", + params: { + pid: "7", + tid: "test01" + }, + mediaType: "video" + } + ] + } + ]; +``` diff --git a/test/spec/modules/getintentBidAdapter_spec.js b/test/spec/modules/getintentBidAdapter_spec.js index e66d2138eaf..1b76c4852b4 100644 --- a/test/spec/modules/getintentBidAdapter_spec.js +++ b/test/spec/modules/getintentBidAdapter_spec.js @@ -1,146 +1,131 @@ -import Adapter from '../../../modules/getintentBidAdapter'; -import bidManager from '../../../src/bidmanager'; -import {expect} from 'chai'; - -var assert = require('chai').assert; - -describe('getintent media adapter test', () => { - let adapter; - - window.gi_hb = { - makeBid: function(bidRequest, callback) { - var pid = bidRequest.pid; - var tid = bidRequest.tid; - - if (pid == 'p1' || pid == 'p2') { - callback({ - ad: `Ad Markup ${pid} ${tid}`, - cpm: 2.71, - size: `${bidRequest.size}` - }, bidRequest); - } else if (pid == 'p3') { - callback({ - no_bid: 1 - }, bidRequest); - } else if (pid == 'p4') { - callback({ - vast_url: `http://test.com?pid=${pid}&tid=${tid}`, - cpm: 2.88, - size: `${bidRequest.size}` - }, bidRequest); +import { expect } from 'chai' +import { spec } from 'modules/getintentBidAdapter' + +describe('GetIntent Adapter Tests:', () => { + const bidRequests = [{ + bidId: 'bid12345', + params: { + pid: 'p1000', + tid: 't1000' + }, + sizes: [[300, 250]] + }]; + const videoBidRequest = { + bidId: 'bid789', + params: { + pid: 'p1001', + tid: 't1001', + video: { + mimes: ['video/mp4', 'application/javascript'], + max_dur: 20, + api: [1, 2], + skippable: true } - } + }, + sizes: [300, 250], + mediaType: 'video' }; - function callOut() { - adapter.callBids({ - bidderCode: 'getintent', - bids: [ - { - bidder: 'getintent', - adUnitCode: 'test1', - sizes: [[320, 240]], - params: { - pid: 'p1', - tid: 't1', - cur: 'USD' - } - }, - { - bidder: 'getintent', - adUnitCode: 'test2', - sizes: [[720, 90]], - params: { - pid: 'p2', - tid: 't1', - cur: 'USD' - } - }, - { - bidder: 'getintent', - adUnitCode: 'test3', - sizes: [[400, 500]], - params: { - pid: 'p3', - tid: 't2', - cur: 'USD' - } - }, - { - bidder: 'getintent', - adUnitCode: 'test4', - mediaType: 'video', - sizes: [[480, 352]], - params: { - pid: 'p4', - tid: 't3', - cur: 'USD' - } - } - ] - }); - } - - beforeEach(() => { - adapter = new Adapter(); + it('Verify build request', () => { + const serverRequests = spec.buildRequests(bidRequests); + let serverRequest = serverRequests[0]; + expect(serverRequest.url).to.equal('//px.adhigh.net/rtb/direct_banner'); + expect(serverRequest.method).to.equal('GET'); + expect(serverRequest.data.bid_id).to.equal('bid12345'); + expect(serverRequest.data.pid).to.equal('p1000'); + expect(serverRequest.data.tid).to.equal('t1000'); + expect(serverRequest.data.size).to.equal('300x250'); + expect(serverRequest.data.is_video).to.equal(false); }); - afterEach(() => { + it('Verify build video request', () => { + const serverRequests = spec.buildRequests([videoBidRequest]); + let serverRequest = serverRequests[0]; + expect(serverRequest.url).to.equal('//px.adhigh.net/rtb/direct_vast'); + expect(serverRequest.method).to.equal('GET'); + expect(serverRequest.data.bid_id).to.equal('bid789'); + expect(serverRequest.data.pid).to.equal('p1001'); + expect(serverRequest.data.tid).to.equal('t1001'); + expect(serverRequest.data.size).to.equal('300x250'); + expect(serverRequest.data.is_video).to.equal(true); + expect(serverRequest.data.mimes).to.equal('video/mp4,application/javascript'); + expect(serverRequest.data.max_dur).to.equal(20); + expect(serverRequest.data.api).to.equal('1,2'); + expect(serverRequest.data.skippable).to.equal(true); }); - describe('adding bids to the manager', () => { - let firstBid; - let secondBid; - let thirdBid; - let videoBid; - - beforeEach(() => { - sinon.stub(bidManager, 'addBidResponse'); - callOut(); - firstBid = bidManager.addBidResponse.firstCall.args[1]; - secondBid = bidManager.addBidResponse.secondCall.args[1]; - thirdBid = bidManager.addBidResponse.thirdCall.args[1]; - videoBid = bidManager.addBidResponse.lastCall.args[1]; - }); - - afterEach(() => { - bidManager.addBidResponse.restore(); - }); - - it('was called four times', () => { - assert.strictEqual(bidManager.addBidResponse.callCount, 4); - }); + it('Verify parse response', () => { + const serverResponse = { + body: { + bid_id: 'bid12345', + cpm: 2.25, + currency: 'USD', + size: '300x250', + creative_id: '1000', + ad: 'Ad markup' + }, + headers: { + } + }; + const bids = spec.interpretResponse(serverResponse); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(2.25); + expect(bid.currency).to.equal('USD'); + expect(bid.creativeId).to.equal('1000'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.requestId).to.equal('bid12345'); + expect(bid.mediaType).to.equal('banner'); + expect(bid.ad).to.equal('Ad markup'); + }); - it('will respond to the first bid', () => { - expect(firstBid).to.have.property('ad', 'Ad Markup p1 t1'); - expect(firstBid).to.have.property('cpm', 2.71); - expect(firstBid).to.have.property('width', '320'); - expect(firstBid).to.have.property('height', '240'); - }); + it('Verify parse video response', () => { + const serverResponse = { + body: { + bid_id: 'bid789', + cpm: 3.25, + currency: 'USD', + size: '300x250', + creative_id: '2000', + vast_url: '//vast.xml/url' + }, + headers: { + } + }; + const bids = spec.interpretResponse(serverResponse); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.cpm).to.equal(3.25); + expect(bid.currency).to.equal('USD'); + expect(bid.creativeId).to.equal('2000'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.requestId).to.equal('bid789'); + expect(bid.mediaType).to.equal('video'); + expect(bid.vastUrl).to.equal('//vast.xml/url'); + }); - it('will respond to the second bid', () => { - expect(secondBid).to.have.property('ad', 'Ad Markup p2 t1'); - expect(secondBid).to.have.property('cpm', 2.71); - expect(secondBid).to.have.property('width', '720'); - expect(secondBid).to.have.property('height', '90'); - }); + it('Verify bidder code', () => { + expect(spec.code).to.equal('getintent'); + }); - it('wont respond to the third bid', () => { - expect(thirdBid).to.not.have.property('ad'); - expect(thirdBid).to.not.have.property('cpm'); - }); + it('Verify bidder aliases', () => { + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.equal('getintentAdapter'); + }); - it('will add the bidder code to the bid object', () => { - expect(firstBid).to.have.property('bidderCode', 'getintent'); - expect(secondBid).to.have.property('bidderCode', 'getintent'); - expect(thirdBid).to.have.property('bidderCode', 'getintent'); - }); + it('Verify supported media types', () => { + expect(spec.supportedMediaTypes).to.have.lengthOf(2); + expect(spec.supportedMediaTypes[0]).to.equal('video'); + expect(spec.supportedMediaTypes[1]).to.equal('banner'); + }); - it('will respond to the video bid', () => { - expect(videoBid).to.have.property('vastUrl', 'http://test.com?pid=p4&tid=t3'); - expect(videoBid).to.have.property('cpm', 2.88); - expect(videoBid).to.have.property('width', '480'); - expect(videoBid).to.have.property('height', '352'); - }); + it('Verify if bid request valid', () => { + expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true); + expect(spec.isBidRequestValid({})).to.equal(false); + expect(spec.isBidRequestValid({ params: {} })).to.equal(false); + expect(spec.isBidRequestValid({ params: { test: 123 } })).to.equal(false); + expect(spec.isBidRequestValid({ params: { pid: 111, tid: 222 } })).to.equal(true); }); });