Skip to content

Commit

Permalink
Delta Projects bid adapter: add new bid adapter (prebid#7564)
Browse files Browse the repository at this point in the history
* Delta Projects bid adapter: add new bid adapter

* Delta Projects bid adapter: revert accidental change to hello_world.html

* Remove unsupported functions by IE, add support for floor price

remove bidderParams which is not currently supported

remove bid parameter floor

remove unused function so linting is happy

Remove unused params in tests

use adservercurrency

include .js to make linter happy again

Co-authored-by: Boris-Tang <boris.tang@deltaprojects.com>
  • Loading branch information
2 people authored and Chris Pabst committed Jan 10, 2022
1 parent 11db0de commit ef70eb7
Show file tree
Hide file tree
Showing 3 changed files with 683 additions and 0 deletions.
252 changes: 252 additions & 0 deletions modules/deltaprojectsBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER } from '../src/mediaTypes.js';
import {
_each, _map, isFn, isNumber, createTrackPixelHtml, deepAccess, parseUrl, logWarn, logError
} from '../src/utils.js';
import {config} from '../src/config.js';

export const BIDDER_CODE = 'deltaprojects';
export const BIDDER_ENDPOINT_URL = 'https://d5p.de17a.com/dogfight/prebid';
export const USERSYNC_URL = 'https://userservice.de17a.com/getuid/prebid';

/** -- isBidRequestValid --**/
function isBidRequestValid(bid) {
if (!bid) return false;

if (bid.bidder !== BIDDER_CODE) return false;

// publisher id is required
const publisherId = deepAccess(bid, 'params.publisherId')
if (!publisherId) {
logError('Invalid bid request, missing publisher id in params');
return false;
}

return true;
}

/** -- Build requests --**/
function buildRequests(validBidRequests, bidderRequest) {
/** == shared ==**/
// -- build id
const id = bidderRequest.auctionId;

// -- build site
const loc = parseUrl(bidderRequest.refererInfo.referer);
const publisherId = setOnAny(validBidRequests, 'params.publisherId');
const siteId = setOnAny(validBidRequests, 'params.siteId');
const site = {
id: siteId,
domain: loc.hostname,
page: loc.href,
ref: loc.href,
publisher: { id: publisherId },
};

// -- build device
const ua = navigator.userAgent;
const device = {
ua,
w: screen.width,
h: screen.height
}

// -- build user, reg
let user = { ext: {} };
const regs = { ext: {} };
const gdprConsent = bidderRequest && bidderRequest.gdprConsent;
if (gdprConsent) {
user.ext = { consent: gdprConsent.consentString };
if (typeof gdprConsent.gdprApplies == 'boolean') {
regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0
}
}

// -- build tmax
let tmax = (bidderRequest && bidderRequest.timeout > 0) ? bidderRequest.timeout : undefined;

// build bid specific
return validBidRequests.map(validBidRequest => {
const openRTBRequest = buildOpenRTBRequest(validBidRequest, id, site, device, user, tmax, regs);
return {
method: 'POST',
url: BIDDER_ENDPOINT_URL,
data: openRTBRequest,
options: { contentType: 'application/json' },
bids: [validBidRequest],
};
});
}

function buildOpenRTBRequest(validBidRequest, id, site, device, user, tmax, regs) {
// build cur
const currency = config.getConfig('currency.adServerCurrency') || deepAccess(validBidRequest, 'params.currency');
const cur = currency && [currency];

// build impression
const impression = buildImpression(validBidRequest, currency);

// build test
const test = deepAccess(validBidRequest, 'params.test') ? 1 : 0

const at = 1

// build source
const source = {
tid: validBidRequest.transactionId,
fd: 1,
}

return {
id,
at,
imp: [impression],
site,
device,
user,
test,
tmax,
cur,
source,
regs,
ext: {},
};
}

function buildImpression(bid, currency) {
const impression = {
id: bid.bidId,
tagid: bid.params.tagId,
ext: {},
};

const bannerMediaType = deepAccess(bid, `mediaTypes.${BANNER}`);
impression.banner = buildImpressionBanner(bid, bannerMediaType);

// bid floor
const bidFloor = getBidFloor(bid, BANNER, '*', currency);
if (bidFloor) {
impression.bidfloor = bidFloor.floor;
impression.bidfloorcur = bidFloor.currency;
}

return impression;
}

