Skip to content

Commit

Permalink
Brid Bid Adapter: Initial adapter release (prebid#9800)
Browse files Browse the repository at this point in the history
* TargetVideo bid adapter

* TargetVideo bid adapter

* TargetVideo bid adapter

* TargetVideo Bid Adapter: Add GDPR/USP support

* TargetVideo Bid Adapter: Add GDPR/USP support tests

* TargetVideo Bid Adapter: Updating margin rule

* Add Brid bid adapter

* Brid adapter requested changes
  • Loading branch information
grajzer authored and Michele Nasti committed Aug 25, 2023
1 parent 6a3c2e3 commit d869693
Show file tree
Hide file tree
Showing 2 changed files with 352 additions and 0 deletions.
223 changes: 223 additions & 0 deletions modules/bridBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import {createTrackPixelHtml, _each, deepAccess, getDefinedParams, parseGPTSingleSizeArrayToRtbSize} from '../src/utils.js';
import {VIDEO} from '../src/mediaTypes.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {getRefererInfo} from '../src/refererDetection.js';

const SOURCE = 'pbjs';
const BIDDER_CODE = 'brid';
const ENDPOINT_URL = 'https://pbs.prebrid.tv/openrtb2/auction';
const GVLID = 934;
const TIME_TO_LIVE = 300;
const VIDEO_PARAMS = [
'api', 'linearity', 'maxduration', 'mimes', 'minduration', 'placement',
'playbackmethod', 'protocols', 'startdelay'
];

export const spec = {

code: BIDDER_CODE,
gvlid: GVLID,
supportedMediaTypes: [VIDEO],

/**
* Determines whether or not the given bid request is valid.
*
* @param {object} bid The bid to validate.
* @return boolean True if this is a valid bid, and false otherwise.
*/
isBidRequestValid: function(bid) {
return !!(bid && bid.params && bid.params.placementId);
},

/**
* Make a server request from the list of BidRequests.
*
* @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server.
* @param {BidderRequest} bidderRequest bidder request object.
* @return ServerRequest Info describing the request to the server.
*/
buildRequests: function(bidRequests, bidderRequest) {
const requests = [];

_each(bidRequests, function(bid) {
const placementId = bid.params.placementId;
const bidId = bid.bidId;
let sizes = bid.sizes;
if (sizes && !Array.isArray(sizes[0])) sizes = [sizes];

const site = getSiteObj();

const postBody = {
sdk: {
source: SOURCE,
version: '$prebid.version$'
},
id: bidderRequest.bidderRequestId,
site,
imp: []
};

const imp = {
ext: {
prebid: {
storedrequest: {'id': placementId}
}
}
};

const video = deepAccess(bid, 'mediaTypes.video');
if (video) {
imp.video = getDefinedParams(video, VIDEO_PARAMS);
if (video.playerSize) {
imp.video = Object.assign(
imp.video, parseGPTSingleSizeArrayToRtbSize(video.playerSize[0]) || {}
);
} else if (video.w && video.h) {
imp.video.w = video.w;
imp.video.h = video.h;
};
};

postBody.imp.push(imp);

const gdprConsent = bidderRequest && bidderRequest.gdprConsent;
const uspConsent = bidderRequest && bidderRequest.uspConsent;

if (gdprConsent || uspConsent) {
postBody.regs = { ext: {} };

if (uspConsent) {
postBody.regs.ext.us_privacy = uspConsent;
};

if (gdprConsent) {
if (typeof gdprConsent.gdprApplies !== 'undefined') {
postBody.regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0;
};

if (typeof gdprConsent.consentString !== 'undefined') {
postBody.user = {
ext: { consent: gdprConsent.consentString }
};
};
};
};

if (bidRequests[0].schain) {
postBody.schain = bidRequests[0].schain;
}

const params = bid.params;

requests.push({
method: 'POST',
url: ENDPOINT_URL,
data: JSON.stringify(postBody),
options: {
withCredentials: true
},
bidId,
params
});
});

return requests;
},

/**
* Unpack the response from the server into a list of bids.
*
* @param {*} serverResponse A successful response from the server.
* @return {Bid[]} An array of bids which were nested inside the server.
*/
interpretResponse: function(serverResponse, bidRequest) {
const response = serverResponse.body;
const bidResponses = [];

_each(response.seatbid, (resp) => {
_each(resp.bid, (bid) => {
const requestId = bidRequest.bidId;
const params = bidRequest.params;

const {ad, adUrl, vastUrl, vastXml} = getAd(bid);

const bidResponse = {
requestId,
params,
cpm: bid.price,
width: bid.w,
height: bid.h,
creativeId: bid.adid,
currency: response.cur,
netRevenue: false,
ttl: TIME_TO_LIVE,
meta: {
advertiserDomains: bid.adomain || []
}
};

if (vastUrl || vastXml) {
bidResponse.mediaType = VIDEO;
if (vastUrl) bidResponse.vastUrl = vastUrl;
if (vastXml) bidResponse.vastXml = vastXml;
} else {
bidResponse.ad = ad;
bidResponse.adUrl = adUrl;
};

bidResponses.push(bidResponse);
});
});

return bidResponses;
},

}

/**
* Helper function to get ad
*
* @param {object} bid The bid.
* @return {object} ad object.
*/
function getAd(bid) {
let ad, adUrl, vastXml, vastUrl;

switch (deepAccess(bid, 'ext.prebid.type')) {
case VIDEO:
if (bid.adm.substr(0, 4) === 'http') {
vastUrl = bid.adm;
} else {
vastXml = bid.adm;
};
break;
default:
if (bid.adm && bid.nurl) {
ad = bid.adm;
ad += createTrackPixelHtml(decodeURIComponent(bid.nurl));
} else if (bid.adm) {
ad = bid.adm;
} else if (bid.nurl) {
adUrl = bid.nurl;
};
}

return {ad, adUrl, vastXml, vastUrl};
}

/**
* Helper function to get site object
*
* @return {object} siteObj.
*/
function getSiteObj() {
const refInfo = (getRefererInfo && getRefererInfo()) || {};

return {
page: refInfo.page,
ref: refInfo.ref,
domain: refInfo.domain
};
}

registerBidder(spec);
129 changes: 129 additions & 0 deletions test/spec/modules/bridBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { spec } from '../../../modules/bridBidAdapter.js'

describe('Brid Bid Adapter', function() {
const videoRequest = [{
bidder: 'brid',
params: {
placementId: 12345,
},
mediaTypes: {
video: {
playerSize: [[640, 360]],
context: 'instream',
playbackmethod: [1, 2, 3, 4]
}
}
}];

it('Test the bid validation function', function() {
const validBid = spec.isBidRequestValid(videoRequest[0]);
const invalidBid = spec.isBidRequestValid(null);

expect(validBid).to.be.true;
expect(invalidBid).to.be.false;
});

it('Test the request processing function', function () {
const request = spec.buildRequests(videoRequest, videoRequest[0]);
expect(request).to.not.be.empty;

const payload = JSON.parse(request[0].data);
expect(payload).to.not.be.empty;
expect(payload.sdk).to.deep.equal({
source: 'pbjs',
version: '$prebid.version$'
});
expect(payload.imp[0].ext.prebid.storedrequest.id).to.equal(12345);
});

it('Test nobid responses', function () {
const responseBody = {
'id': 'test-id',
'cur': 'USD',
'seatbid': [],
'nbr': 0
};
const bidderRequest = null;

const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest});

expect(bidResponse.length).to.equal(0);
});

it('Test the response parsing function', function () {
const responseBody = {
'id': 'test-id',
'cur': 'USD',
'seatbid': [{
'bid': [{
'id': '5044997188309660254',
'price': 5,
'adm': 'test ad',
'adid': '97517771',
'crid': '97517771',
'adomain': ['domain.com'],
'w': 640,
'h': 480
}],
'seat': 'bidder'
}]
};
const bidderRequest = {
bidderCode: 'brid',
bidderRequestId: '22edbae2733bf6',
bids: videoRequest
};

const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest});
expect(bidResponse).to.not.be.empty;

const bid = bidResponse[0];
expect(bid).to.not.be.empty;
expect(bid.ad).to.equal('test ad');
expect(bid.cpm).to.equal(5);
expect(bid.width).to.equal(640);
expect(bid.height).to.equal(480);
expect(bid.currency).to.equal('USD');
});

