Skip to content

Commit

Permalink
Feature/adapter (prebid#4219)
Browse files Browse the repository at this point in the history
* feat(bidrequest): code for making bidrequest

* feat(bidresponse): format and return the response

* feat(tests): added tests for adapter

* feat(docs): added docs for the adapter

* refactor(url): changed adserver url

* test(user sync): added unit tests for the user syncs

* refactor(endpoint): changed endpoint for prebid

* refactor(endpoint): changed endpoint for prebid

* doc(tagid): mandatory param definition added

* fix(imp id): fix for correct impression id

* fix(width/height): fix for correct width and height sequence
  • Loading branch information
sourabhg authored and sa1omon committed Nov 28, 2019
1 parent bf94a0d commit bb4ae80
Show file tree
Hide file tree
Showing 3 changed files with 357 additions and 0 deletions.
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);
});
})
});

0 comments on commit bb4ae80

Please sign in to comment.