Skip to content

Commit

Permalink
Eskimi Bid Adapter: initial adapter release (prebid#9768)
Browse files Browse the repository at this point in the history
* rewrite the adapter to use ortbConverter

* Eskimi: fix `isBidRequestValid`

* Eskimi: fix request validation tests

---------

Co-authored-by: Sekandar <sekandar@eskimi.com>
  • Loading branch information
2 people authored and jorgeluisrocha committed May 18, 2023
1 parent c33c468 commit f6c90e0
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 0 deletions.
72 changes: 72 additions & 0 deletions modules/eskimiBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER} from '../src/mediaTypes.js';
import * as utils from '../src/utils.js';
import {ortbConverter} from '../libraries/ortbConverter/converter.js'

const BIDDER_CODE = 'eskimi';
// const ENDPOINT = 'https://hb.eskimi.com/bids'
const ENDPOINT = 'https://sspback.eskimi.com/bid-request'

const DEFAULT_BID_TTL = 30;
const DEFAULT_CURRENCY = 'USD';
const DEFAULT_NET_REVENUE = true;
const GVLID = 814;

export const spec = {
code: BIDDER_CODE,
gvlid: GVLID,
supportedMediaTypes: [BANNER],

isBidRequestValid: function (bid) {
return !!bid.params.placementId;
},

buildRequests(bidRequests, bidderRequest) {
const data = converter.toORTB({bidRequests, bidderRequest})

let bid = bidRequests.find((b) => b.params.placementId)
if (!data.site) data.site = {}
data.site.ext = {placementId: bid.params.placementId}

if (bidderRequest.gdprConsent) {
if (!data.user) data.user = {};
if (!data.user.ext) data.user.ext = {};
if (!data.regs) data.regs = {};
if (!data.regs.ext) data.regs.ext = {};
data.user.ext.consent = bidderRequest.gdprConsent.consentString;
data.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0;
}

return [{
method: 'POST',
url: ENDPOINT,
data,
options: {contentType: 'application/json;charset=UTF-8', withCredentials: false}
}]
},

interpretResponse(response, request) {
return converter.fromORTB({response: response.body, request: request.data}).bids;
},

/**
* Register bidder specific code, which will execute if a bid from this bidder won the auction
* @param {Bid} bid The bid that won the auction
*/
onBidWon: function (bid) {
if (bid.burl) {
utils.triggerPixel(bid.burl);
}
}
}

const converter = ortbConverter({
context: {
netRevenue: DEFAULT_NET_REVENUE,
ttl: DEFAULT_BID_TTL,
currency: DEFAULT_CURRENCY,
mediaType: BANNER // TODO: support more types, we should set mtype on the winning bid
}
});

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

Module Name: ESKIMI Bidder Adapter
Module Type: Bidder Adapter
Maintainer: tech@eskimi.com

# Description

An adapter to get a bid from Eskimi DSP.

# Test Parameters
```javascript
var adUnits = [{
code: 'div-gpt-ad-1460505748561-0',
mediaTypes: {
banner: {
sizes: [[300, 250], [300, 600]]
}
},

bids: [{
bidder: 'eskimi',
params: {
placementId: 612
}
}]

}];
```

Where:

* placementId - Placement ID of the ad unit (required)

155 changes: 155 additions & 0 deletions test/spec/modules/eskimiBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import {expect} from 'chai';
import {spec} from 'modules/eskimiBidAdapter.js';

const REQUEST = {
'bidderCode': 'eskimi',
'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708',
'bidderRequestId': 'requestId',
'bidRequest': [{
'bidder': 'eskimi',
'params': {
'placementId': 1003000,
'accId': 123
},
'sizes': [
[300, 250]
],
'bidId': 'bidId1',
'adUnitCode': 'adUnitCode1',
'bidderRequestId': 'bidderRequestId',
'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708'
},
{
'bidder': 'eskimi',
'params': {
'placementId': 1003001,
'accId': 123
},
'sizes': [
[300, 250]
],
'bidId': 'bidId2',
'adUnitCode': 'adUnitCode2',
'bidderRequestId': 'bidderRequestId',
'auctionId': 'auctionId-56a2-4f71-9098-720a68f2f708'
}],
'start': 1487883186070,
'auctionStart': 1487883186069,
'timeout': 3000
};

