Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Relaido Adapter #5101

Merged
merged 3 commits into from
Apr 13, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
296 changes: 296 additions & 0 deletions modules/relaidoBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
import * as utils from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { Renderer } from '../src/Renderer.js';
import { getStorageManager } from '../src/storageManager.js';

const BIDDER_CODE = 'relaido';
const BIDDER_DOMAIN = 'api.relaido.jp';
const ADAPTER_VERSION = '1.0.0';
const DEFAULT_TTL = 300;
const UUID_KEY = 'relaido_uuid';

const storage = getStorageManager();

function isBidRequestValid(bid) {
if (!utils.isSafariBrowser() && !hasUuid()) {
utils.logWarn('uuid is not found.');
return false;
}
if (!utils.deepAccess(bid, 'params.placementId')) {
utils.logWarn('placementId param is reqeuired.');
return false;
}
if (hasVideoMediaType(bid)) {
if (!isVideoValid(bid)) {
utils.logWarn('Invalid mediaType video.');
return false;
}
} else if (hasBannerMediaType(bid)) {
if (!isBannerValid(bid)) {
utils.logWarn('Invalid mediaType banner.');
return false;
}
} else {
utils.logWarn('Invalid mediaTypes input banner or video.');
return false;
}
return true;
}

function buildRequests(validBidRequests, bidderRequest) {
let bidRequests = [];

for (let i = 0; i < validBidRequests.length; i++) {
const bidRequest = validBidRequests[i];
const placementId = utils.getBidIdParameter('placementId', bidRequest.params);
const bidDomain = bidRequest.params.domain || BIDDER_DOMAIN;
const bidUrl = `https://${bidDomain}/vast/v1/out/bid/${placementId}`;
const uuid = getUuid();
const mediaType = getMediaType(bidRequest);

let payload = {
version: ADAPTER_VERSION,
ref: bidderRequest.refererInfo.referer,
timeout_ms: bidderRequest.timeout,
ad_unit_code: bidRequest.adUnitCode,
auction_id: bidRequest.auctionId,
bidder: bidRequest.bidder,
bidder_request_id: bidRequest.bidderRequestId,
bid_requests_count: bidRequest.bidRequestsCount,
bid_id: bidRequest.bidId,
transaction_id: bidRequest.transactionId,
media_type: mediaType,
uuid: uuid,
};

if (hasVideoMediaType(bidRequest)) {
const playerSize = utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize');
payload.width = playerSize[0][0];
payload.height = playerSize[0][1];
} else if (hasBannerMediaType(bidRequest)) {
const sizes = utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes');
payload.width = sizes[0][0];
payload.height = sizes[0][1];
}

bidRequests.push({
method: 'GET',
url: bidUrl,
data: payload,
options: {
withCredentials: true
},
bidId: bidRequest.bidId,
player: bidRequest.params.player,
width: payload.width,
height: payload.height,
mediaType: mediaType,
});
}

return bidRequests;
}

function interpretResponse(serverResponse, bidRequest) {
const bidResponses = [];
const body = serverResponse.body;
if (!body || body.status != 'ok') {
return [];
}

if (body.uuid) {
storage.setDataInLocalStorage(UUID_KEY, body.uuid);
}

const playerUrl = bidRequest.player || body.playerUrl;
const mediaType = bidRequest.mediaType || VIDEO;

let bidResponse = {
requestId: bidRequest.bidId,
width: bidRequest.width,
height: bidRequest.height,
cpm: body.price,
currency: body.currency,
creativeId: body.creativeId,
dealId: body.dealId || '',
ttl: body.ttl || DEFAULT_TTL,
netRevenue: true,
mediaType: mediaType,
};
if (mediaType === VIDEO) {
bidResponse.vastXml = body.vast;
bidResponse.renderer = newRenderer(bidRequest.bidId, playerUrl);
} else {
const playerTag = createPlayerTag(playerUrl);
const renderTag = createRenderTag(bidRequest.width, bidRequest.height, body.vast);
bidResponse.ad = `<div id="rop-prebid">${playerTag}${renderTag}</div>`;
}
bidResponses.push(bidResponse);

return bidResponses;
}

function getUserSyncs(syncOptions, serverResponses) {
const resSyncUrl = utils.deepAccess(serverResponses, '0.body.syncUrl');
const proSyncUrl = `https://${BIDDER_DOMAIN}/tr/v1/prebid/sync.html`;
const syncUrl = resSyncUrl || proSyncUrl;
receiveMessage();
return [{
type: 'iframe',
url: syncUrl
}];
}

