Skip to content

Commit

Permalink
SmartHub Bid Adapter: initial release (#8064)
Browse files Browse the repository at this point in the history
* add SmartHub Prebid.js adapter

* fix unit tests

* fix test params

* fix

* Update smarthubBidAdapter.md

add relevant partnerName

* trigger circleci

* fix response validation

* add netRevenue validation to main condition

Co-authored-by: maksym.voloshyn <maksym.voloshyn@smartyads.com>
  • Loading branch information
SmartHubSolutions and maksym.voloshyn authored Mar 9, 2022
1 parent 4e0bd23 commit 631c2f4
Show file tree
Hide file tree
Showing 3 changed files with 673 additions and 0 deletions.
187 changes: 187 additions & 0 deletions modules/smarthubBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import {deepAccess, isFn, logError, logMessage} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {config} from '../src/config.js';

const BIDDER_CODE = 'smarthub';

function isBidResponseValid(bid) {
if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency || !bid.hasOwnProperty('netRevenue')) {
return false;
}
switch (bid.mediaType) {
case BANNER:
return Boolean(bid.width && bid.height && bid.ad);
case VIDEO:
return Boolean(bid.width && bid.height && (bid.vastUrl || bid.vastXml));
case NATIVE:
return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length);
default:
return false;
}
}

function getPlacementReqData(bid) {
const { params, bidId, mediaTypes } = bid;
const schain = bid.schain || {};
const { partnerName, seat, token, iabCat, minBidfloor, pos } = params;
const bidfloor = getBidFloor(bid);

const placement = {
partnerName: partnerName.toLowerCase(),
seat,
token,
iabCat,
minBidfloor,
pos,
bidId,
schain,
bidfloor
};

if (mediaTypes && mediaTypes[BANNER]) {
placement.adFormat = BANNER;
placement.sizes = mediaTypes[BANNER].sizes;
} else if (mediaTypes && mediaTypes[VIDEO]) {
placement.adFormat = VIDEO;
placement.playerSize = mediaTypes[VIDEO].playerSize;
placement.minduration = mediaTypes[VIDEO].minduration;
placement.maxduration = mediaTypes[VIDEO].maxduration;
placement.mimes = mediaTypes[VIDEO].mimes;
placement.protocols = mediaTypes[VIDEO].protocols;
placement.startdelay = mediaTypes[VIDEO].startdelay;
placement.placement = mediaTypes[VIDEO].placement;
placement.skip = mediaTypes[VIDEO].skip;
placement.skipafter = mediaTypes[VIDEO].skipafter;
placement.minbitrate = mediaTypes[VIDEO].minbitrate;
placement.maxbitrate = mediaTypes[VIDEO].maxbitrate;
placement.delivery = mediaTypes[VIDEO].delivery;
placement.playbackmethod = mediaTypes[VIDEO].playbackmethod;
placement.api = mediaTypes[VIDEO].api;
placement.linearity = mediaTypes[VIDEO].linearity;
} else if (mediaTypes && mediaTypes[NATIVE]) {
placement.native = mediaTypes[NATIVE];
placement.adFormat = NATIVE;
}

return placement;
}

function getBidFloor(bid) {
if (!isFn(bid.getFloor)) {
return deepAccess(bid, 'params.bidfloor', 0);
}

try {
const bidFloor = bid.getFloor({
currency: 'USD',
mediaType: '*',
size: '*',
});
return bidFloor.floor;
} catch (e) {
logError(e);
return 0;
}
}

function buildRequestParams(bidderRequest = {}, placements = []) {
let deviceWidth = 0;
let deviceHeight = 0;

let winLocation;
try {
const winTop = window.top;
deviceWidth = winTop.screen.width;
deviceHeight = winTop.screen.height;
winLocation = winTop.location;
} catch (e) {
logMessage(e);
winLocation = window.location;
}

const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.referer;
let refferLocation;
try {
refferLocation = refferUrl && new URL(refferUrl);
} catch (e) {
logMessage(e);
}

let location = refferLocation || winLocation;
const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : '';
const host = location.host;
const page = location.pathname;
const secure = location.protocol === 'https:' ? 1 : 0;
return {
deviceWidth,
deviceHeight,
language,
secure,
host,
page,
placements,
coppa: config.getConfig('coppa') === true ? 1 : 0,
ccpa: bidderRequest.uspConsent || undefined,
gdpr: bidderRequest.gdprConsent || undefined,
tmax: config.getConfig('bidderTimeout')
};
}

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

isBidRequestValid: (bid = {}) => {
const { params, bidId, mediaTypes } = bid;
let valid = Boolean(bidId && params && params.partnerName && params.seat && params.token);

if (mediaTypes && mediaTypes[BANNER]) {
valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes);
} else if (mediaTypes && mediaTypes[VIDEO]) {
valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize);
} else if (mediaTypes && mediaTypes[NATIVE]) {
valid = valid && Boolean(mediaTypes[NATIVE]);
} else {
valid = false;
}
return valid;
},

buildRequests: (validBidRequests = [], bidderRequest = {}) => {
const tempObj = {};

const len = validBidRequests.length;
for (let i = 0; i < len; i++) {
const bid = validBidRequests[i];
const data = getPlacementReqData(bid);
tempObj[data.partnerName] = tempObj[data.partnerName] || [];
tempObj[data.partnerName].push(data);
}

return Object.keys(tempObj).map(key => {
const request = buildRequestParams(bidderRequest, tempObj[key]);
return {
method: 'POST',
url: `https://${key}-prebid.smart-hub.io/pbjs`,
data: request,
}
});
},

interpretResponse: (serverResponse) => {
let response = [];
for (let i = 0; i < serverResponse.body.length; i++) {
let resItem = serverResponse.body[i];
if (isBidResponseValid(resItem)) {
const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : [];
resItem.meta = { ...resItem.meta, advertiserDomains };

response.push(resItem);
}
}
return response;
}
};

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

```
Module Name: SmartHub Bidder Adapter
Module Type: SmartHub Bidder Adapter
Maintainer: support@smart-hub.io
```

# Description

Connects to SmartHub exchange for bids.

SmartHub bid adapter supports Banner, Video (instream and outstream) and Native.

# Test Parameters
```
var adUnits = [
// Will return static test banner
{
code: 'adunit1',
mediaTypes: {
banner: {
sizes: [ [300, 250], [320, 50] ],
}
},
bids: [
{
bidder: 'smarthub',
params: {
partnerName: 'pbjstest',
seat: 'testSeat',
token: 'testBanner',
iabCat: ['IAB1-1', 'IAB3-1', 'IAB4-3'],
minBidfloor: 10,
pos: 1,
}
}
]
},
{
code: 'addunit2',
mediaTypes: {
video: {
playerSize: [ [640, 480] ],
minduration: 5,
maxduration: 60,
}
},
bids: [
{
bidder: 'smarthub',
params: {
partnerName: 'pbjstest',
seat: 'testSeat',
token: 'testVideo',
iabCat: ['IAB1-1', 'IAB3-1', 'IAB4-3'],
minBidfloor: 10,
pos: 1,
}
}
]
},
{
code: 'addunit3',
mediaTypes: {
native: {
title: {
required: true
},
body: {
required: true
},
icon: {
required: true,
size: [64, 64]
}
}
},
bids: [
{
bidder: 'smarthub',
params: {
partnerName: 'pbjstest',
seat: 'testSeat',
token: 'testNative',
iabCat: ['IAB1-1', 'IAB3-1', 'IAB4-3'],
minBidfloor: 10,
pos: 1,
}
}
]
}
];
```
Loading

0 comments on commit 631c2f4

Please sign in to comment.