Skip to content

Commit

Permalink
lkqdBidAdapter (#4627)
Browse files Browse the repository at this point in the history
* Refactored URL query parameter passthrough for additional values, changed SSP endpoint to v.lkqd.net, and updated associated unit tests

* Use refererInfo.referer as fallback pageurl

* Removed logs and testing values

* LF-3002: Preliminary updates for Prebid.js v3.0

* LF-3002: add schain

* LF-3002: remove unnecessary guard

* LF-3002: remove more unnecessary guard

* Addressed issues from Prebid.js code review

* Corrected sizes to work with mediaTypes.video.playerSize
  • Loading branch information
mrcrawfo authored and msm0504 committed Jan 13, 2020
1 parent 2729df9 commit ae820af
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 91 deletions.
107 changes: 75 additions & 32 deletions modules/lkqdBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,30 @@ function buildRequests(validBidRequests, bidderRequest) {
for (let i = 0; i < validBidRequests.length; i++) {
let bidRequest = validBidRequests[i];

let sizes = [];
// if width/height not provided to the ad unit for some reason then attempt request with default 640x480 size
if (!bidRequest.sizes || !bidRequest.sizes.length) {
let bidRequestSizes = bidRequest.sizes;
let bidRequestDeepSizes = utils.deepAccess(bidRequest, 'mediaTypes.video.playerSize');
if ((!bidRequestSizes || !bidRequestSizes.length) && (!bidRequestDeepSizes || !bidRequestDeepSizes.length)) {
utils.logWarn('Warning: Could not find valid width/height parameters on the provided adUnit');
bidRequest.sizes = [[640, 480]];
sizes = [[640, 480]];
}

// JWPlayer demo page uses sizes: [640,480] instead of sizes: [[640,480]] so need to handle single-layer array as well as nested arrays
if (bidRequest.sizes.length === 2 && typeof bidRequest.sizes[0] === 'number' && typeof bidRequest.sizes[1] === 'number') {
let adWidth = bidRequest.sizes[0];
let adHeight = bidRequest.sizes[1];
bidRequest.sizes = [[adWidth, adHeight]];
if (bidRequestSizes && bidRequestSizes.length > 0) {
sizes = bidRequestSizes;
if (bidRequestSizes.length === 2 && typeof bidRequestSizes[0] === 'number' && typeof bidRequestSizes[1] === 'number') {
sizes = [bidRequestSizes];
}
} else if (bidRequestDeepSizes && bidRequestDeepSizes.length > 0) {
sizes = bidRequestDeepSizes;
if (bidRequestDeepSizes.length === 2 && typeof bidRequestDeepSizes[0] === 'number' && typeof bidRequestDeepSizes[1] === 'number') {
sizes = [bidRequestDeepSizes];
}
}

for (let j = 0; j < bidRequest.sizes.length; j++) {
let size = bidRequest.sizes[j];
for (let j = 0; j < sizes.length; j++) {
let size = sizes[j];
let playerWidth;
let playerHeight;
if (size && size.length == 2) {
Expand Down Expand Up @@ -127,6 +136,9 @@ function buildRequests(validBidRequests, bidderRequest) {
if (bidRequest.params.hasOwnProperty('flrmp') && bidRequest.params.flrmp != null) {
sspData.flrmp = bidRequest.params.flrmp;
}
if (bidRequest.params.hasOwnProperty('schain') && bidRequest.params.schain != null) {
sspData.schain = bidRequest.params.schain;
}
if (bidRequest.params.hasOwnProperty('placement') && bidRequest.params.placement != null) {
sspData.placement = bidRequest.params.placement;
}
Expand All @@ -139,7 +151,7 @@ function buildRequests(validBidRequests, bidderRequest) {
if (bidRequest.params.hasOwnProperty('pageurl') && bidRequest.params.pageurl != null) {
sspData.pageurl = bidRequest.params.pageurl;
} else if (bidderRequest && bidderRequest.refererInfo) {
sspData.pageurl = encodeURIComponent(bidderRequest.refererInfo.referer);
sspData.pageurl = encodeURIComponent(encodeURIComponent(bidderRequest.refererInfo.referer));
}
if (bidRequest.params.hasOwnProperty('contentId') && bidRequest.params.contentId != null) {
sspData.contentid = bidRequest.params.contentId;
Expand All @@ -153,6 +165,9 @@ function buildRequests(validBidRequests, bidderRequest) {
if (bidRequest.params.hasOwnProperty('contentUrl') && bidRequest.params.contentUrl != null) {
sspData.contenturl = bidRequest.params.contentUrl;
}
if (bidRequest.params.hasOwnProperty('schain') && bidRequest.params.schain) {
sspData.schain = bidRequest.params.schain;
}

// random number to prevent caching
sspData.rnd = Math.floor(Math.random() * 999999999);
Expand All @@ -165,7 +180,7 @@ function buildRequests(validBidRequests, bidderRequest) {
bidRequests.push({
method: 'GET',
url: sspUrl,
data: sspData
data: Object.keys(sspData).map(function (key) { return key + '=' + sspData[key] }).join('&') + '&'
});
}
}
Expand All @@ -182,29 +197,57 @@ function interpretResponse(serverResponse, bidRequest) {
} else {
try {
let bidResponse = {};
if (bidRequest && bidRequest.data && bidRequest.data.bidId && bidRequest.data.bidId !== '') {
let sspXmlString = serverResponse.body;
let sspXml = new window.DOMParser().parseFromString(sspXmlString, 'text/xml');
if (sspXml && sspXml.getElementsByTagName('parsererror').length == 0) {
let sspUrl = bidRequest.url.concat();

bidResponse.requestId = bidRequest.data.bidId;
bidResponse.bidderCode = BIDDER_CODE;
bidResponse.ad = '';
bidResponse.cpm = parseFloat(sspXml.getElementsByTagName('Pricing')[0].textContent);
bidResponse.width = bidRequest.data.bidWidth;
bidResponse.height = bidRequest.data.bidHeight;
bidResponse.ttl = BID_TTL_DEFAULT;
bidResponse.creativeId = sspXml.getElementsByTagName('Ad')[0].getAttribute('id');
bidResponse.currency = sspXml.getElementsByTagName('Pricing')[0].getAttribute('currency');
bidResponse.netRevenue = true;
bidResponse.vastUrl = sspUrl;
bidResponse.vastXml = sspXmlString;
bidResponse.mediaType = VIDEO;

bidResponses.push(bidResponse);
if (bidRequest && bidRequest.data && typeof bidRequest.data === 'string') {
let sspData;
let sspBidId;
let sspBidWidth;
let sspBidHeight;
if (window.URLSearchParams) {
sspData = new URLSearchParams(bidRequest.data);
sspBidId = sspData.get('bidId');
sspBidWidth = sspData.get('bidWidth');
sspBidHeight = sspData.get('bidHeight');
} else {
if (bidRequest.data.indexOf('bidId=') >= 0) {
sspBidId = bidRequest.data.substr(bidRequest.data.indexOf('bidId=') + 6, bidRequest.data.length);
sspBidId = sspBidId.split('&')[0];
}
if (bidRequest.data.indexOf('bidWidth=') >= 0) {
sspBidWidth = bidRequest.data.substr(bidRequest.data.indexOf('bidWidth=') + 9, bidRequest.data.length);
sspBidWidth = sspBidWidth.split('&')[0];
}
if (bidRequest.data.indexOf('bidHeight=') >= 0) {
sspBidHeight = bidRequest.data.substr(bidRequest.data.indexOf('bidHeight=') + 10, bidRequest.data.length);
sspBidHeight = sspBidHeight.split('&')[0];
}
}

if (sspBidId) {
let sspXmlString = serverResponse.body;
let sspXml = new window.DOMParser().parseFromString(sspXmlString, 'text/xml');
if (sspXml && sspXml.getElementsByTagName('parsererror').length == 0) {
let sspUrl = bidRequest.url.concat();

bidResponse.requestId = sspBidId;
bidResponse.bidderCode = BIDDER_CODE;
bidResponse.ad = '';
bidResponse.cpm = parseFloat(sspXml.getElementsByTagName('Pricing')[0].textContent);
bidResponse.width = sspBidWidth;
bidResponse.height = sspBidHeight;
bidResponse.ttl = BID_TTL_DEFAULT;
bidResponse.creativeId = sspXml.getElementsByTagName('Ad')[0].getAttribute('id');
bidResponse.currency = sspXml.getElementsByTagName('Pricing')[0].getAttribute('currency');
bidResponse.netRevenue = true;
bidResponse.vastUrl = sspUrl;
bidResponse.vastXml = sspXmlString;
bidResponse.mediaType = VIDEO;

bidResponses.push(bidResponse);
} else {
utils.logError('Error: Server response contained invalid XML');
}
} else {
utils.logError('Error: Server response contained invalid XML');
utils.logError('Error: Could not associate bid request to server response');
}
} else {
utils.logError('Error: Could not associate bid request to server response');
Expand Down
10 changes: 6 additions & 4 deletions modules/lkqdBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ For more information about [LKQD Ad Serving and Management](http://www.lkqd.com/
var videoAdUnit = [
{
code: 'video1',
sizes: [
[300, 250],
[640, 480]
],
mediaTypes: {
video: {
context: "instream",
playerSize: [640, 480]
}
},
bids: [{
bidder: 'lkqd',
params: {
Expand Down
98 changes: 43 additions & 55 deletions test/spec/modules/lkqdBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import { spec } from 'modules/lkqdBidAdapter';
import { newBidder } from 'src/adapters/bidderFactory';
const { expect } = require('chai');

describe('LKQD Bid Adapter Test', function () {
describe('LKQD Bid Adapter Test', () => {
const adapter = newBidder(spec);

describe('inherited functions', function () {
it('exists and is a function', function () {
describe('inherited functions', () => {
it('exists and is a function', () => {
expect(adapter.callBids).to.exist.and.to.be.a('function');
});
});

describe('isBidRequestValid', function () {
describe('isBidRequestValid', () => {
let bid = {
'bidder': 'lkqd',
'params': {
Expand All @@ -26,11 +26,11 @@ describe('LKQD Bid Adapter Test', function () {
'transactionId': 'd6f6b392-54a9-454c-85fb-a2fd882c4a2d',
};

it('should return true when required params found', function () {
it('should return true when required params found', () => {
expect(spec.isBidRequestValid(bid)).to.equal(true);
});

it('should return false when required params are not passed', function () {
it('should return false when required params are not passed', () => {
let bid = Object.assign({}, bid);
delete bid.params;
bid.params = {
Expand Down Expand Up @@ -62,7 +62,8 @@ describe('LKQD Bid Adapter Test', function () {
'bidder': 'lkqd',
'params': {
'siteId': '662921',
'placementId': '263'
'placementId': '263',
'schain': '1.0,1!exchange1.com,1234%21abcd,1,bid-request-1,publisher%2c%20Inc.,publisher.com'
},
'adUnitCode': 'lkqd',
'sizes': [640, 480],
Expand All @@ -73,61 +74,52 @@ describe('LKQD Bid Adapter Test', function () {
}
];

it('should populate available parameters', function () {
it('should populate available parameters', () => {
const requests = spec.buildRequests(bidRequests);
expect(requests.length).to.equal(2);
const r1 = requests[0].data;
expect(r1).to.have.property('pid');
expect(r1.pid).to.equal('263');
expect(r1).to.have.property('sid');
expect(r1.sid).to.equal('662921');
expect(r1).to.have.property('width');
expect(r1.width).to.equal(300);
expect(r1).to.have.property('height');
expect(r1.height).to.equal(250);
expect(r1).to.have.string('pid=263&');
expect(r1).to.have.string('&sid=662921&');
expect(r1).to.have.string('&width=300&');
expect(r1).to.have.string('&height=250&');
const r2 = requests[1].data;
expect(r2).to.have.property('pid');
expect(r2.pid).to.equal('263');
expect(r2).to.have.property('sid');
expect(r2.sid).to.equal('662921');
expect(r2).to.have.property('width');
expect(r2.width).to.equal(640);
expect(r2).to.have.property('height');
expect(r2.height).to.equal(480);
expect(r2).to.have.string('pid=263&');
expect(r2).to.have.string('&sid=662921&');
expect(r2).to.have.string('&width=640&');
expect(r2).to.have.string('&height=480&');
});

it('should not populate unspecified parameters', function () {
it('should not populate unspecified parameters', () => {
const requests = spec.buildRequests(bidRequests);
expect(requests.length).to.equal(2);
const r1 = requests[0].data;
expect(r1).to.not.have.property('dnt');
expect(r1).to.not.have.property('contentid');
expect(r1).to.not.have.property('contenttitle');
expect(r1).to.not.have.property('contentlength');
expect(r1).to.not.have.property('contenturl');
expect(r1).to.not.have.string('&dnt=');
expect(r1).to.not.have.string('&contentid=');
expect(r1).to.not.have.string('&contenttitle=');
expect(r1).to.not.have.string('&contentlength=');
expect(r1).to.not.have.string('&contenturl=');
expect(r1).to.not.have.string('&schain=');
const r2 = requests[1].data;
expect(r2).to.not.have.property('dnt');
expect(r2).to.not.have.property('contentid');
expect(r2).to.not.have.property('contenttitle');
expect(r2).to.not.have.property('contentlength');
expect(r2).to.not.have.property('contenturl');
expect(r2).to.not.have.string('&dnt=');
expect(r2).to.not.have.string('&contentid=');
expect(r2).to.not.have.string('&contenttitle=');
expect(r2).to.not.have.string('&contentlength=');
expect(r2).to.not.have.string('&contenturl=');
expect(r2).to.not.have.string('&schain=');
});

it('should handle single size request', function () {
it('should handle single size request', () => {
const requests = spec.buildRequests(bidRequest);
expect(requests.length).to.equal(1);
const r1 = requests[0].data;
expect(r1).to.have.property('pid');
expect(r1.pid).to.equal('263');
expect(r1).to.have.property('sid');
expect(r1.sid).to.equal('662921');
expect(r1).to.have.property('width');
expect(r1.width).to.equal(640);
expect(r1).to.have.property('height');
expect(r1.height).to.equal(480);
expect(r1).to.have.string('pid=263&');
expect(r1).to.have.string('&sid=662921&');
expect(r1).to.have.string('&width=640&');
expect(r1).to.have.string('&height=480&');
expect(r1).to.have.string('&schain=1.0,1!exchange1.com,1234%21abcd,1,bid-request-1,publisher%2c%20Inc.,publisher.com&');
});

it('sends bid request to ENDPOINT via GET', function () {
it('sends bid request to ENDPOINT via GET', () => {
const requests = spec.buildRequests(bidRequests);
expect(requests.length).to.equal(2);
const r1 = requests[0];
Expand All @@ -139,14 +131,10 @@ describe('LKQD Bid Adapter Test', function () {
});
});

describe('interpretResponse', function () {
describe('interpretResponse', () => {
let bidRequest = {
'url': 'https://ssp.lkqd.net/ad?pid=263&sid=662921&output=vast&execution=any&placement=&playinit=auto&volume=100&timeout=&width=300%E2%80%8C&height=250&pbt=[PREBID_TOKEN]%E2%80%8C&dnt=[DO_NOT_TRACK]%E2%80%8C&pageurl=[PAGEURL]%E2%80%8C&contentid=[CONTENT_ID]%E2%80%8C&contenttitle=[CONTENT_TITLE]%E2%80%8C&contentlength=[CONTENT_LENGTH]%E2%80%8C&contenturl=[CONTENT_URL]&prebid=true%E2%80%8C&rnd=874313435?bidId=253dcb69fb2577&bidWidth=300&bidHeight=250&',
'data': {
'bidId': '253dcb69fb2577',
'bidWidth': '640',
'bidHeight': '480'
}
'data': 'pid=263&sid=662921&prebid=true&output=vast&execution=any&support=html5&playinit=auto&volume=100&width=640&height=480&rnd=89811791&bidId=20d2f9095ba4e3&bidWidth=640&bidHeight=480&'
};
let serverResponse = {};
serverResponse.body = `<VAST version="2.0">
Expand Down Expand Up @@ -320,12 +308,12 @@ https://creative.lkqd.net/internal/lkqd_300x250.mp4
</Ad>
</VAST>`;

it('should correctly parse valid bid response', function () {
it('should correctly parse valid bid response', () => {
const BIDDER_CODE = 'lkqd';
let bidResponses = spec.interpretResponse(serverResponse, bidRequest);
expect(bidResponses.length).to.equal(1);
let bidResponse = bidResponses[0];
expect(bidResponse.requestId).to.equal(bidRequest.data.bidId);
expect(bidResponse.requestId).to.equal('20d2f9095ba4e3');
expect(bidResponse.bidderCode).to.equal(BIDDER_CODE);
expect(bidResponse.ad).to.equal('');
expect(bidResponse.cpm).to.equal(2.87);
Expand All @@ -338,15 +326,15 @@ https://creative.lkqd.net/internal/lkqd_300x250.mp4
expect(bidResponse.mediaType).to.equal('video');
});

it('safely handles XML parsing failure from invalid bid response', function () {
it('safely handles XML parsing failure from invalid bid response', () => {
let invalidServerResponse = {};
invalidServerResponse.body = '<Ad id="677477"><InLine></AdSystem></InLine></Ad>';

let result = spec.interpretResponse(invalidServerResponse, bidRequest);
expect(result.length).to.equal(0);
});

it('handles nobid responses', function () {
it('handles nobid responses', () => {
let nobidResponse = {};
nobidResponse.body = '<?xml version=\'1.0\' encoding=\'UTF-8\'?><VAST version=\'2.0\'></VAST>';

Expand Down

0 comments on commit ae820af

Please sign in to comment.