forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Viqeo Bid Adapter: initial adapter release (prebid#8920)
* add viqeo prebid adapter * added bid params to docs * updated to Outstream * updated to Outstream (tests)
- Loading branch information
1 parent
f5e6c61
commit c3f789b
Showing
3 changed files
with
360 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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**: muravjovv1@gmail.com | ||
|
||
# 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, | ||
} | ||
}] | ||
}]; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}); | ||
}); |