Skip to content

Commit

Permalink
TargetVideo Bid Adapter: add new bid adapter (#7718)
Browse files Browse the repository at this point in the history
* TargetVideo bid adapter

* TargetVideo bid adapter

* TargetVideo bid adapter

* TargetVideo Bid Adapter update

* TargetVideo Bid Adapter implementing requested changes
  • Loading branch information
grajzer authored Nov 29, 2021
1 parent 960215d commit 4fd5009
Show file tree
Hide file tree
Showing 4 changed files with 317 additions and 1 deletion.
1 change: 0 additions & 1 deletion modules/appnexusBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ export const spec = {
{ code: 'districtm', gvlid: 144 },
{ code: 'adasta' },
{ code: 'beintoo', gvlid: 618 },
{ code: 'targetVideo' },
],
supportedMediaTypes: [BANNER, VIDEO, NATIVE],

Expand Down
187 changes: 187 additions & 0 deletions modules/targetVideoBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import find from 'core-js-pure/features/array/find.js';
import { getBidRequest } from '../src/utils.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';

const SOURCE = 'pbjs';
const BIDDER_CODE = 'targetVideo';
const ENDPOINT_URL = 'https://ib.adnxs.com/ut/v3/prebid';
const MARGIN = 1.35;

export const spec = {

code: BIDDER_CODE,
supportedMediaTypes: [BANNER],

/**
* Determines whether or not the given bid request is valid.
*
* @param {object} 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.placementId);
},

/**
* Make a server request from the list of BidRequests.
*
* @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server.
* @return ServerRequest Info describing the request to the server.
*/
buildRequests: function(bidRequests, bidderRequest) {
const tags = bidRequests.map(createVideoTag);
const schain = bidRequests[0].schain;
const payload = {
tags: tags,
sdk: {
source: SOURCE,
version: '$prebid.version$'
},
schain: schain
};
return formatRequest(payload, bidderRequest);
},

/**
* 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, { bidderRequest }) {
serverResponse = serverResponse.body;
const bids = [];

if (serverResponse.tags) {
serverResponse.tags.forEach(serverBid => {
const rtbBid = getRtbBid(serverBid);
if (rtbBid && rtbBid.cpm !== 0 && rtbBid.ad_type == VIDEO) {
bids.push(newBid(serverBid, rtbBid, bidderRequest));
}
});
}

return bids;
}

}

function getSizes(request) {
let sizes = request.sizes;
if (!sizes && request.mediaTypes && request.mediaTypes.banner && request.mediaTypes.banner.sizes) {
sizes = request.mediaTypes.banner.sizes;
}
if (Array.isArray(sizes) && !Array.isArray(sizes[0])) {
sizes = [sizes[0], sizes[1]];
}
if (!Array.isArray(sizes) || !Array.isArray(sizes[0])) {
sizes = [[0, 0]];
}

return sizes;
}

function formatRequest(payload, bidderRequest) {
const options = {
withCredentials: true
};
const request = {
method: 'POST',
url: ENDPOINT_URL,
data: JSON.stringify(payload),
bidderRequest,
options
};

return request;
}

/**
* Create video auction.
*
* @param {*} serverResponse A successful response from the server.
* @return {Bid[]} An array of bids which were nested inside the server.
*/
function createVideoTag(bid) {
const tag = {};
tag.id = parseInt(bid.params.placementId, 10);
tag.gpid = 'targetVideo';
tag.sizes = getSizes(bid);
tag.primary_size = tag.sizes[0];
tag.ad_types = [VIDEO];
tag.uuid = bid.bidId;
tag.allow_smaller_sizes = false;
tag.use_pmt_rule = false
tag.prebid = true;
tag.disable_psa = true;
tag.hb_source = 1;
tag.require_asset_url = true;
tag.video = {
playback_method: 2,
skippable: true
};

return tag;
}

/**
* Unpack the Server's Bid into a Prebid-compatible one.
* @param serverBid
* @param rtbBid
* @param bidderRequest
* @return Bid
*/
function newBid(serverBid, rtbBid, bidderRequest) {
const bidRequest = getBidRequest(serverBid.uuid, [bidderRequest]);
const sizes = getSizes(bidRequest);
const bid = {
requestId: serverBid.uuid,
cpm: rtbBid.cpm * MARGIN,
creativeId: rtbBid.creative_id,
dealId: rtbBid.deal_id,
currency: 'USD',
netRevenue: true,
width: sizes[0][0],
height: sizes[0][1],
ttl: 300,
adUnitCode: bidRequest.adUnitCode,
appnexus: {
buyerMemberId: rtbBid.buyer_member_id,
dealPriority: rtbBid.deal_priority,
dealCode: rtbBid.deal_code
}
};

if (rtbBid.rtb.video) {
Object.assign(bid, {
vastImpUrl: rtbBid.notify_url,
ad: getBannerHtml(rtbBid.notify_url + '&redir=' + encodeURIComponent(rtbBid.rtb.video.asset_url)),
ttl: 3600
});
}

return bid;
}

function getRtbBid(tag) {
return tag && tag.ads && tag.ads.length && find(tag.ads, ad => ad.rtb);
}

function getBannerHtml(vastUrl) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>html, body {width: 100%; height: 100%; margin: 0;}</style>
</head>
<body>
<div id="targetVideoPlayer"></div>
<script src="https://player.target-video.com/custom/targetvideo-banner.js"></script>
<script>initPlayer("${vastUrl}");</script>
</body>
</html>`;
}

registerBidder(spec);
34 changes: 34 additions & 0 deletions modules/targetVideoBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Overview

```
Module Name: Target Video Bid Adapter
Module Type: Bidder Adapter
Maintainer: [email protected]
```

# Description

Connects to Appnexus exchange for bids.

TargetVideo bid adapter supports Banner.

# Test Parameters
```
var adUnits = [
// Banner adUnit
{
code: 'banner-div',
mediaTypes: {
banner: {
sizes: [[640, 480], [300, 250]],
}
},
bids: [{
bidder: 'targetVideo',
params: {
placementId: 13232361
}
}]
}
];
```
96 changes: 96 additions & 0 deletions test/spec/modules/targetVideoBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { spec } from '../../../modules/targetVideoBidAdapter.js'

describe('TargetVideo Bid Adapter', function() {
const bannerRequest = [{
bidder: 'targetVideo',
mediaTypes: {
banner: {
sizes: [[300, 250]],
}
},
params: {
placementId: 12345,
}
}];

it('Test the bid validation function', function() {
const validBid = spec.isBidRequestValid(bannerRequest[0]);
const invalidBid = spec.isBidRequestValid(null);

expect(validBid).to.be.true;
expect(invalidBid).to.be.false;
});

it('Test the request processing function', function () {
const request = spec.buildRequests(bannerRequest, bannerRequest[0]);
expect(request).to.not.be.empty;

const payload = JSON.parse(request.data);
expect(payload).to.not.be.empty;
expect(payload.sdk).to.deep.equal({
source: 'pbjs',
version: '$prebid.version$'
});
expect(payload.tags[0].id).to.equal(12345);
expect(payload.tags[0].gpid).to.equal('targetVideo');
expect(payload.tags[0].ad_types[0]).to.equal('video');
});

it('Handle nobid responses', function () {
const responseBody = {
'version': '0.0.1',
'tags': [{
'uuid': '84ab500420319d',
'tag_id': 5976557,
'auction_id': '297492697822162468',
'nobid': true
}]
};
const bidderRequest = null;

const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest});
expect(bidResponse.length).to.equal(0);
});

it('Test the response parsing function', function () {
const responseBody = {
'tags': [{
'uuid': '84ab500420319d',
'ads': [{
'ad_type': 'video',
'cpm': 0.500000,
'notify_url': 'https://www.target-video.com/',
'rtb': {
'video': {
'player_width': 640,
'player_height': 360,
'asset_url': 'https://www.target-video.com/'
}
}
}]
}]
};
const bidderRequest = {
bids: [{
bidId: '84ab500420319d',
adUnitCode: 'code',
mediaTypes: {
banner: {
sizes: [[300, 250]]
}
}
}]
};

const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest});
expect(bidResponse).to.not.be.empty;

const bid = bidResponse[0];
expect(bid).to.not.be.empty;
expect(bid.cpm).to.equal(0.675);
expect(bid.width).to.equal(300);
expect(bid.height).to.equal(250);
expect(bid.ad).to.include('<script src="https://player.target-video.com/custom/targetvideo-banner.js"></script>')
expect(bid.ad).to.include('initPlayer')
});
});

0 comments on commit 4fd5009

Please sign in to comment.