Skip to content

Commit 5112950

Browse files
EpanchinEjsnellbaker
authored andcommitted
Added LoopMe Adapter (prebid#3586)
* Added LoopMe Adapter * Replace Object.entries with entries
1 parent d36d458 commit 5112950

File tree

3 files changed

+229
-0
lines changed

3 files changed

+229
-0
lines changed

modules/loopmeBidAdapter.js

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import * as utils from 'src/utils';
2+
import { registerBidder } from 'src/adapters/bidderFactory';
3+
import { BANNER } from 'src/mediaTypes';
4+
5+
const LOOPME_ENDPOINT = 'https://loopme.me/api/hb';
6+
7+
const entries = (obj) => {
8+
let output = [];
9+
for (let key in obj) {
10+
if (obj.hasOwnProperty(key)) {
11+
output.push([key, obj[key]])
12+
}
13+
}
14+
return output;
15+
}
16+
17+
export const spec = {
18+
code: 'loopme',
19+
supportedMediaTypes: [BANNER],
20+
/**
21+
* @param {object} bid
22+
* @return boolean
23+
*/
24+
isBidRequestValid: function(bid) {
25+
if (typeof bid.params !== 'object') {
26+
return false;
27+
}
28+
29+
return !!bid.params.ak;
30+
},
31+
/**
32+
* @param {BidRequest[]} bidRequests
33+
* @param bidderRequest
34+
* @return ServerRequest[]
35+
*/
36+
buildRequests: function(bidRequests, bidderRequest) {
37+
return bidRequests.map(bidRequest => {
38+
bidRequest.startTime = new Date().getTime();
39+
let payload = bidRequest.params;
40+
41+
if (bidderRequest && bidderRequest.gdprConsent) {
42+
payload.user_consent = bidderRequest.gdprConsent.consentString;
43+
}
44+
45+
let queryString = entries(payload)
46+
.map(item => `${item[0]}=${encodeURI(item[1])}`)
47+
.join('&');
48+
49+
const sizes =
50+
'&sizes=' +
51+
utils
52+
.getAdUnitSizes(bidRequest)
53+
.map(size => `${size[0]}x${size[1]}`)
54+
.join('&sizes=');
55+
56+
queryString = `${queryString}${sizes}`;
57+
58+
return {
59+
method: 'GET',
60+
url: `${LOOPME_ENDPOINT}`,
61+
options: { withCredentials: false },
62+
bidId: bidRequest.bidId,
63+
data: queryString
64+
};
65+
});
66+
},
67+
/**
68+
* @param {*} responseObj
69+
* @param {BidRequest} bidRequest
70+
* @return {Bid[]} An array of bids which
71+
*/
72+
interpretResponse: function(response = {}, bidRequest) {
73+
const responseObj = response.body;
74+
75+
if (
76+
responseObj == null ||
77+
typeof responseObj !== 'object' ||
78+
!responseObj.hasOwnProperty('ad')
79+
) {
80+
return [];
81+
}
82+
83+
return [
84+
{
85+
requestId: bidRequest.bidId,
86+
cpm: responseObj.cpm,
87+
width: responseObj.width,
88+
height: responseObj.height,
89+
ad: responseObj.ad,
90+
ttl: responseObj.ttl,
91+
currency: responseObj.currency,
92+
creativeId: responseObj.creativeId,
93+
dealId: responseObj.dealId,
94+
netRevenue: responseObj.netRevenue
95+
}
96+
];
97+
}
98+
};
99+
registerBidder(spec);

modules/loopmeBidAdapter.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Overview
2+
3+
```
4+
Module Name: LoopMe Bid Adapter
5+
Module Type: Bidder Adapter
6+
Maintainer: [email protected]
7+
```
8+
9+
# Description
10+
11+
Connect to LoopMe's exchange for bids.
12+
13+
# Test Parameters
14+
```
15+
var adUnits = [{
16+
code: 'test-div',
17+
mediaTypes: {
18+
banner: {
19+
sizes: [[300, 250], [300,600]],
20+
}
21+
},
22+
bids: [{
23+
bidder: 'loopme',
24+
params: {
25+
ak: 'cc885e3acc'
26+
}
27+
}]
28+
}];
29+
```
+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { expect } from 'chai';
2+
import { spec } from '../../../modules/loopmeBidAdapter';
3+
import * as utils from 'src/utils';
4+
5+
describe('LoopMeAdapter', function () {
6+
const bidRequests = [{
7+
bidder: 'loopme',
8+
params: {
9+
ak: 'b510d5bcda'
10+
},
11+
mediaTypes: {
12+
banner: {
13+
sizes: [[300, 250]]
14+
}
15+
},
16+
adUnitCode: 'ad-1',
17+
bidId: '2652ca954bce9'
18+
}];
19+
20+
describe('isBidRequestValid', function () {
21+
it('should return true if the ak parameter is present', function () {
22+
expect(spec.isBidRequestValid(bidRequests[0])).to.be.true;
23+
});
24+
25+
it('should return false if the ak parameter is not present', function () {
26+
let bidRequest = utils.deepClone(bidRequests[0]);
27+
delete bidRequest.params.ak;
28+
expect(spec.isBidRequestValid(bidRequest)).to.be.false;
29+
});
30+
31+
it('should return false if the params object is not present', function () {
32+
let bidRequest = utils.deepClone(bidRequests);
33+
delete bidRequest[0].params;
34+
expect(spec.isBidRequestValid(bidRequest)).to.be.false;
35+
});
36+
});
37+
38+
describe('buildRequests', function () {
39+
it('should generate a valid single GET request for multiple bid requests', function () {
40+
const request = spec.buildRequests(bidRequests)[0];
41+
expect(request.method).to.equal('GET');
42+
expect(request.url).to.equal('https://loopme.me/api/hb');
43+
expect(request.bidId).to.equal('2652ca954bce9');
44+
expect(request.data).to.exist;
45+
46+
const requestData = request.data;
47+
expect(requestData).to.contain('ak=b510d5bcda');
48+
expect(requestData).to.contain('sizes=300x250');
49+
});
50+
51+
it('should add GDPR data to request if available', function () {
52+
const bidderRequest = {
53+
gdprConsent: {
54+
consentString: 'AAABBB'
55+
}
56+
};
57+
const request = spec.buildRequests(bidRequests, bidderRequest)[0];
58+
const requestData = request.data;
59+
60+
expect(requestData).to.contain('user_consent=AAABBB');
61+
});
62+
});
63+
64+
describe('interpretResponse', function () {
65+
it('should return an empty array if an invalid response is passed', function () {
66+
const interpretedResponse = spec.interpretResponse({});
67+
expect(interpretedResponse).to.be.an('array').that.is.empty;
68+
});
69+
70+
it('should return valid response when passed valid server response', function () {
71+
const serverResponse = {
72+
body: {
73+
'requestId': '2652ca954bce9',
74+
'cpm': 1,
75+
'width': 480,
76+
'height': 320,
77+
'creativeId': '20154',
78+
'currency': 'USD',
79+
'netRevenue': false,
80+
'ttl': 360,
81+
'ad': `<div>Hello</div>`
82+
}
83+
};
84+
85+
const request = spec.buildRequests(bidRequests)[0];
86+
const interpretedResponse = spec.interpretResponse(serverResponse, request);
87+
88+
expect(interpretedResponse).to.have.lengthOf(1);
89+
90+
expect(interpretedResponse[0].requestId).to.equal(serverResponse.body.requestId);
91+
expect(interpretedResponse[0].cpm).to.equal(serverResponse.body.cpm);
92+
expect(interpretedResponse[0].width).to.equal(serverResponse.body.width);
93+
expect(interpretedResponse[0].height).to.equal(serverResponse.body.height);
94+
expect(interpretedResponse[0].creativeId).to.equal(serverResponse.body.creativeId);
95+
expect(interpretedResponse[0].currency).to.equal(serverResponse.body.currency);
96+
expect(interpretedResponse[0].netRevenue).to.equal(serverResponse.body.netRevenue);
97+
expect(interpretedResponse[0].ad).to.equal(serverResponse.body.ad);
98+
expect(interpretedResponse[0].ttl).to.equal(serverResponse.body.ttl);
99+
});
100+
});
101+
});

0 commit comments

Comments
 (0)