Skip to content

Commit

Permalink
Viqeo Bid Adapter: initial adapter release (prebid#8920)
Browse files Browse the repository at this point in the history
* add viqeo prebid adapter

* added bid params to docs

* updated to Outstream

* updated to Outstream (tests)
  • Loading branch information
kinoshnik2070 authored Nov 16, 2022
1 parent f5e6c61 commit c3f789b
Show file tree
Hide file tree
Showing 3 changed files with 360 additions and 0 deletions.
180 changes: 180 additions & 0 deletions modules/viqeoBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {logError, logInfo, _each, mergeDeep, isFn, isNumber, isPlainObject} from '../src/utils.js'
import {VIDEO} from '../src/mediaTypes.js';
import {Renderer} from '../src/Renderer.js';

const BIDDER_CODE = 'viqeo';
const DEFAULT_MIMES = ['application/javascript'];
const VIQEO_ENDPOINT = 'https://ads.betweendigital.com/openrtb_bid';
const RENDERER_URL = 'https://cdn.viqeo.tv/js/vq_starter.js';
const DEFAULT_CURRENCY = 'USD';
const DEFAULT_SSPID = 44697;

function getBidFloor(bid) {
const {floor, currency} = bid.params;
const curr = currency || DEFAULT_CURRENCY;
if (!isFn(bid.getFloor)) {
return {floor: isNumber(floor) ? floor : 0, currency: curr};
}
const floorInfo = bid.getFloor({currency: curr, mediaType: VIDEO, size: '*'});
if (isPlainObject(floorInfo) && isNumber(floorInfo.floor) && floorInfo.currency === curr) {
return floorInfo;
}
return {floor: floor || 0, currency: currency || DEFAULT_CURRENCY};
}

function getVideoTargetingParams({mediaTypes: {video}}) {
const result = {};
Object.keys(Object(video))
.forEach(key => {
if (key === 'playerSize') {
result.w = video.playerSize[0][0];
result.h = video.playerSize[0][1];
} else if (key !== 'context') {
result[key] = video[key];
}
})
return result;
}

/**
* @type {BidderSpec}
*/
export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [VIDEO],
/**
* @param {BidRequest} bidRequest The bid params to validate.
* @return boolean True if this is a valid bid, and false otherwise.
*/
isBidRequestValid: ({params}) => {
if (!params) {
logError('failed validation: params not declared');
return false;
}
if (!params.user && !params.user?.buyeruid) {
logError('failed validation: user.buyeruid not declared');
return false;
}
if (!params.playerOptions) {
logError('failed validation: playerOptions not declared');
return false;
}
const {profileId, videoId, playerId} = params.playerOptions;
if (!profileId) {
logError('failed validation: profileId not declared');
return false;
}
if (!videoId && !playerId) {
logError('failed validation: videoId or playerId not declared');
return false;
}
return true;
},
/**
* @param validBidRequests {BidRequest[]}
* @returns {ServerRequest[]}
*/
buildRequests: (validBidRequests) => {
logInfo('validBidRequests', validBidRequests);
const bidRequests = [];
_each(validBidRequests, (bid, i) => {
const {
params: {test, sspId, endpointUrl},
mediaTypes: {video},
} = bid;
const ortb2 = bid.ortb2 || {};
const user = bid.params.user || {};
const device = bid.params.device || {};
const site = bid.params.site || {};
const w = window;
const floorInfo = getBidFloor(bid);
const data = {
id: bid.bidId,
test,
imp: [{
id: `${i}`,
tagid: bid.adUnitCode,
video: {
...getVideoTargetingParams(bid),
mimes: video.mimes || DEFAULT_MIMES,
},
bidfloor: floorInfo.floor,
bidfloorcur: floorInfo.currency,
secure: 1
}],
site: test === 1 ? {
page: 'https://viqeo.tv',
domain: 'viqeo.tv'
} : mergeDeep({
domain: w.location.hostname,
page: w.location.href
}, ortb2.site, site),
device: mergeDeep({
w: w.screen.width,
h: w.screen.height,
ua: w.navigator.userAgent,
}, ortb2.device, device),
user: mergeDeep({...user}, ortb2.user),
app: bid.params.app,
};
bidRequests.push({
url: endpointUrl || `${VIQEO_ENDPOINT}/?sspId=${sspId || DEFAULT_SSPID}`,
method: 'POST',
data,
bids: validBidRequests,
});
});
return bidRequests;
},
/**
* @param {ServerResponse} serverResponse
* @param {BidRequest} bidRequests
* @return {Bid[]}
*/
interpretResponse: (serverResponse, bidRequests) => {
logInfo('serverResponse', serverResponse);
const bidResponses = [];
if (!serverResponse || !serverResponse.body) {
logError('empty response');
return [];
}
try {
const {id, seatbid, cur} = serverResponse.body;
_each(seatbid, (sb) => {
const {bid} = sb;
_each(bid, (b) => {
const bidRequest = bidRequests.bids.find(({bidId}) => bidId === id);
const renderer = Renderer.install({
url: bidRequest?.params?.renderUrl || RENDERER_URL,
});
renderer.setRender((bid) => {
if (window.VIQEO) {
window.VIQEO.renderPrebid(bid);
} else {
logError('failed get window.VIQEO');
}
});
bidResponses.push({
requestId: id,
currency: cur,
cpm: b.price,
ttl: b.exp,
netRevenue: true,
creativeId: b.cid,
width: b.w || bidRequest?.mediaTypes[VIDEO].playerSize[0][0],
height: b.h || bidRequest?.mediaTypes[VIDEO].playerSize[0][1],
vastXml: b.adm,
vastUrl: b.nurl,
mediaType: VIDEO,
renderer,
})
})
});
} catch (error) {
logError(error);
}
return bidResponses;
},
}
registerBidder(spec);
56 changes: 56 additions & 0 deletions modules/viqeoBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Overview

