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

Added Hybrid.ai adapter #4566

Merged
merged 9 commits into from
Apr 3, 2020
198 changes: 198 additions & 0 deletions modules/hybridBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import * as utils from '../src/utils'
import { registerBidder } from '../src/adapters/bidderFactory'
import { auctionManager } from '../src/auctionManager'
import { BANNER, VIDEO } from '../src/mediaTypes'
import {Renderer} from '../src/Renderer';
idettman marked this conversation as resolved.
Show resolved Hide resolved
import find from 'core-js/library/fn/array/find';

const BIDDER_CODE = 'hybrid';
const DSP_ENDPOINT = 'https://hbe198.hybrid.ai/prebidhb';
const TRAFFIC_TYPE_WEB = 1;
const PLACEMENT_TYPE_BANNER = 1;
const PLACEMENT_TYPE_VIDEO = 2;
const TTL = 60;
const RENDERER_URL = '//acdn.adnxs.com/video/outstream/ANOutstreamVideo.js';
idettman marked this conversation as resolved.
Show resolved Hide resolved

const placemenTypes = {
'banner': PLACEMENT_TYPE_BANNER,
'video': PLACEMENT_TYPE_VIDEO
};

function buildBidRequests(validBidRequests) {
return utils._map(validBidRequests, function(validBidRequest) {
const params = validBidRequest.params;
const bidRequest = {
bidId: validBidRequest.bidId,
transactionId: validBidRequest.transactionId,
sizes: validBidRequest.sizes,
placement: placemenTypes[params.placement],
placeId: params.placeId
};

return bidRequest;
})
}

const outstreamRender = bid => {
bid.renderer.push(() => {
window.ANOutstreamVideo.renderAd({
sizes: [bid.width, bid.height],
targetId: bid.adUnitCode,
rendererOptions: {
showBigPlayButton: false,
showProgressBar: 'bar',
showVolume: false,
allowFullscreen: true,
skippable: false,
content: bid.vastXml
}
});
});
}

const createRenderer = (bid) => {
const renderer = Renderer.install({
targetId: bid.adUnitCode,
url: RENDERER_URL,
loaded: false
});

try {
renderer.setRender(outstreamRender);
} catch (err) {
utils.logWarn('Prebid Error calling setRender on renderer', err);
}

return renderer;
}

function buildBid(bidData) {
const bid = {
requestId: bidData.bidId,
cpm: bidData.price,
width: bidData.width,
height: bidData.height,
creativeId: bidData.bidId,
currency: bidData.currency,
netRevenue: true,
ttl: TTL
};

if (bidData.placement === PLACEMENT_TYPE_VIDEO) {
bid.vastXml = bidData.content;
bid.mediaType = VIDEO;

let adUnit = find(auctionManager.getAdUnits(), function (unit) {
return unit.transactionId === bidData.transactionId;
});

if (adUnit) {
bid.width = adUnit.mediaTypes.video.playerSize[0][0];
bid.height = adUnit.mediaTypes.video.playerSize[0][1];

if (adUnit.mediaTypes.video.context === 'outstream') {
bid.renderer = createRenderer(bid);
}
}
} else {
bid.ad = bidData.content;
bid.mediaType = BANNER;
}

return bid;
}

function getMediaTypeFromBid(bid) {
return bid.mediaTypes && Object.keys(bid.mediaTypes)[0]
}

function hasVideoMandatoryParams(mediaTypes) {
const isHasVideoContext = !!mediaTypes.video && (mediaTypes.video.context === 'instream' || mediaTypes.video.context === 'outstream');

const isPlayerSize =
!!utils.deepAccess(mediaTypes, 'video.playerSize') &&
utils.isArray(utils.deepAccess(mediaTypes, 'video.playerSize'));

return isHasVideoContext && isPlayerSize;
}

export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [BANNER, VIDEO],
placemenTypes: placemenTypes,

/**
* Determines whether or not the given bid request is valid.
*
* @param {BidRequest} bid The bid params to validate.
* @return boolean True if this is a valid bid, and false otherwise.
*/
isBidRequestValid(bid) {
return (
!!bid.params.placeId &&
!!bid.params.placement &&
(
(getMediaTypeFromBid(bid) === BANNER && bid.params.placement === 'banner') ||
(getMediaTypeFromBid(bid) === VIDEO && bid.params.placement === 'video' && hasVideoMandatoryParams(bid.mediaTypes))
)
);
},

/**
* Make a server request from the list of BidRequests.
*
* @param {validBidRequests[]} - an array of bids
* @return ServerRequest Info describing the request to the server.
*/
buildRequests(validBidRequests, bidderRequest) {
const payload = {
url: bidderRequest.refererInfo.referer,
cmp: !!bidderRequest.gdprConsent,
trafficType: TRAFFIC_TYPE_WEB,
bidRequests: buildBidRequests(validBidRequests)
};

if (payload.cmp) {
const gdprApplies = bidderRequest.gdprConsent.gdprApplies;
if (gdprApplies !== undefined) payload['ga'] = gdprApplies;
payload['cs'] = bidderRequest.gdprConsent.consentString;
}

const payloadString = JSON.stringify(payload);
return {
method: 'POST',
url: DSP_ENDPOINT,
data: payloadString,
options: {
contentType: 'application/json'
}
}
},

/**
* Unpack the response from the server into a list of bids.
*
* @param {ServerResponse} serverResponse A successful response from the server.
* @return {Bid[]} An array of bids which were nested inside the server.
*/
interpretResponse: function(serverResponse, bidRequest) {
let bidRequests = JSON.parse(bidRequest.data).bidRequests;
const serverBody = serverResponse.body;

if (serverBody && serverBody.bids && utils.isArray(serverBody.bids)) {
return utils._map(serverBody.bids, function(bid) {
let rawBid = find(bidRequests, function (item) {
return item.bidId === bid.bidId;
});
bid.placement = rawBid.placement;
bid.transactionId = rawBid.transactionId;
bid.placeId = rawBid.placeId;
return buildBid(bid);
});
} else {
return [];
}
}

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


**Module Name**: Hybrid.ai Bidder Adapter
**Module Type**: Bidder Adapter
**Maintainer**: prebid@hybrid.ai

# Description

You can use this adapter to get a bid from Hybrid.ai

About us: https://hybrid.ai

## Sample Banner Ad Unit

```js
var adUnits = [{
code: 'banner_ad_unit',
mediaTypes: {
banner: {
sizes: [[728, 90]]
}
},
bids: [{
bidder: "hybrid",
params: {
placement: "banner", // required
placeId: "5af45ad34d506ee7acad0c26" // required
}
}]
}];
```

## Sample Video Ad Unit

```js
var adUnits = [{
code: 'video_ad_unit',
mediaTypes: {
video: {
context: 'outstream', // required
playerSize: [640, 480] // required
}
},
bids: [{
bidder: 'hybrid',
params: {
placement: "video", // required
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is the adapter expected to handle multi-format ad Units?

Where is is both banner and video?

What would the placement be?

Was thinking placement could be just inferred based on the adUnit otherwise.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate on this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The adapter doesn't support multi-format ad Units in the current realization. If this a required condition we can add support multi-format ad Units.

placeId: "5af45ad34d506ee7acad0c26" // required
}
}]
}];
```

Loading