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

Feature/adapter #4219

Merged
merged 14 commits into from
Nov 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
142 changes: 142 additions & 0 deletions modules/deepintentBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import {registerBidder} from '../src/adapters/bidderFactory';
import {BANNER} from '../src/mediaTypes';
import * as utils from '../src/utils';
const BIDDER_CODE = 'deepintent';
const BIDDER_ENDPOINT = 'https://prebid.deepintent.com/prebid';
const USER_SYNC_URL = 'https://beacon.deepintent.com/usersync.html';

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

// tagId is mandatory param
isBidRequestValid: bid => {
let valid = false;
if (bid && bid.params && bid.params.tagId) {
if (typeof bid.params.tagId === 'string' || bid.params.tagId instanceof String) {
valid = true;
}
}
return valid;
},
interpretResponse: function(bidResponse, request) {
let responses = [];
if (bidResponse && bidResponse.body) {
let bids = bidResponse.body.seatbid && bidResponse.body.seatbid[0] ? bidResponse.body.seatbid[0].bid : [];
responses = bids.map(bid => formatResponse(bid))
}
return responses;
},
buildRequests: function (validBidRequests, bidderRequest) {
const openRtbBidRequest = {
id: utils.generateUUID(),
at: 1,
imp: validBidRequests.map(bid => buildImpression(bid)),
site: buildSite(bidderRequest),
device: buildDevice(),
source: {
fd: 0,
ext: {
type: 2
}
}
};

return {
method: 'POST',
url: BIDDER_ENDPOINT,
data: JSON.stringify(openRtbBidRequest),
options: {
contentType: 'application/json'
}
};
},
/**
* Register User Sync.
*/
getUserSyncs: syncOptions => {
if (syncOptions.iframeEnabled) {
return [{
type: 'iframe',
url: USER_SYNC_URL
}];
}
}

};

function formatResponse(bid) {
return {
requestId: bid && bid.impid ? bid.impid : undefined,
cpm: bid && bid.price ? bid.price : 0.0,
width: bid && bid.w ? bid.w : 0,
height: bid && bid.h ? bid.h : 0,
ad: bid && bid.adm ? bid.adm : '',
creativeId: bid && bid.crid ? bid.crid : undefined,
netRevenue: false,
currency: bid && bid.cur ? bid.cur : 'USD',
ttl: 300,
dealId: bid && bid.dealId ? bid.dealId : undefined
}
}

function buildImpression(bid) {
return {
id: bid.bidId,
tagid: bid.params.tagId || '',
secure: window.location.protocol === 'https' ? 1 : 0,
banner: buildBanner(bid),
ext: bid.params.custom ? bid.params.custom : {}
};
}

function buildBanner(bid) {
if (utils.deepAccess(bid, 'mediaTypes.banner')) {
// Get Sizes from MediaTypes Object, Will always take first size, will be overrided by params for exact w,h
if (utils.deepAccess(bid, 'mediaTypes.banner.sizes') && !bid.params.height && !bid.params.width) {
let sizes = utils.deepAccess(bid, 'mediaTypes.banner.sizes');
if (utils.isArray(sizes) && sizes.length > 0) {
return {
h: sizes[0][1],
w: sizes[0][0]
}
}
} else {
return {
h: bid.params.height,
w: bid.params.width
}
}
}
}

function buildSite(bidderRequest) {
let site = {};
if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) {
site.page = bidderRequest.refererInfo.referer;
site.domain = getDomain(bidderRequest.refererInfo.referer);
}
return site;
}

function getDomain(referer) {
if (referer) {
let domainA = document.createElement('a');
domainA.href = referer;
return domainA.hostname;
}
}

function buildDevice() {
return {
ua: navigator.userAgent,
js: 1,
dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack === '1') ? 1 : 0,
h: screen.height,
w: screen.width,
language: navigator.language
}
}

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

```
Module Name: Deepintent Bidder Adapter
Module Type: Bidder Adapter
Maintainer: prebid@deepintent.com
```

# Description

Deepintent currently supports the BANNER type ads through prebid js

Module that connects to Deepintent's demand sources.