function onBidWon(bid) {
let query = utils.parseQueryStringParameters({
placement_id: utils.deepAccess(bid, 'params.0.placementId'),
creative_id: utils.deepAccess(bid, 'creativeId'),
price: utils.deepAccess(bid, 'cpm'),
auction_id: utils.deepAccess(bid, 'auctionId'),
bid_id: utils.deepAccess(bid, 'requestId'),
ad_id: utils.deepAccess(bid, 'adId'),
ad_unit_code: utils.deepAccess(bid, 'adUnitCode'),
ref: window.location.href,
}).replace(/\&$/, '');
const bidDomain = utils.deepAccess(bid, 'params.0.domain') || BIDDER_DOMAIN;
const burl = `https://${bidDomain}/tr/v1/prebid/win.gif?${query}`;
utils.triggerPixel(burl);
}

function onTimeout(data) {
let query = utils.parseQueryStringParameters({
placement_id: utils.deepAccess(data, '0.params.0.placementId'),
timeout: utils.deepAccess(data, '0.timeout'),
auction_id: utils.deepAccess(data, '0.auctionId'),
bid_id: utils.deepAccess(data, '0.bidId'),
ad_unit_code: utils.deepAccess(data, '0.adUnitCode'),
ref: window.location.href,
}).replace(/\&$/, '');
const bidDomain = utils.deepAccess(data, '0.params.0.domain') || BIDDER_DOMAIN;
const timeoutUrl = `https://${bidDomain}/tr/v1/prebid/timeout.gif?${query}`;
utils.triggerPixel(timeoutUrl);
}

function createPlayerTag(playerUrl) {
return `<script src="${playerUrl}"></script>`;
}

function createRenderTag(width, height, vast) {
return `<script>(function(){` +
`window.RelaidoPlayer.renderAd({` +
`width: ${width},` +
`height: ${height},` +
`vastXml: '${vast.replace(/\r?\n/g, '')}',` +
`mediaType: 'banner'` +
`});` +
`})();</script>`;
};

function newRenderer(bidId, playerUrl) {
const renderer = Renderer.install({
id: bidId,
url: playerUrl,
loaded: false
});
try {
renderer.setRender(outstreamRender);
} catch (err) {
utils.logWarn('renderer.setRender Error', err);
}
return renderer;
}

function outstreamRender(bid) {
bid.renderer.push(() => {
window.RelaidoPlayer.renderAd({
adUnitCode: bid.adUnitCode,
width: bid.width,
height: bid.height,
vastXml: bid.vastXml,
mediaType: bid.mediaType,
});
});
}

function receiveMessage() {
window.addEventListener('message', function (e) {
if (e.data && e.data.relaido_uuid) {
storage.setDataInLocalStorage(UUID_KEY, e.data.relaido_uuid);
}
});
}

function isBannerValid(bid) {
if (!isMobile()) {
return false;
}
const sizes = utils.deepAccess(bid, 'mediaTypes.banner.sizes');
if (sizes && utils.isArray(sizes)) {
if (utils.isArray(sizes[0])) {
const width = sizes[0][0];
const height = sizes[0][1];
if (width >= 300 && height >= 250) {
return true;
}
}
}
return false;
}

function isVideoValid(bid) {
const playerSize = utils.deepAccess(bid, 'mediaTypes.video.playerSize');
if (playerSize && utils.isArray(playerSize) && playerSize.length > 0) {
const context = utils.deepAccess(bid, 'mediaTypes.video.context');
if (context && context === 'outstream') {
return true;
}
}
return false;
}

function hasUuid() {
return !!storage.getDataFromLocalStorage(UUID_KEY);
}

function getUuid() {
return storage.getDataFromLocalStorage(UUID_KEY) || '';
}

export function isMobile() {
const ua = navigator.userAgent;
if (ua.indexOf('iPhone') > -1 || ua.indexOf('iPod') > -1 || (ua.indexOf('Android') > -1 && ua.indexOf('Tablet') == -1)) {
return true;
}
return false;
}

function getMediaType(bid) {
if (hasVideoMediaType(bid)) {
return VIDEO;
} else if (hasBannerMediaType(bid)) {
return BANNER;
}
return '';
}

function hasBannerMediaType(bid) {
return !!utils.deepAccess(bid, 'mediaTypes.banner');
}

function hasVideoMediaType(bid) {
return !!utils.deepAccess(bid, 'mediaTypes.video');
}

export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [BANNER, VIDEO],
isBidRequestValid,
buildRequests,
interpretResponse,
getUserSyncs: getUserSyncs,
onBidWon,
onTimeout
}

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

```
Module Name: Relaido Bidder Adapter
Module Type: Bidder Adapter
Maintainer: video-dev@cg.relaido.co.jp
```

# Description

Connects to Relaido exchange for bids.

Relaido bid adapter supports Outstream Video.

# Test Parameters

```javascript
var adUnits=[{
code: 'banner-ad-div',
mediaTypes: {
banner: {
sizes: [
[300, 250]
]
}
},
bids: [{
bidder: 'relaido',
params: {
placementId: '9900'
}
}]
},{
code: 'video-ad-player',
mediaTypes: {
video: {
context: 'outstream',
playerSize: [640, 360]
}
},
bids: [{
bidder: 'relaido',
params: {
placementId: '9900'
}
}]
}];
```
Loading