Skip to content

Commit

Permalink
AdGrid Bid Adapter : initial release (prebid#12152)
Browse files Browse the repository at this point in the history
* Added AdGrid Adapter Files

* Removed coppa
  • Loading branch information
soman authored Sep 11, 2024
1 parent fa33f7c commit 88ec0b2
Show file tree
Hide file tree
Showing 3 changed files with 320 additions and 0 deletions.
175 changes: 175 additions & 0 deletions modules/adgridBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { _each, isEmpty, deepAccess } from '../src/utils.js';
import { config } from '../src/config.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER } from '../src/mediaTypes.js';

const BIDDER = Object.freeze({
CODE: 'adgrid',
HOST: 'https://api-prebid.adgrid.io',
REQUEST_METHOD: 'POST',
REQUEST_ENDPOINT: '/api/v1/auction',
SUPPORTED_MEDIA_TYPES: [BANNER],
});

const CURRENCY = Object.freeze({
KEY: 'currency',
US_DOLLAR: 'USD',
});

function isBidRequestValid(bid) {
if (!bid || !bid.params) {
return false;
}

return !!bid.params.domainId;
}

/**
* Return some extra params
*/
function getAudience(validBidRequests, bidderRequest) {
const params = {
domain: deepAccess(bidderRequest, 'refererInfo.page')
};

if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies')) {
params.gdpr = 1;
params.gdprConsent = deepAccess(bidderRequest, 'gdprConsent.consentString');
}

if (deepAccess(bidderRequest, 'uspConsent')) {
params.usp = deepAccess(bidderRequest, 'uspConsent');
}

if (deepAccess(validBidRequests[0], 'schain')) {
params.schain = deepAccess(validBidRequests[0], 'schain');
}

if (deepAccess(validBidRequests[0], 'userId')) {
params.userIds = deepAccess(validBidRequests[0], 'userId');
}

if (deepAccess(validBidRequests[0], 'userIdAsEids')) {
params.userEids = deepAccess(validBidRequests[0], 'userIdAsEids');
}

if (bidderRequest.gppConsent) {
params.gpp = bidderRequest.gppConsent.gppString;
params.gppSid = bidderRequest.gppConsent.applicableSections?.toString();
} else if (bidderRequest.ortb2?.regs?.gpp) {
params.gpp = bidderRequest.ortb2.regs.gpp;
params.gppSid = bidderRequest.ortb2.regs.gpp_sid;
}

return params;
}

function buildRequests(validBidRequests, bidderRequest) {
const currencyObj = config.getConfig(CURRENCY.KEY);
const currency = (currencyObj && currencyObj.adServerCurrency) ? currencyObj.adServerCurrency : 'USD';
const bids = [];

_each(validBidRequests, bid => {
bids.push(getBidData(bid))
});

const bidsParams = Object.assign({}, {
url: window.location.href,
timeout: bidderRequest.timeout,
ts: new Date().getTime(),
device: {
size: [
window.screen.width,
window.screen.height
]
},
bids
});

// Add currency if not USD
if (currency != null && currency != CURRENCY.US_DOLLAR) {
bidsParams.cur = currency;
}

bidsParams.audience = getAudience(validBidRequests, bidderRequest);

// Passing geo location data if found in prebid config
bidsParams.geodata = config.getConfig('adgGeodata') || {};

return Object.assign({}, bidderRequest, {
method: BIDDER.REQUEST_METHOD,
url: `${BIDDER.HOST}${BIDDER.REQUEST_ENDPOINT}`,
data: bidsParams,
currency: currency,
options: {
withCredentials: false,
contentType: 'application/json'
}
});
}

function interpretResponse(response, bidRequest) {
let bids = response.body;
const bidResponses = [];

if (isEmpty(bids)) {
return bidResponses;
}

if (typeof bids !== 'object') {
return bidResponses;
}

bids = bids.bids;

bids.forEach((adUnit) => {
const bidResponse = {
requestId: adUnit.bidId,
cpm: Number(adUnit.cpm),
width: adUnit.width,
height: adUnit.height,
ttl: 300,
creativeId: adUnit.creativeId,
netRevenue: true,
currency: adUnit.currency || bidRequest.currency,
mediaType: adUnit.mediaType,
ad: adUnit.ad,
};

bidResponses.push(bidResponse);
});

return bidResponses;
}