const RESPONSE = {
'headers': null,
'body': {
'id': 'requestId',
'seatbid': [
{
'bid': [
{
'id': 'bidId1',
'impid': 'bidId1',
'price': 0.18,
'adm': '<script>adm</script>',
'adid': '144762342',
'adomain': [
'https://dummydomain.com'
],
'iurl': 'iurl',
'cid': '109',
'crid': 'creativeId',
'cat': [],
'w': 300,
'h': 250,
'ext': {
'prebid': {
'type': 'banner'
},
'bidder': {
'eskimi': {
'brand_id': 334553,
'auction_id': 514667951122925701,
'bidder_id': 2,
'bid_ad_type': 0
}
}
}
}
],
'seat': 'seat'
}
]
}
};

describe('Eskimi bid adapter', function () {
describe('isBidRequestValid', function () {
it('should accept request if placementId is passed', function () {
let bid = {
bidder: 'eskimi',
params: {
placementId: 123
}
};
expect(spec.isBidRequestValid(bid)).to.equal(true);
});
it('reject requests without params', function () {
let bid = {
bidder: 'eskimi',
params: {}
};
expect(spec.isBidRequestValid(bid)).to.equal(false);
});
});

describe('buildRequests', function () {
it('creates request data', function () {
let request = spec.buildRequests(REQUEST.bidRequest, REQUEST)[0];
expect(request).to.exist.and.to.be.a('object');
const payload = request.data;
expect(payload.imp[0]).to.have.property('id', REQUEST.bidRequest[0].bidId);
expect(payload.imp[1]).to.have.property('id', REQUEST.bidRequest[1].bidId);
});

it('has gdpr data if applicable', function () {
const req = Object.assign({}, REQUEST, {
gdprConsent: {
consentString: 'consentString',
gdprApplies: true,
}
});
let request = spec.buildRequests(REQUEST.bidRequest, req)[0];

const payload = request.data;
expect(payload.user.ext).to.have.property('consent', req.gdprConsent.consentString);
expect(payload.regs.ext).to.have.property('gdpr', 1);
});
});

describe('interpretResponse', function () {
it('has bids', function () {
let request = spec.buildRequests(REQUEST.bidRequest, REQUEST)[0];
let bids = spec.interpretResponse(RESPONSE, request);
expect(bids).to.be.an('array').that.is.not.empty;
validateBidOnIndex(0);

function validateBidOnIndex(index) {
expect(bids[index]).to.have.property('currency', 'USD');
expect(bids[index]).to.have.property('requestId', RESPONSE.body.seatbid[0].bid[index].id);
expect(bids[index]).to.have.property('cpm', RESPONSE.body.seatbid[0].bid[index].price);
expect(bids[index]).to.have.property('width', RESPONSE.body.seatbid[0].bid[index].w);
expect(bids[index]).to.have.property('height', RESPONSE.body.seatbid[0].bid[index].h);
expect(bids[index]).to.have.property('ad', RESPONSE.body.seatbid[0].bid[index].adm);
expect(bids[index]).to.have.property('creativeId', RESPONSE.body.seatbid[0].bid[index].crid);
expect(bids[index]).to.have.property('ttl', 30);
expect(bids[index]).to.have.property('netRevenue', true);
}
});

it('handles empty response', function () {
let request = spec.buildRequests(REQUEST.bidRequest, REQUEST)[0];
const EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {}});
const bids = spec.interpretResponse(EMPTY_RESP, request);
expect(bids).to.be.empty;
});
});
});

0 comments on commit f6c90e0

Please sign in to comment.