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

Add Automatad Bid Adapter #5017

Merged
merged 17 commits into from
Apr 7, 2020
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
123 changes: 123 additions & 0 deletions modules/automatadBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {registerBidder} from '../src/adapters/bidderFactory.js'
import * as utils from '../src/utils.js'
import {BANNER} from '../src/mediaTypes.js'
import {ajax} from '../src/ajax.js'

const BIDDER = 'automatad'

const ENDPOINT_URL = 'https://rtb2.automatad.com/ortb2'

const DEFAULT_BID_TTL = 30
const DEFAULT_CURRENCY = 'USD'
const DEFAULT_NET_REVENUE = true

export const spec = {
code: BIDDER,
aliases: ['atd'],
supportedMediaTypes: [BANNER],

isBidRequestValid: function (bid) {
// will receive request bid. check if have necessary params for bidding
return (bid && bid.hasOwnProperty('params') && bid.params.hasOwnProperty('siteId') && bid.params.hasOwnProperty('placementId') && bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty('banner'))
},

buildRequests: function (validBidRequests, bidderRequest) {
if (!validBidRequests || !bidderRequest) {
return
}

const siteId = validBidRequests[0].params.siteId
const placementId = validBidRequests[0].params.placementId

const impressions = validBidRequests.map(bidRequest => ({
id: bidRequest.bidId,
banner: {
format: bidRequest.sizes.map(sizeArr => ({
w: sizeArr[0],
h: sizeArr[1],
}))
},
}))

// params from bid request
const openrtbRequest = {
id: validBidRequests[0].auctionId,
imp: impressions,
site: {
id: siteId,
placement: placementId,
domain: window.location.hostname,
page: window.location.href,
ref: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer || null : null,
},
}

const payloadString = JSON.stringify(openrtbRequest)
return {
method: 'POST',
url: ENDPOINT_URL + '/resp',
data: payloadString,
options: {
contentType: 'application/json',
withCredentials: false,
crossOrigin: true,
},
}
},

interpretResponse: function (serverResponse, request) {
const bidResponses = []
const response = (serverResponse || {}).body

if (response && response.seatbid && response.seatbid.length === 1 && response.seatbid[0].bid && response.seatbid[0].bid.length) {
response.seatbid[0].bid.forEach(bid => {
bidResponses.push({
requestId: bid.impid,
cpm: bid.price,
ad: bid.adm,
adDomain: bid.adomain[0],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you fix to meta.advertiserDomains?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@patmmccann I have created a pull request for this change #6509

currency: DEFAULT_CURRENCY,
ttl: DEFAULT_BID_TTL,
creativeId: bid.crid,
width: bid.w,
height: bid.h,
netRevenue: DEFAULT_NET_REVENUE,
nurl: bid.nurl,
})
})
} else {
utils.logInfo('automatad :: no valid responses to interpret')
}

return bidResponses
},
getUserSyncs: function(syncOptions, serverResponse) {
return [{
type: 'iframe',
url: 'https://rtb2.automatad.com/ortb2/async_usersync'
}]
},
onBidWon: function(bid) {
msm0504 marked this conversation as resolved.
Show resolved Hide resolved
if (!bid.nurl) { return }
const winUrl = bid.nurl.replace(
/\$\{AUCTION_PRICE\}/,
bid.cpm
).replace(
/\$\{AUCTION_IMP_ID\}/,
bid.requestId
).replace(
/\$\{AUCTION_CURRENCY\}/,
bid.currency
).replace(
/\$\{AUCTION_ID\}/,
bid.auctionId
)
spec.ajaxCall(winUrl, null)
return true
},
ajaxCall: function(endpoint, data) {
ajax(endpoint, data)
},

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

```
Module Name: Automatad Bid Adapter
Module Type: Bidder Adapter
Maintainer: tech@automatad.com
```

# Description

Connects to automatad exchange for bids.

automatad bid adapter supports Banner ads.

# Test Parameters
```
var adUnits = [
{
code: 'banner-ad-div',
mediaTypes: {
msm0504 marked this conversation as resolved.
Show resolved Hide resolved
banner: {
sizes: [[300, 250], [300,600]]
}
},
bids: [{
bidder: 'automatad',
params: {
siteId: 'someValue',
placementId: 'someValue'
}
}]
}
];
```
144 changes: 144 additions & 0 deletions test/spec/modules/automatadBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { expect } from 'chai'
import { spec } from 'modules/automatadBidAdapter.js'
import { newBidder } from 'src/adapters/bidderFactory.js'

describe('automatadBidAdapter', function () {
const adapter = newBidder(spec)

let bidRequest = {
bidder: 'automatad',
params: {siteId: '123ad', placementId: '123abc345'},
mediaTypes: {
banner: {
sizes: [[300, 600]],
}
},
adUnitCode: 'some-ad-unit-code',
transactionId: '1465569e-52cc-4c36-88a1-7174cfef4b44',
sizes: [[300, 600]],
bidId: '123abc',
bidderRequestId: '3213887463c059',
auctionId: 'abc-123',
src: 'client',
bidRequestsCount: 1
}

let expectedResponse = [{
'body': {
'id': 'abc-123',
'seatbid': [
{
'bid': [
{
'adm': '<!-- creative code -->',
'adomain': [
'someAdDomain'
],
'crid': 123,
'h': 600,
'id': 'bid1',
'impid': '1',
'nurl': 'https://example/win',
'price': 0.5,
'w': 300
}
]
}
]
}
}]

describe('codes', function () {
it('should return a bidder code of automatad', function () {
expect(spec.code).to.equal('automatad')
})
it('should alias atd', function () {
expect(spec.aliases.length > 0 && spec.aliases[0] === 'atd').to.be.true
})
})

describe('isBidRequestValid', function () {
let inValidBid = Object.assign({}, bidRequest)
delete inValidBid.params
it('should return true if all params present', function () {
expect(spec.isBidRequestValid(bidRequest)).to.equal(true)
})

it('should return false if any parameter missing', function () {
expect(spec.isBidRequestValid(inValidBid)).to.be.false
})
})

describe('buildRequests', function () {
let req = spec.buildRequests([ bidRequest ], { refererInfo: { } })
let rdata

it('should return request object', function () {
expect(req).to.not.be.null
})

it('should build request data', function () {
expect(req.data).to.not.be.null
})

it('should include one request', function () {
rdata = JSON.parse(req.data)
expect(rdata.imp.length).to.equal(1)
})

it('should include media types', function () {
let r = rdata.imp[0]
expect(r.media_types !== null).to.be.true
})

it('should include all publisher params', function () {
let r = rdata.imp[0]
expect(r.siteID !== null && r.placementID !== null).to.be.true
})
})

describe('interpretResponse', function () {
it('should get the correct bid response', function () {
let result = spec.interpretResponse(expectedResponse[0])
expect(result).to.be.an('array').that.is.not.empty
})

it('handles empty bid response', function () {
let response = {
body: ''
}
let result = spec.interpretResponse(response)
expect(result.length).to.equal(0)
})
})

describe('getUserSyncs', function () {
it('should return iframe sync', function () {
let sync = spec.getUserSyncs()
expect(sync.length).to.equal(1)
expect(sync[0].type === 'iframe')
expect(typeof sync[0].url === 'string')
})
})

describe('onBidWon', function () {
let serverResponses = spec.interpretResponse(expectedResponse[0])
let wonbid = serverResponses[0]
let ajaxStub

beforeEach(() => {
ajaxStub = sinon.stub(spec, 'ajaxCall')
})

afterEach(() => {
ajaxStub.restore()
})

it('Returns true is nurl is good/not blank', function () {
expect(wonbid.nurl).to.not.equal('')
expect(spec.onBidWon(wonbid)).to.be.true
expect(ajaxStub.calledOnce).to.equal(true)
expect(ajaxStub.firstCall.args[0].indexOf('https://')).to.equal(0)
})
})
})