-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Add adapter for IAS #2056
Merged
matthewlane
merged 6 commits into
prebid:master
from
integralads:PET-201_add-ias-adapter
Feb 8, 2018
Merged
Add adapter for IAS #2056
Changes from 5 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
e8fae4c
PET-201: got a working version
katzhang-ias a3ec930
PET-201: encoded query string
katzhang-ias 40a31f2
PET-201: added unit tests
katzhang-ias 17602c6
PET-201: added missing keyword
katzhang-ias fbba68c
PET-201: corrected coding styles
katzhang-ias e9e1a76
PET-201: decreased cpm so real bidders could win per code review
katzhang-ias File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import * as utils from 'src/utils'; | ||
import { registerBidder } from 'src/adapters/bidderFactory'; | ||
|
||
const BIDDER_CODE = 'ias'; | ||
|
||
function isBidRequestValid(bid) { | ||
const { pubId, adUnitPath } = bid.params; | ||
return !!(pubId && adUnitPath); | ||
} | ||
|
||
/** | ||
* Converts GPT-style size array into a string | ||
* @param {Array} sizes: list of GPT-style sizes, e.g. [[300, 250], [300, 300]] | ||
* @return {String} a string containing sizes, e.g. '[300.250,300.300]' | ||
*/ | ||
function stringifySlotSizes(sizes) { | ||
let result = ''; | ||
if (utils.isArray(sizes)) { | ||
result = sizes.reduce((acc, size) => { | ||
acc.push(size.join('.')); | ||
return acc; | ||
}, []); | ||
result = '[' + result.join(',') + ']'; | ||
} | ||
return result; | ||
} | ||
|
||
function stringifySlot(bidRequest) { | ||
const id = bidRequest.adUnitCode; | ||
const ss = stringifySlotSizes(bidRequest.sizes); | ||
const p = bidRequest.params.adUnitPath; | ||
const slot = { id, ss, p }; | ||
const keyValues = utils.getKeys(slot).map(function(key) { | ||
return [key, slot[key]].join(':'); | ||
}); | ||
return '{' + keyValues.join(',') + '}'; | ||
} | ||
|
||
function stringifyWindowSize() { | ||
return [window.innerWidth || -1, window.innerHeight || -1].join('.'); | ||
} | ||
|
||
function stringifyScreenSize() { | ||
return [(window.screen && window.screen.width) || -1, (window.screen && window.screen.height) || -1].join('.'); | ||
} | ||
|
||
function buildRequests(bidRequests) { | ||
const IAS_HOST = '//pixel.adsafeprotected.com/services/pub'; | ||
const anId = bidRequests[0].params.pubId; | ||
|
||
let queries = []; | ||
queries.push(['anId', anId]); | ||
queries = queries.concat(bidRequests.reduce(function(acc, request) { | ||
acc.push(['slot', stringifySlot(request)]); | ||
return acc; | ||
}, [])); | ||
|
||
queries.push(['wr', stringifyWindowSize()]); | ||
queries.push(['sr', stringifyScreenSize()]); | ||
|
||
const queryString = encodeURI(queries.map(qs => qs.join('=')).join('&')); | ||
|
||
return { | ||
method: 'GET', | ||
url: IAS_HOST, | ||
data: queryString, | ||
bidRequest: bidRequests[0] | ||
} | ||
} | ||
|
||
function getPageLevelKeywords(response) { | ||
let result = {}; | ||
shallowMerge(result, response.brandSafety); | ||
result.fr = response.fr; | ||
return result; | ||
} | ||
|
||
function shallowMerge(dest, src) { | ||
utils.getKeys(src).reduce((dest, srcKey) => { | ||
dest[srcKey] = src[srcKey]; | ||
return dest; | ||
}, dest); | ||
} | ||
|
||
function interpretResponse(serverResponse, request) { | ||
const iasResponse = serverResponse.body; | ||
const bidResponses = []; | ||
|
||
// Keys in common bid response are not used; | ||
// Necessary to get around with prebid's common bid response check | ||
const commonBidResponse = { | ||
requestId: request.bidRequest.bidId, | ||
cpm: 1, | ||
width: 100, | ||
height: 200, | ||
creativeId: 434, | ||
dealId: 42, | ||
currency: 'usd', | ||
netRevenue: true, | ||
ttl: 360 | ||
}; | ||
|
||
shallowMerge(commonBidResponse, getPageLevelKeywords(iasResponse)); | ||
commonBidResponse.slots = iasResponse.slots; | ||
bidResponses.push(commonBidResponse); | ||
return bidResponses; | ||
} | ||
|
||
export const spec = { | ||
code: BIDDER_CODE, | ||
aliases: [], | ||
isBidRequestValid: isBidRequestValid, | ||
buildRequests: buildRequests, | ||
interpretResponse: interpretResponse | ||
}; | ||
|
||
registerBidder(spec); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Overview | ||
|
||
``` | ||
Module Name: Integral Ad Science(IAS) Bidder Adapter | ||
Module Type: Bidder Adapter | ||
Maintainer: kat@integralads.com | ||
``` | ||
|
||
# Description | ||
|
||
This module is an integration with prebid.js with an IAS product, pet.js. It is not a bidder per se but works in a similar way: retrieve data that publishers might be interested in setting keyword targeting. | ||
|
||
# Test Parameters | ||
``` | ||
var adUnits = [ | ||
{ | ||
code: 'ias-dfp-test-async', | ||
sizes: [[300, 250]], // a display size | ||
bids: [ | ||
{ | ||
bidder: "ias", | ||
params: { | ||
pubId: '99', | ||
adUnitPath: '/57514611/news.com' | ||
} | ||
} | ||
] | ||
} | ||
]; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
import { expect } from 'chai'; | ||
import { spec } from 'modules/iasBidAdapter'; | ||
|
||
describe('iasBidAdapter is an adapter that', () => { | ||
it('has the correct bidder code', () => { | ||
expect(spec.code).to.equal('ias'); | ||
}); | ||
describe('has a method `isBidRequestValid` that', () => { | ||
it('exists', () => { | ||
expect(spec.isBidRequestValid).to.be.a('function'); | ||
}); | ||
it('returns false if bid params misses `pubId`', () => { | ||
expect(spec.isBidRequestValid( | ||
{ | ||
params: { | ||
adUnitPath: 'someAdUnitPath' | ||
} | ||
})).to.equal(false); | ||
}); | ||
it('returns false if bid params misses `adUnitPath`', () => { | ||
expect(spec.isBidRequestValid( | ||
{ | ||
params: { | ||
pubId: 'somePubId' | ||
} | ||
})).to.equal(false); | ||
}); | ||
it('returns true otherwise', () => { | ||
expect(spec.isBidRequestValid( | ||
{ | ||
params: { | ||
adUnitPath: 'someAdUnitPath', | ||
pubId: 'somePubId', | ||
someOtherParam: 'abc' | ||
} | ||
})).to.equal(true); | ||
}); | ||
}); | ||
|
||
describe('has a method `buildRequests` that', () => { | ||
it('exists', () => { | ||
expect(spec.buildRequests).to.be.a('function'); | ||
}); | ||
describe('given bid requests, returns a `ServerRequest` instance that', () => { | ||
let bidRequests, IAS_HOST; | ||
beforeEach(() => { | ||
IAS_HOST = '//pixel.adsafeprotected.com/services/pub'; | ||
bidRequests = [ | ||
{ | ||
adUnitCode: 'one-div-id', | ||
auctionId: 'someAuctionId', | ||
bidId: 'someBidId', | ||
bidder: 'ias', | ||
bidderRequestId: 'someBidderRequestId', | ||
params: { | ||
pubId: '1234', | ||
adUnitPath: '/a/b/c' | ||
}, | ||
sizes: [ | ||
[10, 20], | ||
[300, 400] | ||
], | ||
transactionId: 'someTransactionId' | ||
}, | ||
{ | ||
adUnitCode: 'two-div-id', | ||
auctionId: 'someAuctionId', | ||
bidId: 'someBidId', | ||
bidder: 'ias', | ||
bidderRequestId: 'someBidderRequestId', | ||
params: { | ||
pubId: '1234', | ||
adUnitPath: '/d/e/f' | ||
}, | ||
sizes: [ | ||
[50, 60] | ||
], | ||
transactionId: 'someTransactionId' | ||
} | ||
]; | ||
}); | ||
it('has property `method` of `GET`', () => { | ||
expect(spec.buildRequests(bidRequests)).to.deep.include({ | ||
method: 'GET' | ||
}); | ||
}); | ||
it('has property `url` to be the correct IAS endpoint', () => { | ||
expect(spec.buildRequests(bidRequests)).to.deep.include({ | ||
url: IAS_HOST | ||
}); | ||
}); | ||
describe('has property `data` that is an encode query string containing information such as', () => { | ||
let val; | ||
const ANID_PARAM = 'anId'; | ||
const SLOT_PARAM = 'slot'; | ||
const SLOT_ID_PARAM = 'id'; | ||
const SLOT_SIZE_PARAM = 'ss'; | ||
const SLOT_AD_UNIT_PATH_PARAM = 'p'; | ||
|
||
beforeEach(() => val = decodeURI(spec.buildRequests(bidRequests).data)); | ||
it('publisher id', () => { | ||
expect(val).to.have.string(`${ANID_PARAM}=1234`); | ||
}); | ||
it('ad slot`s id, size and ad unit path', () => { | ||
expect(val).to.have.string(`${SLOT_PARAM}={${SLOT_ID_PARAM}:one-div-id,${SLOT_SIZE_PARAM}:[10.20,300.400],${SLOT_AD_UNIT_PATH_PARAM}:/a/b/c}`); | ||
expect(val).to.have.string(`${SLOT_PARAM}={${SLOT_ID_PARAM}:two-div-id,${SLOT_SIZE_PARAM}:[50.60],${SLOT_AD_UNIT_PATH_PARAM}:/d/e/f}`); | ||
}); | ||
it('window size', () => { | ||
expect(val).to.match(/.*wr=[0-9]*\.[0-9]*/); | ||
}); | ||
it('screen size', () => { | ||
expect(val).to.match(/.*sr=[0-9]*\.[0-9]*/); | ||
}); | ||
}); | ||
it('has property `bidRequest` that is the first passed in bid request', () => { | ||
expect(spec.buildRequests(bidRequests)).to.deep.include({ | ||
bidRequest: bidRequests[0] | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('has a method `interpretResponse` that', () => { | ||
it('exists', () => { | ||
expect(spec.interpretResponse).to.be.a('function'); | ||
}); | ||
describe('returns a list of bid response that', () => { | ||
let bidResponse, slots; | ||
beforeEach(() => { | ||
const request = { | ||
bidRequest: { | ||
bidId: '102938' | ||
} | ||
}; | ||
slots = {}; | ||
slots['test-div-id'] = { | ||
id: '1234', | ||
vw: ['60', '70'] | ||
}; | ||
slots['test-div-id-two'] = { | ||
id: '5678', | ||
vw: ['80', '90'] | ||
}; | ||
const serverResponse = { | ||
body: { | ||
brandSafety: { | ||
adt: 'adtVal', | ||
alc: 'alcVal', | ||
dlm: 'dlmVal', | ||
drg: 'drgVal', | ||
hat: 'hatVal', | ||
off: 'offVal', | ||
vio: 'vioVal' | ||
}, | ||
fr: 'false', | ||
slots: slots | ||
}, | ||
headers: {} | ||
}; | ||
bidResponse = spec.interpretResponse(serverResponse, request); | ||
}); | ||
it('has IAS keyword `adt` as property', () => { | ||
expect(bidResponse[0]).to.deep.include({ adt: 'adtVal' }); | ||
}); | ||
it('has IAS keyword `alc` as property', () => { | ||
expect(bidResponse[0]).to.deep.include({ alc: 'alcVal' }); | ||
}); | ||
it('has IAS keyword `dlm` as property', () => { | ||
expect(bidResponse[0]).to.deep.include({ dlm: 'dlmVal' }); | ||
}); | ||
it('has IAS keyword `drg` as property', () => { | ||
expect(bidResponse[0]).to.deep.include({ drg: 'drgVal' }); | ||
}); | ||
it('has IAS keyword `hat` as property', () => { | ||
expect(bidResponse[0]).to.deep.include({ hat: 'hatVal' }); | ||
}); | ||
it('has IAS keyword `off` as property', () => { | ||
expect(bidResponse[0]).to.deep.include({ off: 'offVal' }); | ||
}); | ||
it('has IAS keyword `vio` as property', () => { | ||
expect(bidResponse[0]).to.deep.include({ vio: 'vioVal' }); | ||
}); | ||
it('has IAS keyword `fr` as property', () => { | ||
expect(bidResponse[0]).to.deep.include({ fr: 'false' }); | ||
}); | ||
it('has property `slots`', () => { | ||
expect(bidResponse[0]).to.deep.include({ slots: slots }); | ||
}); | ||
}); | ||
}); | ||
}); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be changed to
0.01
? The hardcoded1
may prevent bidders with real cpms from winning