Skip to content

Commit

Permalink
AdHash Bidder Adapter: initial prebid.js integration (prebid#6274)
Browse files Browse the repository at this point in the history
  • Loading branch information
wyand-sp authored and seergiioo6 committed Mar 23, 2021
1 parent 0d9b088 commit b3293e9
Show file tree
Hide file tree
Showing 3 changed files with 300 additions and 0 deletions.
102 changes: 102 additions & 0 deletions modules/adhashBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { registerBidder } from '../src/adapters/bidderFactory.js';
import includes from 'core-js-pure/features/array/includes.js';
import { BANNER } from '../src/mediaTypes.js';

const VERSION = '1.0';

export const spec = {
code: 'adhash',
url: 'https://bidder.adhash.org/rtb?version=' + VERSION + '&prebid=true',
supportedMediaTypes: [ BANNER ],

isBidRequestValid: (bid) => {
try {
const { publisherId, platformURL } = bid.params;
return (
includes(Object.keys(bid.mediaTypes), BANNER) &&
typeof publisherId === 'string' &&
publisherId.length === 42 &&
typeof platformURL === 'string' &&
platformURL.length >= 13
);
} catch (error) {
return false;
}
},

buildRequests: (validBidRequests, bidderRequest) => {
const { gdprConsent } = bidderRequest;
const { url } = spec;
const bidRequests = [];
let referrer = '';
if (bidderRequest && bidderRequest.refererInfo) {
referrer = bidderRequest.refererInfo.referer;
}
for (var i = 0; i < validBidRequests.length; i++) {
var index = Math.floor(Math.random() * validBidRequests[i].sizes.length);
var size = validBidRequests[i].sizes[index].join('x');
bidRequests.push({
method: 'POST',
url: url,
bidRequest: validBidRequests[i],
data: {
timezone: new Date().getTimezoneOffset() / 60,
location: referrer,
publisherId: validBidRequests[i].params.publisherId,
size: {
screenWidth: window.screen.width,
screenHeight: window.screen.height
},
navigator: {
platform: window.navigator.platform,
language: window.navigator.language,
userAgent: window.navigator.userAgent
},
creatives: [{
size: size,
position: validBidRequests[i].adUnitCode
}],
blockedCreatives: [],
currentTimestamp: new Date().getTime(),
recentAds: [],
GDPR: gdprConsent
},
options: {
withCredentials: false,
crossOrigin: true
},
});
}
return bidRequests;
},

interpretResponse: (serverResponse, request) => {
const responseBody = serverResponse ? serverResponse.body : {};

if (!responseBody.creatives || responseBody.creatives.length === 0) {
return [];
}

const publisherURL = JSON.stringify(request.bidRequest.params.platformURL);
const oneTimeId = request.bidRequest.adUnitCode + Math.random().toFixed(16).replace('0.', '.');
const bidderResponse = JSON.stringify({ responseText: JSON.stringify(responseBody) });
const requestData = JSON.stringify(request.data);

return [{
requestId: request.bidRequest.bidId,
cpm: responseBody.creatives[0].costEUR,
ad:
`<div id="${oneTimeId}"></div>
<script src="https://bidder.adhash.org/static/scripts/creative.min.js"></script>
<script>callAdvertiser(${bidderResponse},['${oneTimeId}'],${requestData},${publisherURL})</script>`,
width: request.bidRequest.sizes[0][0],
height: request.bidRequest.sizes[0][1],
creativeId: request.bidRequest.adUnitCode,
netRevenue: true,
currency: 'EUR',
ttl: 60
}];
}
};

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

```
Module Name: AdHash Bidder Adapter
Module Type: Bidder Adapter
Maintainer: damyan@adhash.org
```

# Description

Here is what you need for Prebid integration with AdHash:
1. Register with AdHash.
2. Once registered and approved, you will receive a Publisher ID and Platform URL.
3. Use the Publisher ID and Platform URL as parameters in params.

Please note that a number of AdHash functionalities are not supported in the Prebid.js integration:
* Cookie-less frequency and recency capping;
* Audience segments;
* Price floors and passback tags, as they are not needed in the Prebid.js setup;
* Reservation for direct deals only, as bids are evaluated based on their price.

# Test Parameters
```
var adUnits = [
{
code: '/19968336/header-bid-tag-1',
mediaTypes: {
banner: {
sizes: [[300, 250]]
}
},
bids: [
{
bidder: 'adhash',
params: {
publisherId: '0x1234567890123456789012345678901234567890',
platformURL: 'https://adhash.org/p/struma/'
}
}
]
}
];
```
155 changes: 155 additions & 0 deletions test/spec/modules/adhashBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { expect } from 'chai';
import { spec } from 'modules/adhashBidAdapter.js';

describe('adhashBidAdapter', function () {
describe('isBidRequestValid', function () {
const validBid = {
bidder: 'adhash',
params: {
publisherId: '0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb',
platformURL: 'https://adhash.org/p/struma/'
},
mediaTypes: {
banner: {
sizes: [[300, 250]]
}
},
adUnitCode: 'adunit-code',
sizes: [[300, 250]],
bidId: '12345678901234',
bidderRequestId: '98765432109876',
auctionId: '01234567891234',
};

it('should return true when all mandatory parameters are there', function () {
expect(spec.isBidRequestValid(validBid)).to.equal(true);
});

it('should return false when there are no params', function () {
const bid = { ...validBid };
delete bid.params;
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false when unsupported media type is requested', function () {
const bid = { ...validBid };
bid.mediaTypes = { native: { sizes: [[300, 250]] } };
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false when publisherId is not a string', function () {
const bid = { ...validBid };
bid.params.publisherId = 123;
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false when publisherId is not valid', function () {
const bid = { ...validBid };
bid.params.publisherId = 'short string';
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false when publisherId is not a string', function () {
const bid = { ...validBid };
bid.params.platformURL = 123;
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false when publisherId is not valid', function () {
const bid = { ...validBid };
bid.params.platformURL = 'https://';
expect(spec.isBidRequestValid(bid)).to.equal(false);
});
});

describe('buildRequests', function () {
const bidRequest = {
params: {
publisherId: '0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb'
},
sizes: [[300, 250]],
adUnitCode: 'adUnitCode'
};
it('should build the request correctly', function () {
const result = spec.buildRequests(
[ bidRequest ],
{ gdprConsent: true, refererInfo: { referer: 'http://example.com/' } }
);
expect(result.length).to.equal(1);
expect(result[0].method).to.equal('POST');
expect(result[0].url).to.equal('https://bidder.adhash.org/rtb?version=1.0&prebid=true');
expect(result[0].bidRequest).to.equal(bidRequest);
expect(result[0].data).to.have.property('timezone');
expect(result[0].data).to.have.property('location');
expect(result[0].data).to.have.property('publisherId');
expect(result[0].data).to.have.property('size');
expect(result[0].data).to.have.property('navigator');
expect(result[0].data).to.have.property('creatives');
expect(result[0].data).to.have.property('blockedCreatives');
expect(result[0].data).to.have.property('currentTimestamp');
expect(result[0].data).to.have.property('recentAds');
});
it('should build the request correctly without referer', function () {
const result = spec.buildRequests([ bidRequest ], { gdprConsent: true });
expect(result.length).to.equal(1);
expect(result[0].method).to.equal('POST');
expect(result[0].url).to.equal('https://bidder.adhash.org/rtb?version=1.0&prebid=true');
expect(result[0].bidRequest).to.equal(bidRequest);
expect(result[0].data).to.have.property('timezone');
expect(result[0].data).to.have.property('location');
expect(result[0].data).to.have.property('publisherId');
expect(result[0].data).to.have.property('size');
expect(result[0].data).to.have.property('navigator');
expect(result[0].data).to.have.property('creatives');
expect(result[0].data).to.have.property('blockedCreatives');
expect(result[0].data).to.have.property('currentTimestamp');
expect(result[0].data).to.have.property('recentAds');
});
});

describe('interpretResponse', function () {
const request = {
data: { some: 'data' },
bidRequest: {
bidId: '12345678901234',
adUnitCode: 'adunit-code',
sizes: [[300, 250]],
params: {
platformURL: 'https://adhash.org/p/struma/'
}
}
};

it('should interpret the response correctly', function () {
const serverResponse = {
body: {
creatives: [{
costEUR: 1.234
}]
}
};
const result = spec.interpretResponse(serverResponse, request);
expect(result.length).to.equal(1);
expect(result[0].requestId).to.equal('12345678901234');
expect(result[0].cpm).to.equal(1.234);
expect(result[0].width).to.equal(300);
expect(result[0].height).to.equal(250);
expect(result[0].creativeId).to.equal('adunit-code');
expect(result[0].netRevenue).to.equal(true);
expect(result[0].currency).to.equal('EUR');
expect(result[0].ttl).to.equal(60);
});

it('should return empty array when there are no creatives returned', function () {
expect(spec.interpretResponse({body: {creatives: []}}, request).length).to.equal(0);
});

it('should return empty array when there is no creatives key in the response', function () {
expect(spec.interpretResponse({body: {}}, request).length).to.equal(0);
});

it('should return empty array when something is not right', function () {
expect(spec.interpretResponse(null, request).length).to.equal(0);
});
});
});

0 comments on commit b3293e9

Please sign in to comment.