Skip to content

Commit

Permalink
GothamAds Bid Adapter: add at, ccpa, gdpr and coppa support (prebid#6470
Browse files Browse the repository at this point in the history
)

* update gothamAds adapter

* update GothamAdsAdapter according to commetns. Add meta for adomains
  • Loading branch information
supportGothamad authored and stsepelin committed May 28, 2021
1 parent 7347d47 commit 1f8c994
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 56 deletions.
61 changes: 48 additions & 13 deletions modules/gothamadsBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
import * as utils from '../src/utils.js';
import {config} from '../src/config.js';
import { config } from '../src/config.js';

const BIDDER_CODE = 'gothamads';
const ACCOUNTID_MACROS = '[account_id]';
const URL_ENDPOINT = `https://us-e-node1.gothamads.com/bid?pass=${ACCOUNTID_MACROS}&integration=prebidjs`;
const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' };
const NATIVE_ASSET_IDS = {
0: 'title',
2: 'icon',
3: 'image',
5: 'sponsoredBy',
4: 'body',
1: 'cta'
};
const NATIVE_PARAMS = {
title: {
id: 0,
Expand Down Expand Up @@ -94,9 +101,23 @@ export const spec = {
source: {
tid: bidRequest.transactionId
},
regs: {
coppa: config.getConfig('coppa') === true ? 1 : 0,
ext: {}
},
tmax: bidRequest.timeout,
imp: [impObject],
};

if (bidRequest.gdprConsent && bidRequest.gdprConsent.gdprApplies) {
utils.deepSetValue(data, 'regs.ext.gdpr', bidRequest.gdprConsent.gdprApplies ? 1 : 0);
utils.deepSetValue(data, 'user.ext.consent', bidRequest.gdprConsent.consentString);
}

if (bidRequest.uspConsent !== undefined) {
utils.deepSetValue(data, 'regs.ext.us_privacy', bidRequest.uspConsent);
}

bids.push(data)
}
return {
Expand All @@ -114,10 +135,10 @@ export const spec = {
*/
interpretResponse: (serverResponse) => {
if (!serverResponse || !serverResponse.body) return []
let GothamAdskResponse = serverResponse.body;
let GothamAdsResponse = serverResponse.body;

let bids = [];
for (let response of GothamAdskResponse) {
for (let response of GothamAdsResponse) {
let mediaType = response.seatbid[0].bid[0].ext && response.seatbid[0].bid[0].ext.mediaType ? response.seatbid[0].bid[0].ext.mediaType : BANNER;

let bid = {
Expand All @@ -133,16 +154,21 @@ export const spec = {
mediaType: mediaType
};

bid.meta = {};
if (response.seatbid[0].bid[0].adomain && response.seatbid[0].bid[0].adomain.length > 0) {
bid.meta.advertiserDomains = response.seatbid[0].bid[0].adomain;
}

switch (mediaType) {
case VIDEO:
bid.vastXml = response.seatbid[0].bid[0].adm
bid.vastUrl = response.seatbid[0].bid[0].ext.vastUrl
break
bid.vastXml = response.seatbid[0].bid[0].adm;
bid.vastUrl = response.seatbid[0].bid[0].ext.vastUrl;
break;
case NATIVE:
bid.native = parseNative(response.seatbid[0].bid[0].adm)
break
bid.native = parseNative(response.seatbid[0].bid[0].adm);
break;
default:
bid.ad = response.seatbid[0].bid[0].adm
bid.ad = response.seatbid[0].bid[0].adm;
}

bids.push(bid);
Expand All @@ -164,18 +190,27 @@ const checkRequestType = (bidRequest, type) => {
}

const parseNative = admObject => {
const { assets, link, imptrackers, jstracker } = admObject.native;
const {
assets,
link,
imptrackers,
jstracker
} = admObject.native;
const result = {
clickUrl: link.url,
clickTrackers: link.clicktrackers || undefined,
impressionTrackers: imptrackers || undefined,
javascriptTrackers: jstracker ? [ jstracker ] : undefined
javascriptTrackers: jstracker ? [jstracker] : undefined
};
assets.forEach(asset => {
const kind = NATIVE_ASSET_IDS[asset.id];
const content = kind && asset[NATIVE_PARAMS[kind].name];
if (content) {
result[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h };
result[kind] = content.text || content.value || {
url: content.url,
width: content.w,
height: content.h
};
}
});

Expand Down
133 changes: 90 additions & 43 deletions test/spec/modules/gothamadsBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect } from 'chai';
import { spec } from 'modules/gothamadsBidAdapter.js';
import { config } from 'src/config.js';

const NATIVE_BID_REQUEST = {
code: 'native_example',
Expand Down Expand Up @@ -44,7 +45,10 @@ const BANNER_BID_REQUEST = {
code: 'banner_example',
mediaTypes: {
banner: {
sizes: [[300, 250], [300, 600]]
sizes: [
[300, 250],
[300, 600]
]
}
},
bidder: 'gothamads',
Expand All @@ -53,7 +57,11 @@ const BANNER_BID_REQUEST = {
accountId: 'accountId'
},
timeout: 1000,

gdprConsent: {
consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA',
gdprApplies: 1,
},
uspConsent: 'uspConsent'
}

const bidRequest = {
Expand All @@ -65,26 +73,27 @@ const bidRequest = {
const VIDEO_BID_REQUEST = {
code: 'video1',
sizes: [640, 480],
mediaTypes: { video: {
minduration: 0,
maxduration: 999,
boxingallowed: 1,
skip: 0,
mimes: [
'application/javascript',
'video/mp4'
],
w: 1920,
h: 1080,
protocols: [
2
],
linearity: 1,
api: [
1,
2
]
}
mediaTypes: {
video: {
minduration: 0,
maxduration: 999,
boxingallowed: 1,
skip: 0,
mimes: [
'application/javascript',
'video/mp4'
],
w: 1920,
h: 1080,
protocols: [
2
],
linearity: 1,
api: [
1,
2
]
}
},

bidder: 'gothamads',
Expand Down Expand Up @@ -148,20 +157,29 @@ const NATIVE_BID_RESPONSE = {
impid: 'request_imp_id',
price: 5,
adomain: ['example.com'],
adm: { native:
adm: {
native: {
assets: [{
id: 0,
title: 'dummyText'
},
{
id: 3,
image: imgData
},
{
assets: [
{id: 0, title: 'dummyText'},
{id: 3, image: imgData},
{
id: 5,
data: {value: 'organization.name'}
}
],
link: {url: 'example.com'},
imptrackers: ['tracker1.com', 'tracker2.com', 'tracker3.com'],
jstracker: 'tracker1.com'
id: 5,
data: {
value: 'organization.name'
}
}
],
link: {
url: 'example.com'
},
imptrackers: ['tracker1.com', 'tracker2.com', 'tracker3.com'],
jstracker: 'tracker1.com'
}
},
crid: 'crid',
ext: {
Expand All @@ -171,8 +189,23 @@ const NATIVE_BID_RESPONSE = {
}],
};

describe('GothamAdsAdapter', function() {
describe('isBidRequestValid', function() {
describe('GothamAdsAdapter', function () {
describe('with COPPA', function () {
beforeEach(function () {
sinon.stub(config, 'getConfig')
.withArgs('coppa')
.returns(true);
});
afterEach(function () {
config.getConfig.restore();
});

it('should send the Coppa "required" flag set to "1" in the request', function () {
let serverRequest = spec.buildRequests([BANNER_BID_REQUEST]);
expect(serverRequest.data[0].regs.coppa).to.equal(1);
});
});
describe('isBidRequestValid', function () {
it('should return true when required params found', function () {
expect(spec.isBidRequestValid(NATIVE_BID_REQUEST)).to.equal(true);
});
Expand Down Expand Up @@ -221,6 +254,12 @@ describe('GothamAdsAdapter', function() {
expect(request.data).to.exist;
});

it('check consent and ccpa string is set properly', function () {
expect(request.data[0].regs.ext.gdpr).to.equal(1);
expect(request.data[0].user.ext.consent).to.equal(BANNER_BID_REQUEST.gdprConsent.consentString);
expect(request.data[0].regs.ext.us_privacy).to.equal(BANNER_BID_REQUEST.uspConsent);
});

it('sends bid request to our endpoint via POST', function () {
expect(request.method).to.equal('POST');
});
Expand Down Expand Up @@ -250,7 +289,7 @@ describe('GothamAdsAdapter', function() {
});

describe('interpretResponse', function () {
it('Empty response must return empty array', function() {
it('Empty response must return empty array', function () {
const emptyResponse = null;
let response = spec.interpretResponse(emptyResponse);

Expand All @@ -273,6 +312,7 @@ describe('GothamAdsAdapter', function() {
creativeId: BANNER_BID_RESPONSE.seatbid[0].bid[0].crid,
dealId: BANNER_BID_RESPONSE.seatbid[0].bid[0].dealid,
mediaType: 'banner',
meta: BANNER_BID_RESPONSE.seatbid[0].bid[0].adomain,
ad: BANNER_BID_RESPONSE.seatbid[0].bid[0].adm
}

Expand All @@ -281,13 +321,14 @@ describe('GothamAdsAdapter', function() {
expect(bannerResponses).to.be.an('array').that.is.not.empty;
let dataItem = bannerResponses[0];
expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId',
'netRevenue', 'currency', 'dealId', 'mediaType');
'netRevenue', 'currency', 'dealId', 'mediaType', 'meta');
expect(dataItem.requestId).to.equal(expectedBidResponse.requestId);
expect(dataItem.cpm).to.equal(expectedBidResponse.cpm);
expect(dataItem.ad).to.equal(expectedBidResponse.ad);
expect(dataItem.ttl).to.equal(expectedBidResponse.ttl);
expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId);
expect(dataItem.netRevenue).to.be.true;
expect(dataItem.meta).to.have.property('advertiserDomains', expectedBidResponse.meta);
expect(dataItem.currency).to.equal(expectedBidResponse.currency);
expect(dataItem.width).to.equal(expectedBidResponse.width);
expect(dataItem.height).to.equal(expectedBidResponse.height);
Expand All @@ -309,6 +350,7 @@ describe('GothamAdsAdapter', function() {
creativeId: VIDEO_BID_RESPONSE.seatbid[0].bid[0].crid,
dealId: VIDEO_BID_RESPONSE.seatbid[0].bid[0].dealid,
mediaType: 'video',
meta: VIDEO_BID_RESPONSE.seatbid[0].bid[0].adomain,
vastXml: VIDEO_BID_RESPONSE.seatbid[0].bid[0].adm,
vastUrl: VIDEO_BID_RESPONSE.seatbid[0].bid[0].ext.vastUrl
}
Expand All @@ -318,12 +360,13 @@ describe('GothamAdsAdapter', function() {
expect(videoResponses).to.be.an('array').that.is.not.empty;
let dataItem = videoResponses[0];
expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastXml', 'vastUrl', 'ttl', 'creativeId',
'netRevenue', 'currency', 'dealId', 'mediaType');
'netRevenue', 'currency', 'dealId', 'mediaType', 'meta');
expect(dataItem.requestId).to.equal(expectedBidResponse.requestId);
expect(dataItem.cpm).to.equal(expectedBidResponse.cpm);
expect(dataItem.vastXml).to.equal(expectedBidResponse.vastXml)
expect(dataItem.vastXml).to.equal(expectedBidResponse.vastXml);
expect(dataItem.ttl).to.equal(expectedBidResponse.ttl);
expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId);
expect(dataItem.meta).to.have.property('advertiserDomains', expectedBidResponse.meta);
expect(dataItem.netRevenue).to.be.true;
expect(dataItem.currency).to.equal(expectedBidResponse.currency);
expect(dataItem.width).to.equal(expectedBidResponse.width);
Expand All @@ -345,20 +388,24 @@ describe('GothamAdsAdapter', function() {
netRevenue: true,
creativeId: NATIVE_BID_RESPONSE.seatbid[0].bid[0].crid,
dealId: NATIVE_BID_RESPONSE.seatbid[0].bid[0].dealid,
meta: NATIVE_BID_RESPONSE.seatbid[0].bid[0].adomain,
mediaType: 'native',
native: {clickUrl: NATIVE_BID_RESPONSE.seatbid[0].bid[0].adm.native.link.url}
native: {
clickUrl: NATIVE_BID_RESPONSE.seatbid[0].bid[0].adm.native.link.url
}
}

let nativeResponses = spec.interpretResponse(nativeResponse);

expect(nativeResponses).to.be.an('array').that.is.not.empty;
let dataItem = nativeResponses[0];
expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'native', 'ttl', 'creativeId',
'netRevenue', 'currency', 'dealId', 'mediaType');
'netRevenue', 'currency', 'dealId', 'mediaType', 'meta');
expect(dataItem.requestId).to.equal(expectedBidResponse.requestId);
expect(dataItem.cpm).to.equal(expectedBidResponse.cpm);
expect(dataItem.native.clickUrl).to.equal(expectedBidResponse.native.clickUrl)
expect(dataItem.native.clickUrl).to.equal(expectedBidResponse.native.clickUrl);
expect(dataItem.ttl).to.equal(expectedBidResponse.ttl);
expect(dataItem.meta).to.have.property('advertiserDomains', expectedBidResponse.meta);
expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId);
expect(dataItem.netRevenue).to.be.true;
expect(dataItem.currency).to.equal(expectedBidResponse.currency);
Expand Down

0 comments on commit 1f8c994

Please sign in to comment.