**Module Name**: Viqeo Bidder Adapter
**Module Type**: Bidder Adapter
**Maintainer**: [email protected]

# Description

Viqeo Bidder Adapter for Prebid.js. About: https://viqeo.tv/

### Bid params

{: .table .table-bordered .table-striped }
| Name | Scope | Description | Example | Type |
|-----------------------------|----------|----------------------------------------------------------------------------------------------------------------------------|--------------------------|-----------|
| `user` | required | The object containing user data (See OpenRTB spec) | `user: {}` | `object` |
| `user.buyeruid` | required | User id | `"12345"` | `string` |
| `playerOptions` | required | The object containing Viqeo player options | `playerOptions: {}` | `object` |
| `playerOptions.profileId` | required | Viqeo profile id | `1382` | `number` |
| `playerOptions.videId` | optional | Viqeo video id | `"ed584da454c7205ca7e4"` | `string` |
| `playerOptions.playerId` | optional | Viqeo player id | `1` | `number` |
| `device` | optional | The object containing device data (See OpenRTB spec) | `device: {}` | `object` |
| `site` | optional | The object containing site data (See OpenRTB spec) | `site: {}` | `object` |
| `app` | optional | The object containing app data (See OpenRTB spec) | `app: {}` | `object` |
| `floor` | optional | Bid floor price | `0.5` | `number` |
| `currency` | optional | 3-letter ISO 4217 code defining the currency of the bid. | `EUR` | `string` |
| `test` | optional | Flag which will induce a sample bid response when true; only set to true for testing purposes (1 = true, 0 = false) | `1` | `integer` |
| `sspId` | optional | For debug, request id | `1` | `number` |
| `renderUrl` | optional | For debug, script player url | `"https://viqeo.tv"` | `string` |
| `endpointUrl` | optional | For debug, api endpoint | `"https://viqeo.tv"` | `string` |

# Test Parameters
```
var adUnits = [{
code: 'your-slot', // use exactly the same code as your slot div id.
mediaTypes: {
video: {
context: 'outstream',
playerSize: [640, 480]
}
},
bids: [{
bidder: 'viqeo',
params: {
user: {
buyeruid: '1',
},
playerOptions: {
videoId: 'ed584da454c7205ca7e4',
profileId: 1382,
},
test: 1,
}
}]
}];
```
124 changes: 124 additions & 0 deletions test/spec/modules/viqeoBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import {expect} from 'chai';
import {spec} from 'modules/viqeoBidAdapter';

describe('viqeoBidAdapter', function () {
it('minimal params', function () {
expect(spec.isBidRequestValid({
bidder: 'viqeo',
params: {
user: {
buyeruid: '1',
},
playerOptions: {
videoId: 'ed584da454c7205ca7e4',
profileId: 1382,
},
}})).to.equal(true);
});
it('minimal params no playerOptions', function () {
expect(spec.isBidRequestValid({
bidder: 'viqeo',
params: {
currency: 'EUR',
}})).to.equal(false);
});
it('build request check data', function () {
const bidRequestData = [{
bidId: 'id1',
bidder: 'viqeo',
params: {
user: {
buyeruid: '1',
},
currency: 'EUR',
floor: 0.5,
playerOptions: {
videoId: 'ed584da454c7205ca7e4',
profileId: 1382,
},
},
mediaTypes: {
video: { playerSize: [[240, 400]] }
},
}];
const request = spec.buildRequests(bidRequestData);
const requestData = request[0].data;
expect(requestData.id).to.equal('id1')
expect(requestData.imp[0].bidfloorcur).to.equal('EUR');
expect(requestData.imp[0].bidfloor).to.equal(0.5);
expect(requestData.imp[0].video.w).to.equal(240);
expect(requestData.imp[0].video.h).to.equal(400);
expect(requestData.user.buyeruid).to.equal('1');
});
it('build request check url', function () {
const bidRequestData = [{
bidder: 'viqeo',
params: {
playerOptions: {
videoId: 'ed584da454c7205ca7e4',
profileId: 1382,
},
sspId: 42,
},
mediaTypes: {
video: { playerSize: [[240, 400]] }
},
}];
const request = spec.buildRequests(bidRequestData);
expect(request[0].url).to.equal('https://ads.betweendigital.com/openrtb_bid/?sspId=42')
});
it('response_params common case', function () {
const bidRequestData = {
bids: [{
bidId: 'id1',
params: {},
mediaTypes: {
video: { playerSize: [[240, 400]] }
},
}],
};
const serverResponse = {
body: {
id: 'id1',
cur: 'EUR',
seatbid: [{
bid: [{
cpm: 0.5,
ttl: 3600,
netRevenue: true,
creativeId: 'test1',
adm: '',
}],
}],
}
};
const bids = spec.interpretResponse(serverResponse, bidRequestData);
expect(bids).to.have.lengthOf(1);
});
it('should set flooPrice to getFloor.floor value if it is greater than params.floor', function() {
const bidRequestData = [{
bidId: 'id1',
bidder: 'viqeo',
params: {
currency: 'EUR',
floor: 0.5,
playerOptions: {
videoId: 'ed584da454c7205ca7e4',
profileId: 1382,
},
},
mediaTypes: {
video: { playerSize: [[240, 400]] }
},
getFloor: () => {
return {
currency: 'EUR',
floor: 3.32
}
},
}];
const request = spec.buildRequests(bidRequestData);
const requestData = request[0].data;
expect(requestData.imp[0].bidfloor).to.equal(3.32)
});
});

0 comments on commit c3f789b

Please sign in to comment.