function buildImpressionBanner(bid, bannerMediaType) {
const bannerSizes = (bannerMediaType && bannerMediaType.sizes) || bid.sizes;
return {
format: _map(bannerSizes, ([width, height]) => ({ w: width, h: height })),
};
}

/** -- Interpret response --**/
function interpretResponse(serverResponse) {
if (!serverResponse.body) {
logWarn('Response body is invalid, return !!');
return [];
}

const { body: { id, seatbid, cur } } = serverResponse;
if (!id || !seatbid) {
logWarn('Id / seatbid of response is invalid, return !!');
return [];
}

const bidResponses = [];

_each(seatbid, seatbid => {
_each(seatbid.bid, bid => {
const bidObj = {
requestId: bid.impid,
cpm: parseFloat(bid.price),
width: parseInt(bid.w),
height: parseInt(bid.h),
creativeId: bid.crid || bid.id,
dealId: bid.dealid || null,
currency: cur,
netRevenue: true,
ttl: 60,
};

bidObj.mediaType = BANNER;
bidObj.ad = bid.adm;
if (bid.nurl) {
bidObj.ad += createTrackPixelHtml(decodeURIComponent(bid.nurl));
}
if (bid.ext) {
bidObj[BIDDER_CODE] = bid.ext;
}
bidResponses.push(bidObj);
});
});
return bidResponses;
}

/** -- On Bid Won -- **/
function onBidWon(bid) {
let cpm = bid.cpm;
if (bid.currency && bid.currency !== bid.originalCurrency && typeof bid.getCpmInNewCurrency === 'function') {
cpm = bid.getCpmInNewCurrency(bid.originalCurrency);
}
const wonPrice = Math.round(cpm * 1000000);
const wonPriceMacroPatten = /\$\{AUCTION_PRICE:B64\}/g;
bid.ad = bid.ad.replace(wonPriceMacroPatten, wonPrice);
}

/** -- Get user syncs --**/
function getUserSyncs(syncOptions, serverResponses, gdprConsent) {
const syncs = []

if (syncOptions.pixelEnabled) {
let gdprParams;
if (gdprConsent) {
if (typeof gdprConsent.gdprApplies === 'boolean') {
gdprParams = `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`;
} else {
gdprParams = `?gdpr_consent=${gdprConsent.consentString}`;
}
} else {
gdprParams = '';
}
syncs.push({
type: 'image',
url: USERSYNC_URL + gdprParams
});
}
return syncs;
}

/** -- Get bid floor --**/
export function getBidFloor(bid, mediaType, size, currency) {
if (isFn(bid.getFloor)) {
const bidFloorCurrency = currency || 'USD';
const bidFloor = bid.getFloor({currency: bidFloorCurrency, mediaType: mediaType, size: size});
if (isNumber(bidFloor.floor)) {
return bidFloor;
}
}
}

/** -- Helper methods --**/
function setOnAny(collection, key) {
for (let i = 0, result; i < collection.length; i++) {
result = deepAccess(collection[i], key);
if (result) {
return result;
}
}
}

/** -- Register -- */
export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [BANNER],
isBidRequestValid,
buildRequests,
interpretResponse,
onBidWon,
getUserSyncs,
};

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

```
Module Name: Delta Projects Bid Adapter
Module Type: Bidder Adapter
Maintainer: dev@deltaprojects.com
```

# Description

Connects to Delta Projects DSP for bids.

# Test Parameters
```
// define banner unit
var bannerUnit = {
code: 'div-gpt-ad-1460505748561-0',
mediaTypes: {
banner: {
sizes: [[300, 250], [300,600]],
}
},
// Replace this object to test a new Adapter!
bids: [{
bidder: 'deltaprojects',
params: {
publisherId: '4' //required
}
}]
};
```

Loading

0 comments on commit ef70eb7

Please sign in to comment.