function getBidData(bid) {
const bidData = {
requestId: bid.bidId,
tid: bid.ortb2Imp?.ext?.tid,
deviceW: bid.ortb2?.device?.w,
deviceH: bid.ortb2?.device?.h,
deviceUa: bid.ortb2?.device?.ua,
domain: bid.ortb2?.site?.publisher?.domain,
domainId: bid.params.domainId,
code: bid.adUnitCode
};

if (bid.mediaTypes != null) {
if (bid.mediaTypes.banner != null) {
bidData.mediaType = 'banner';
bidData.sizes = bid.mediaTypes.banner.sizes;
}
}

return bidData;
}

export const spec = {
code: BIDDER.CODE,
isBidRequestValid,
buildRequests,
interpretResponse,
supportedMediaTypes: BIDDER.SUPPORTED_MEDIA_TYPES
};

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

**Module Name**: AdGrid Bidder Adapter
**Module Type**: Bidder Adapter
**Maintainer**: support@adgrid.io

# Description

The AdGrid Bidding Adapter requires setup and approval before beginning. Please reach out to <support@adgrid.io> for more details.

# Test Parameters

```javascript
var adUnits = [
// Banner adUnit
{
code: 'test-div-1',
mediaTypes:{
banner:{
sizes: [[300, 250]]
}
}
bids: [{
bidder: 'adgrid',
params: {
domainId: 12345
}
}]
},
{
code: 'test-div-2',
mediaTypes:{
banner:{
sizes: [[728, 90], [320, 50]]
}
}
bids: [{
bidder: 'adgrid',
params: {
domainId: 67890
}
}]
}
];
```
100 changes: 100 additions & 0 deletions test/spec/modules/adgridBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { expect } from 'chai';
import { spec } from '../../../modules/adgridBidAdapter.js'

const globalConfig = {
method: 'POST',
endPoint: 'https://api-prebid.adgrid.io/api/v1/auction'
};

describe('AdGrid Bid Adapter', function () {
const bannerRequest = [{
bidId: 123456,
auctionId: 98765,
mediaTypes: {
banner: {
sizes: [[300, 250]],
}
},
params: {
domainId: 12345
}
}];

describe('isBidRequestValid', function () {
it('Should return true when domainId exist inside params object', function () {
const isBidValid = spec.isBidRequestValid(bannerRequest[0]);
expect(isBidValid).to.be.true;
});

it('Should return false when domainId is not exist inside params object', function () {
const isBidNotValid = spec.isBidRequestValid(null);
expect(isBidNotValid).to.be.false;
});
});

describe('buildRequests', function () {
const request = spec.buildRequests(bannerRequest, bannerRequest[0]);
const payload = request.data;
const apiURL = request.url;
const method = request.method;

it('Test the request is not empty', function () {
expect(request).to.not.be.empty;
});

it('Test the request payload is not empty', function () {
expect(payload).to.not.be.empty;
});

it('Test the API End Point', function () {
expect(apiURL).to.equal(globalConfig.endPoint);
});

it('Test the API Method', function () {
expect(method).to.equal(globalConfig.method);
});
});

describe('interpretResponse', function () {
const responseObj = {
bids: [
{
bidId: '4b99f3428651c1',
cpm: 7.7,
ad: '<div>Ad Content</div>',
creativeId: '9004',
currency: 'USD',
mediaType: 'banner',
width: 320,
height: 50,
domainId: '2002',
marketplaceId: '703',
devices: 'desktop'
}
]
};

it('Test the interpretResponse function', function () {
const receivedBid = responseObj.bids[0];
const response = {};
response.body = responseObj;

const bidRequest = {};
bidRequest.currency = 'USD';

const bidResponse = spec.interpretResponse(response, bidRequest);
expect(bidResponse).to.not.be.empty;

const bid = bidResponse[0];
expect(bid).to.not.be.empty;
expect(bid.requestId).to.equal(receivedBid.bidId);
expect(bid.ad).to.equal(receivedBid.ad);
expect(bid.cpm).to.equal(receivedBid.cpm);
expect(bid.mediaType).to.equal(receivedBid.mediaType);
expect(bid.creativeId).to.equal(receivedBid.creativeId);
expect(bid.width).to.equal(receivedBid.width);
expect(bid.height).to.equal(receivedBid.height);
expect(bid.currency).to.equal(receivedBid.currency);
});
});
});

0 comments on commit 88ec0b2

Please sign in to comment.