# Banner Test Request
```
var adUnits = [
{
code: 'di_adUnit1',
mediaTypes: {
banner: {
sizes: [[300, 250]], // a display size, only first one will be picked up since multiple ad sizes are not supported yet
}
}
bids: [
{
bidder: 'deepintent',
params: {
tagId: '1300', // Required parameter
w: 300, // Width and Height here will override sizes in mediatype
h: 250,
custom: { // Custom parameters in form of key value pairs
user_min_age: 18
}
}
}
]
}
];
```

###Recommended User Sync Configuration

```javascript
pbjs.setConfig({
userSync: {
iframeEnabled: true,
enabledBidders: ['deepintent'],
syncDelay: 3000
}});


```
162 changes: 162 additions & 0 deletions test/spec/modules/deepintentBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import {expect} from 'chai';
import {spec} from 'modules/deepintentBidAdapter';
import * as utils from '../../../src/utils';

describe('Deepintent adapter', function () {
let request;
let bannerResponse;

beforeEach(function () {
request = [
{
bidder: 'deepintent',
mediaTypes: {
banner: {
sizes: [[300, 250]]
}
},
params: {
tagId: '100013',
w: 728,
h: 90,
custom: {
user_gender: 'female',
user_max_age: 25
}
}
}
];
bannerResponse = {
'body': {
'id': '303e1fae-9677-41e2-9a92-15a23445363f',
'seatbid': [{
'bid': [{
'id': '11447bb1-a266-470d-b0d7-8810f5b1b75f',
'impid': 'a7e92b9b-d9db-4de8-9c3f-f90737335445',
'price': 0.6,
'adid': '10001',
'adm': "<span id='deepintent_wrapper_a7e92b9b-d9db-4de8-9c3f-f90737335445' onclick=DeepIntentExecuteClicks('%%CLICK_URL_UNESC%%')><span id='deepintent_wrapper_span_9-1bfd-4764-b4cf-0bb1a74e554e'><a href='https://test-beacon.deepintent.com/click?id=11447bb1-a266-470d-b0d7-8810f5b1b75f&ts=1565252378497&r=http%3A%2F%2Ftest.com' target='_blank'><img src='https://storage.googleapis.com/deepintent-test/adv/10001/asset/a640bcb5c0d5416096290d1c1097a1e9.jpg'></img></a></span><noscript class=\"MOAT-deepintentdisplay440800993657?moatClientLevel1=10001&amp;moatClientLevel2=103389&amp;moatClientLevel3=13665&amp;moatClientSlicer1=washingtonpost.com&amp;zMoatBID=11447bb1-a266-470d-b0d7-8810f5b1b75f&amp;zMoatTIME=1565252378495&amp;zMoatCGRP=530\"></noscript>\r\n<script src=\"https://z.moatads.com/deepintentdisplay440800993657/moatad.js#moatClientLevel1=10001&moatClientLevel2=103389&moatClientLevel3=13665&moatClientSlicer1=washingtonpost.com&zMoatBID=11447bb1-a266-470d-b0d7-8810f5b1b75f&zMoatTIME=1565252378495&zMoatCGRP=530\" type=\"text/javascript\"></script><img src='https://tracking.com' height='0px' width='0px' style='display:none'></img></span><script type='text/javascript'>window.DeepIntentExecuteClicks=window.DeepIntentExecuteClicks||function(e){if(e)for(var n=e.split(','),t=0;t<n.length;t++)(new Image).src=n[t]};</script><img src='https://test-beacon.deepintent.com/impression?id=11447bb1-a266-470d-b0d7-8810f5b1b75f&ts=1565252378497&wp=%%WINNING_PRICE%%' height='0px' width='0px' style='display:none'></img><iframe src='https://cdn.deepintent.com/sync/adx.html' width='0' height='0' style='display:none;'></iframe>",
'adomain': ['deepintent.com'],
'cid': '103389',
'crid': '13665',
'w': 300,
'h': 250,
'dealId': 'dee_12312stdszzsx'
}],
'seat': '10000'
}],
'bidid': '0b08b09f-aaa1-4c14-b1c8-7debb1a7c1cd'
}
}
});

describe('validations', function () {
it('validBid : tagId is passed', function () {
let bid = {
bidder: 'deepintent',
params: {
tagId: '1232'
}
},
isValid = spec.isBidRequestValid(bid);
expect(isValid).to.equals(true);
});
it('invalidBid : tagId is not passed', function () {
let bid = {
bidder: 'deepintent',
params: {
h: 200,
w: 300
}
},
isValid = spec.isBidRequestValid(bid);
expect(isValid).to.equals(false);
});
it('invalidBid : tagId is not a string', function () {
let bid = {
bidder: 'deepintent',
params: {
tagId: 12345
}
},
isValid = spec.isBidRequestValid(bid);
expect(isValid).to.equals(false);
});
});
describe('request check', function () {
it('unmutaable bid request check', function () {
let oRequest = utils.deepClone(request),
bidRequest = spec.buildRequests(request);
expect(request).to.deep.equal(oRequest);
});
it('bidder connection check', function () {
let bRequest = spec.buildRequests(request);
expect(bRequest.url).to.equal('https://prebid.deepintent.com/prebid');
expect(bRequest.method).to.equal('POST');
expect(bRequest.options.contentType).to.equal('application/json');
});
it('bid request check : Device', function () {
let bRequest = spec.buildRequests(request);
let data = JSON.parse(bRequest.data);
expect(data.device.ua).to.be.a('string');
expect(data.device.js).to.equal(1);
expect(data.device.dnt).to.be.a('number');
expect(data.device.h).to.be.a('number');
expect(data.device.w).to.be.a('number');
});
it('bid request check : Impression', function () {
let bRequest = spec.buildRequests(request);
let data = JSON.parse(bRequest.data);
expect(data.at).to.equal(1); // auction type
expect(data.imp[0].id).to.equal(request[0].bidId);
expect(data.imp[0].tagid).to.equal('100013');
});
it('bid request check : ad size', function () {
let bRequest = spec.buildRequests(request);
let data = JSON.parse(bRequest.data);
expect(data.imp[0].banner).to.be.a('object');
expect(data.imp[0].banner.w).to.equal(300);
expect(data.imp[0].banner.h).to.equal(250);
});
it('bid request check : custom params', function () {
let bRequest = spec.buildRequests(request);
let data = JSON.parse(bRequest.data);
expect(data.imp[0].ext).to.be.a('object');
expect(data.imp[0].ext.user_gender).to.equal('female');
expect(data.imp[0].ext.user_max_age).to.equal(25);
});
it('bid request check: source params', function () {
let bRequest = spec.buildRequests(request);
let data = JSON.parse(bRequest.data);
expect(data.source.fd).to.equal(0);
expect(data.source.ext.type).to.equal(2);
})
});
describe('user sync check', function () {
it('user sync url check', function () {
let syncOptions = {
iframeEnabled: true
};
let userSync = spec.getUserSyncs(syncOptions);
expect(userSync).to.be.an('array').with.length.above(0);
expect(userSync[0].type).to.equal('iframe');
expect(userSync[0].url).to.equal('https://beacon.deepintent.com/usersync.html');
});
});
describe('response check', function () {
it('bid response check: valid bid response', function () {
let bRequest = spec.buildRequests(request);
let data = JSON.parse(bRequest.data);
let bResponse = spec.interpretResponse(bannerResponse, request);
expect(bResponse).to.be.an('array').with.length.above(0);
expect(bResponse[0].requestId).to.equal(bannerResponse.body.seatbid[0].bid[0].impid);
expect(bResponse[0].width).to.equal(bannerResponse.body.seatbid[0].bid[0].w);
expect(bResponse[0].height).to.equal(bannerResponse.body.seatbid[0].bid[0].h);
expect(bResponse[0].currency).to.equal('USD');
expect(bResponse[0].netRevenue).to.equal(false);
expect(bResponse[0].ttl).to.equal(300);
expect(bResponse[0].creativeId).to.equal(bannerResponse.body.seatbid[0].bid[0].crid);
expect(bResponse[0].dealId).to.equal(bannerResponse.body.seatbid[0].bid[0].dealId);
});
})
});