Skip to content

Commit 726d0a4

Browse files
cary67idettman
authored andcommitted
Trafficroots Bid Adapter Submission (prebid#2993)
* Trafficroots bid adapter submission. Thank you! * unit test * typo fix
1 parent 005df8b commit 726d0a4

File tree

3 files changed

+315
-0
lines changed

3 files changed

+315
-0
lines changed

modules/trafficrootsBidAdapter.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import {registerBidder} from 'src/adapters/bidderFactory';
2+
import * as utils from 'src/utils';
3+
4+
const TR_BIDDER_CODE = 'trafficroots';
5+
const TR_CURRENCY = 'USD';
6+
const TR_DEFAULT_BID_URL = '//service.trafficroots.com/prebid';
7+
const TR_TTL = 60;
8+
9+
const LOCATION_PARAM_NAME = 'siteurl';
10+
const ID_PARAM_NAME = 'id';
11+
const IFRAME_PARAM_NAME = 'if';
12+
const ZONE_ID_PARAM_NAME = 'zoneId';
13+
const SIZE_PARAM_NAME = 'size';
14+
const KEYWORDS_PARAM_NAME = 'keywords';
15+
const MOBILE_PARAM_NAME = 'mobile';
16+
const TRID_PARAM_NAME = 'trid';
17+
18+
const ARRAY_PARAM_SEPARATOR = ';';
19+
const ARRAY_SIZE_SEPARATOR = ',';
20+
const SIZE_SEPARATOR = 'x';
21+
const IS_MOBILE = window.navigator.userAgent.toLowerCase().includes('mobi');
22+
23+
let keywords = () => {
24+
let clean = input => {
25+
return input.replace(/\W/g, ' ').replace(/[ ]{2,}/g, ' ').trim();
26+
};
27+
let meta = name => {
28+
let tag = document.querySelector("meta[name='" + name + "']");
29+
return (tag !== null) ? tag.getAttribute('content') : '';
30+
};
31+
return encodeURIComponent(
32+
clean(
33+
meta('keywords') + ' ' + meta('description') + ' ' + document.title
34+
)
35+
).substring(0, 400);
36+
};
37+
38+
export const spec = {
39+
code: TR_BIDDER_CODE,
40+
isBidRequestValid: function(bid) {
41+
return bid.params && !!bid.params.zoneId;
42+
},
43+
44+
buildRequests: function(validBidRequests, bidderRequest) {
45+
let deliveryUrl = '';
46+
const idParams = [];
47+
const sizeParams = [];
48+
const zoneIds = [];
49+
let trid = '';
50+
if (window.localStorage) {
51+
try {
52+
var myid = window.localStorage.getItem('trafficroots:trid');
53+
if (myid) {
54+
trid = myid;
55+
}
56+
} catch (ex) {
57+
}
58+
}
59+
utils._each(validBidRequests, function(bid) {
60+
if (!deliveryUrl && typeof bid.params.deliveryUrl === 'string') {
61+
deliveryUrl = bid.params.deliveryUrl;
62+
}
63+
idParams.push(bid.bidId);
64+
sizeParams.push(bid.sizes.map(size => size.join(SIZE_SEPARATOR)).join(ARRAY_SIZE_SEPARATOR));
65+
zoneIds.push(bid.params.zoneId);
66+
});
67+
68+
if (!deliveryUrl) {
69+
deliveryUrl = TR_DEFAULT_BID_URL;
70+
}
71+
72+
let data = {
73+
[IFRAME_PARAM_NAME]: 0,
74+
[LOCATION_PARAM_NAME]: utils.getTopWindowUrl(),
75+
[SIZE_PARAM_NAME]: sizeParams.join(ARRAY_PARAM_SEPARATOR),
76+
[ID_PARAM_NAME]: idParams.join(ARRAY_PARAM_SEPARATOR),
77+
[ZONE_ID_PARAM_NAME]: zoneIds.join(ARRAY_PARAM_SEPARATOR),
78+
[MOBILE_PARAM_NAME]: IS_MOBILE,
79+
[KEYWORDS_PARAM_NAME]: decodeURIComponent(keywords()),
80+
[TRID_PARAM_NAME]: trid
81+
};
82+
83+
if (bidderRequest && bidderRequest.gdprConsent) {
84+
data.gdpr = {
85+
applies: bidderRequest.gdprConsent.gdprApplies,
86+
consent: bidderRequest.gdprConsent.consentString
87+
};
88+
}
89+
90+
return {
91+
method: 'GET',
92+
url: deliveryUrl,
93+
data: data
94+
};
95+
},
96+
97+
interpretResponse: function(serverResponses, request) {
98+
const bidResponses = [];
99+
var tridSet = false;
100+
utils._each(serverResponses.body, function(response) {
101+
if (!tridSet) {
102+
try {
103+
if (window.localStorage) {
104+
window.localStorage.setItem('trafficroots:trid', response.trid);
105+
tridSet = true;
106+
}
107+
} catch (ex) {}
108+
}
109+
if (response.cpm > 0) {
110+
const bidResponse = {
111+
requestId: response.id,
112+
creativeId: response.id,
113+
adId: response.id,
114+
cpm: response.cpm,
115+
width: response.width,
116+
height: response.height,
117+
currency: TR_CURRENCY,
118+
netRevenue: true,
119+
ttl: TR_TTL,
120+
ad: response.ad
121+
};
122+
bidResponses.push(bidResponse);
123+
}
124+
});
125+
return bidResponses;
126+
}
127+
};
128+
129+
registerBidder(spec);

modules/trafficrootsBidAdapter.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Overview
2+
3+
Module Name: Trafficroots Bid Adapter
4+
5+
Module Type: Bidder Adapter
6+
7+
Maintainer: [email protected]
8+
9+
# Description
10+
11+
Module that connects to Trafficroots demand sources
12+
13+
# Test Parameters
14+
```javascript
15+
16+
var adUnits = [
17+
{
18+
code: 'test-div',
19+
sizes: [[300, 250],[300,600]], // a display size
20+
bids: [
21+
{
22+
bidder: 'trafficroots',
23+
params: {
24+
zoneId: 'aa0444af31',
25+
deliveryUrl: location.protocol + '//service.trafficroots.com/prebid'
26+
}
27+
},{
28+
bidder: 'trafficroots',
29+
params: {
30+
zoneId: '8f527a4835',
31+
deliveryUrl: location.protocol + '//service.trafficroots.com/prebid'
32+
}
33+
}
34+
]
35+
}
36+
];
37+
```
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { expect } from 'chai';
2+
import { spec } from 'modules/trafficrootsBidAdapter';
3+
4+
describe('trafficrootsAdapterTests', () => {
5+
describe('bidRequestValidity', () => {
6+
it('bidRequest with zoneId and deliveryUrl params', () => {
7+
expect(spec.isBidRequestValid({
8+
bidder: 'trafficroots',
9+
params: {
10+
zoneId: 'aa0444af31',
11+
deliveryUrl: 'https://service.trafficroosts.com/prebid'
12+
}
13+
})).to.equal(true);
14+
});
15+
16+
it('bidRequest with only zoneId', () => {
17+
expect(spec.isBidRequestValid({
18+
bidder: 'trafficroots',
19+
params: {
20+
zoneId: '8f527a4835'
21+
}
22+
})).to.equal(true);
23+
});
24+
25+
it('bidRequest with only deliveryUrl', () => {
26+
expect(spec.isBidRequestValid({
27+
bidder: 'trafficroots',
28+
params: {
29+
deliveryUrl: 'https://service.trafficroosts.com/prebid'
30+
}
31+
})).to.equal(false);
32+
});
33+
});
34+
35+
describe('bidRequest', () => {
36+
const bidRequests = [{
37+
'bidder': 'trafficroots',
38+
'bidId': '29fa5c08928bde',
39+
'params': {
40+
'zoneId': 'aa0444af31',
41+
},
42+
'adUnitCode': 'div-gpt-ad-1460505748561-0',
43+
'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec',
44+
'sizes': [[300, 250], [300, 600]],
45+
'bidderRequestId': '418b37f85e772c',
46+
'auctionId': '18fd8b8b0bd757'
47+
}, {
48+
'bidder': 'trafficroots',
49+
'bidId': '29fa5c08928bde',
50+
'params': {
51+
'zoneId': '8f527a4835',
52+
'deliveryUrl': 'https://service.trafficroosts.com/prebid'
53+
},
54+
'adUnitCode': 'div-gpt-ad-1460505748561-0',
55+
'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec',
56+
'sizes': [[300, 250], [300, 600]],
57+
'bidderRequestId': '418b37f85e772c',
58+
'auctionId': '18fd8b8b0bd757'
59+
}];
60+
61+
it('bidRequest method', () => {
62+
const request = spec.buildRequests(bidRequests);
63+
expect(request.method).to.equal('GET');
64+
});
65+
66+
it('bidRequest url', () => {
67+
const request = spec.buildRequests(bidRequests);
68+
expect(request.url).to.match(new RegExp(`${bidRequests[1].params.deliveryUrl}`));
69+
});
70+
71+
it('bidRequest data', () => {
72+
const request = spec.buildRequests(bidRequests);
73+
expect(request.data).to.exists;
74+
});
75+
76+
it('bidRequest zoneIds', () => {
77+
const request = spec.buildRequests(bidRequests);
78+
expect(request.data.zoneId).to.equal('aa0444af31;8f527a4835');
79+
});
80+
81+
it('bidRequest gdpr consent', () => {
82+
const consentString = 'consentString';
83+
const bidderRequest = {
84+
bidderCode: 'trafficroots',
85+
auctionId: '18fd8b8b0bd757',
86+
bidderRequestId: '418b37f85e772c',
87+
timeout: 3000,
88+
gdprConsent: {
89+
consentString: consentString,
90+
gdprApplies: true
91+
}
92+
};
93+
94+
const request = spec.buildRequests(bidRequests, bidderRequest);
95+
96+
expect(request.data.gdpr).to.exist;
97+
expect(request.data.gdpr.applies).to.exist.and.to.be.true;
98+
expect(request.data.gdpr.consent).to.exist.and.to.equal(consentString);
99+
});
100+
});
101+
102+
describe('interpretResponse', () => {
103+
const bidRequest = [{
104+
'bidder': 'trafficroots',
105+
'bidId': '29fa5c08928bde',
106+
'params': {
107+
'zoneId': 'aa0444af31',
108+
},
109+
'adUnitCode': 'div-gpt-ad-1460505748561-0',
110+
'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec',
111+
'sizes': [[300, 250], [300, 600]],
112+
'bidderRequestId': '418b37f85e772c',
113+
'auctionId': '18fd8b8b0bd757'
114+
}];
115+
116+
const bidResponse = {
117+
body: [{
118+
'id': 'div-gpt-ad-1460505748561-0',
119+
'ad': 'test ad',
120+
'width': 320,
121+
'height': 250,
122+
'cpm': 5.2
123+
}],
124+
headers: {}
125+
};
126+
127+
it('required keys', () => {
128+
const result = spec.interpretResponse(bidResponse, bidRequest);
129+
130+
let requiredKeys = [
131+
'requestId',
132+
'creativeId',
133+
'adId',
134+
'cpm',
135+
'width',
136+
'height',
137+
'currency',
138+
'netRevenue',
139+
'ttl',
140+
'ad'
141+
];
142+
143+
let resultKeys = Object.keys(result[0]);
144+
resultKeys.forEach(function(key) {
145+
expect(requiredKeys.indexOf(key) !== -1).to.equal(true);
146+
});
147+
})
148+
});
149+
});

0 commit comments

Comments
 (0)