it('Test GDPR and USP consents are present in the request', function () {
let gdprConsentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==';
let uspConsentString = '1YA-';
let bidderRequest = {
'bidderCode': 'brid',
'bidderRequestId': '22edbae2733bf6',
'timeout': 3000,
'uspConsent': uspConsentString,
'gdprConsent': {
consentString: gdprConsentString,
gdprApplies: true,
addtlConsent: '1~7.12.35.62.66.70.89.93.108'
}
};
bidderRequest.bids = videoRequest;

const request = spec.buildRequests(videoRequest, bidderRequest);
const payload = JSON.parse(request[0].data);

expect(payload.user.ext.consent).to.equal(gdprConsentString);
expect(payload.regs.ext.us_privacy).to.equal(uspConsentString);
expect(payload.regs.ext.gdpr).to.equal(1);
});

it('Test GDPR is not present', function () {
let uspConsentString = '1YA-';
let bidderRequest = {
'bidderCode': 'brid',
'bidderRequestId': '22edbae2733bf6',
'timeout': 3000,
'uspConsent': uspConsentString
};
bidderRequest.bids = videoRequest;

const request = spec.buildRequests(videoRequest, bidderRequest);
const payload = JSON.parse(request[0].data);

expect(payload.regs.ext.gdpr).to.be.undefined;
expect(payload.regs.ext.us_privacy).to.equal(uspConsentString);
});
});

0 comments on commit d869693

Please sign in to comment.