Skip to content

Commit

Permalink
Added iQM Bid Adapter for Prebid.js 1.0 (#1880)
Browse files Browse the repository at this point in the history
* Added iQM Bid Adapter for Prebid.js 1.0

* Modified URL from http to https

* Removed geo function which was fetching user location.
  • Loading branch information
pkthakkar26 authored and Matt Kendall committed Dec 19, 2017
1 parent b738584 commit f1f18e1
Show file tree
Hide file tree
Showing 3 changed files with 397 additions and 0 deletions.
137 changes: 137 additions & 0 deletions modules/iqmBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import * as utils from 'src/utils';
// import {config} from 'src/config';
import {registerBidder} from 'src/adapters/bidderFactory';
const BIDDER_CODE = 'iqm';
const ENDPOINT_URL = 'https://pbd.bids.iqm.com';
const VERSION = 'v.1.0.0';

export const spec = {
code: BIDDER_CODE,
aliases: ['iqm'], // short code
/**
* 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: function(bid) {
return !!(bid && bid.params && bid.params.publisherId && bid.params.placementId && bid.params.tagId);
},
/**
* Make a server request from the list of BidRequests.
*
* @return ServerRequest Info describing the request to the server.
* @param validBidRequests - an array of bids
*/
buildRequests: function(validBidRequests) {
let requestId = '';
let siteId = '';
let device = getDevice();
return validBidRequests.map(bid => {
requestId = bid.requestId;
let bidfloor = utils.getBidIdParameter('bidfloor', bid.params);
siteId = utils.getBidIdParameter('tagId', bid.params);
const imp = {
id: bid.bidId,
secure: 1,
bidfloor: bidfloor || 0,
displaymanager: 'Prebid.js',
displaymanagerver: VERSION,
mediatype: 'banner'
};
imp.banner = getSize(bid.sizes);
let data = {
id: requestId,
publisherId: utils.getBidIdParameter('publisherId', bid.params),
tagId: utils.getBidIdParameter('tagId', bid.params),
placementId: utils.getBidIdParameter('placementId', bid.params),
device: device,
site: {
id: siteId,
page: utils.getTopWindowLocation().href,
domain: utils.getTopWindowLocation().host
},
imp: imp
};
return {
method: 'POST',
url: ENDPOINT_URL,
data: data
};
});
},
/**
* Unpack the response from the server into a list of bids.
*
* @param {*} serverResponse A successful response from the server.
* @param bidRequest
* @return {Bid[]} An array of bids which were nested inside the server.
*/
interpretResponse: function(serverResponse, bidRequest) {
// const serverBody = serverResponse.body;
// const headerValue = serverResponse.headers.get('some-response-header')
const bidResponses = [];
serverResponse = serverResponse.body;
if (serverResponse && utils.isArray(serverResponse.seatbid)) {
utils._each(serverResponse.seatbid, function(bidList) {
utils._each(bidList.bid, function(bid) {
const responseCPM = parseFloat(bid.price);
if (responseCPM > 0.0 && bid.impid) {
// const responseNurl = bid.nurl || '';
const bidResponse = {
requestId: bid.impid,
currency: serverResponse.cur || 'USD',
cpm: responseCPM,
netRevenue: true,
creativeId: bid.crid || '',
ad: bid.adm || '',
width: bid.w || bidRequest.data.imp.banner.w,
height: bid.h || bidRequest.data.imp.banner.h,
ttl: bid.ttl || 300
};

bidResponses.push(bidResponse);
}
})
});
}
return bidResponses;
}
};

let getDevice = function () {
const language = navigator.language ? 'language' : 'userLanguage';
return {
h: screen.height,
w: screen.width,
dnt: _getDNT() ? 1 : 0,
language: navigator[language].split('-')[0],
make: navigator.vendor ? navigator.vendor : '',
ua: navigator.userAgent,
devicetype: _isMobile() ? 1 : _isConnectedTV() ? 3 : 2
};
};

let _getDNT = function () {
return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNotTrack === '1' || navigator.doNotTrack === 'yes';
};

let getSize = function (sizes) {
let sizeMap;
if (sizes.length === 2 && typeof sizes[0] === 'number' && typeof sizes[1] === 'number') {
sizeMap = {w: sizes[0], h: sizes[1]};
} else {
sizeMap = {w: sizes[0][0], h: sizes[0][1]};
}
return sizeMap;
};

function _isMobile() {
return (/(ios|ipod|ipad|iphone|android)/i).test(global.navigator.userAgent);
}

function _isConnectedTV() {
return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(global.navigator.userAgent);
}

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

```
Module Name: iQM Bidder Adapter
Module Type: Bidder Adapter
Maintainer: hbteam@iqm.com
```

# Parameters

| Name | Scope | Description | Example |
| :------------ | :------- | :------------------------ | :------------------- |
| `publisherId` | required | The Publisher ID from iQM | "df5fd732-c5f3-11e7" |
| `tagId` | required | The tag ID from iQM | "1c5c9ec2-c5f4-11e7" |
| `placementId` | required | The Placement ID from iQM | "50cc36fe-c5f4-11e7" |
| `bidfloor` | optional | Bid Floor | 0.50 |

# Description

Module that connects to iQM demand sources

# Test Parameters
```
var adUnits = [
{
code: 'test-div1',
sizes: [[320, 50]], // display 320x50
bids: [
{
bidder: 'iqm',
params: {
publisherId: 'df5fd732-c5f3-11e7-abc4-cec278b6b50a',
tagId: '1c5c9ec2-c5f4-11e7-abc4-cec278b6b50a',
placementId: '50cc36fe-c5f4-11e7-abc4-cec278b6b50a',
bidfloor: 0.50,
}
}
]
}
];
```
219 changes: 219 additions & 0 deletions test/spec/modules/iqmBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import {expect} from 'chai';
import {spec} from 'modules/iqmBidAdapter'
import * as utils from 'src/utils';

describe('iqmBidAdapter', () => {
const ENDPOINT_URL = 'https://pbd.bids.iqm.com';
const bidRequests = [{
bidder: 'iqm',
params: {
position: 1,
tagId: 'tagId-1',
placementId: 'placementId-1',
pubId: 'pubId-1',
secure: true,
bidfloor: 0.5
},
placementCode: 'pcode000',
transactionId: 'tx000',
sizes: [[300, 250]],
bidId: 'bid000',
bidderRequestId: '117d765b87bed38',
requestId: 'req000'
}];

const bidResponses = {
body: {
id: 'req000',
seatbid: [{
bid: [{
nurl: 'nurl',
adm: '<img src"http://www.imgurl.com" />',
crid: 'cr-65981',
impid: 'bid000',
price: 0.99,
w: 300,
h: 250,
adomain: ['https://example.com'],
id: 'bid000',
ttl: 300
}]
}]
},
headers: {}};

const bidResponseEmptySeat = {
body: {
id: 'req000',
seatbid: []
},
headers: {}
};

const bidResponseEmptyBid = {
body: {
id: 'req000',
seatbid: [{
bid: []
}]
},
headers: {}
};

const bidResponseNoImpId = {
body: {
id: 'req000',
seatbid: [{
bid: [{
nurl: 'nurl',
adm: '<img src"http://www.imgurl.com" />',
crid: 'cr-65981',
price: 0.99,
w: 300,
h: 250,
adomain: ['https://example.com'],
id: 'bid000',
ttl: 300
}]
}]
},
headers: {}
};

describe('Request verification', () => {
it('basic property verification', () => {
expect(spec.code).to.equal('iqm');
expect(spec.aliases).to.be.an('array');
// expect(spec.aliases).to.be.ofSize(1);
expect(spec.aliases).to.have.lengthOf(1);
});

describe('isBidRequestValid', () => {
let bid = {
'bidder': 'iqm',
'params': {
'placementId': 'placementId',
'tagId': 'tagId',
'publisherId': 'pubId'
},
'adUnitCode': 'ad-unit-code',
'sizes': [[300, 250], [300, 600]],
'bidId': '30b31c1838de1e',
'bidderRequestId': '22edbae2733bf6',
'auctionId': '1d1a030790a475'
};

it('should return false for empty object', () => {
expect(spec.isBidRequestValid({})).to.equal(false);
});

it('should return false for request without param', () => {
let bid = Object.assign({}, bid);
delete bid.params;
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false for invalid params', () => {
let bid = Object.assign({}, bid);
delete bid.params;
bid.params = {
'placementId': 'placementId'
};
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return true for proper request', () => {
expect(spec.isBidRequestValid(bid)).to.equal(true);
});
});

describe('buildRequests', () => {
it('sends every bid request to ENDPOINT_URL via POST method', () => {
const requests = spec.buildRequests(bidRequests);
expect(requests[0].method).to.equal('POST');
expect(requests[0].url).to.equal(ENDPOINT_URL);
// expect(requests[1].method).to.equal('POST');
// expect(requests[1].url).to.equal(ENDPOINT_URL);
});

it('should send request data with every request', () => {
const requests = spec.buildRequests(bidRequests);
const data = requests[0].data;
expect(data.id).to.equal(bidRequests[0].requestId);

expect(data.imp.id).to.equal(bidRequests[0].bidId);
expect(data.imp.bidfloor).to.equal(bidRequests[0].params.bidfloor);
expect(data.imp.secure).to.equal(1);
expect(data.imp.displaymanager).to.equal('Prebid.js');
expect(data.imp.displaymanagerver).to.equal('v.1.0.0');
expect(data.imp.mediatype).to.equal('banner');
expect(data.imp.banner).to.deep.equal({
w: 300,
h: 250
});
expect(data.publisherId).to.equal(utils.getBidIdParameter('publisherId', bidRequests[0].params));
expect(data.tagId).to.equal(utils.getBidIdParameter('tagId', bidRequests[0].params));
expect(data.placementId).to.equal(utils.getBidIdParameter('placementId', bidRequests[0].params));
expect(data.device.w).to.equal(screen.width);
expect(data.device.h).to.equal(screen.height);
expect(data.device.make).to.equal(navigator.vendor ? navigator.vendor : '');
expect(data.device.ua).to.equal(navigator.userAgent);
expect(data.device.dnt).to.equal(navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNotTrack === '1' || navigator.doNotTrack === 'yes' ? 1 : 0);
expect(data.site).to.deep.equal({
id: utils.getBidIdParameter('tagId', bidRequests[0].params),
page: utils.getTopWindowLocation().href,
domain: utils.getTopWindowLocation().host
});

expect(data.device.ua).to.equal(navigator.userAgent);
expect(data.device.h).to.equal(screen.height);
expect(data.device.w).to.equal(screen.width);

expect(data.site.id).to.equal(bidRequests[0].params.tagId);
expect(data.site.page).to.equal(utils.getTopWindowLocation().href);
expect(data.site.domain).to.equal(utils.getTopWindowLocation().host);
});
});

describe('interpretResponse', () => {
it('should handle no bid response', () => {
const response = spec.interpretResponse({ body: null }, { bidRequests });
expect(response.length).to.equal(0);
});

it('should have at least one Seat Object', () => {
const request = spec.buildRequests(bidRequests);
const response = spec.interpretResponse(bidResponseEmptySeat, request);
expect(response.length).to.equal(0);
});

it('should have at least one Bid Object', () => {
const request = spec.buildRequests(bidRequests);
const response = spec.interpretResponse(bidResponseEmptyBid, request);
expect(response.length).to.equal(0);
});

it('should have impId in Bid Object', () => {
const request = spec.buildRequests(bidRequests);
const response = spec.interpretResponse(bidResponseNoImpId, request);
expect(response.length).to.equal(0);
});

it('should handle valid response', () => {
const request = spec.buildRequests(bidRequests);
const response = spec.interpretResponse(bidResponses, request);
expect(response).to.be.an('array').to.have.lengthOf(1);

let bid = response[0];
expect(bid).to.have.property('requestId', 'bid000');
expect(bid).to.have.property('currency', 'USD');
expect(bid).to.have.property('cpm', 0.99);
expect(bid).to.have.property('creativeId', 'cr-65981');
expect(bid).to.have.property('width', 300);
expect(bid).to.have.property('height', 250);
expect(bid).to.have.property('ttl', 300);
expect(bid).to.have.property('ad', '<img src"http://www.imgurl.com" />');
});
});
});
});

0 comments on commit f1f18e1

Please sign in to comment.