Skip to content

Commit

Permalink
Add Automatad Bid Adapter (#5017)
Browse files Browse the repository at this point in the history
* added automatad bid adapter

* added automatad bid adapter readme

* added automatad bidder adapter unit test

* updated maintainer email id for automatad adapter

* refactored automatadBidAdapter js

* refactored automatadBidAdapter unit test

* refactored automatadBidAdapter unit test

* added usersync code to automatad bid adapter

* Added unit test for onBidWon in automatadBidAdapter_spec

* removed trailing spaces

* removed trailing space

* changes for getUserSync function

* lint error fixes

* updated usersync url

* additional test for onBidWon function added

* added ajax stub in test
  • Loading branch information
kanchika-kapoor authored Apr 7, 2020
1 parent 73d5861 commit 21d1f53
Show file tree
Hide file tree
Showing 3 changed files with 301 additions and 0 deletions.
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],
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) {
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: {
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)
})
})
})

0 comments on commit 21d1f53

Please sign in to comment.