Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adkernel Bid Adapter: multiformat imp splitting #10918

Merged
merged 3 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 72 additions & 31 deletions modules/adkernelBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => {
return acc;
}, {});

const MULTI_FORMAT_SUFFIX = '__mf';
const MULTI_FORMAT_SUFFIX_BANNER = 'b' + MULTI_FORMAT_SUFFIX;
const MULTI_FORMAT_SUFFIX_VIDEO = 'v' + MULTI_FORMAT_SUFFIX;
const MULTI_FORMAT_SUFFIX_NATIVE = 'n' + MULTI_FORMAT_SUFFIX;

/**
* Adapter for requesting bids from AdKernel white-label display platform
*/
Expand Down Expand Up @@ -173,6 +178,9 @@ export const spec = {
ttl: 360,
netRevenue: true
};
if (prBid.requestId.endsWith(MULTI_FORMAT_SUFFIX)) {
prBid.requestId = stripMultiformatSuffix(prBid.requestId);
}
if ('banner' in imp) {
prBid.mediaType = BANNER;
prBid.width = rtbBid.w;
Expand Down Expand Up @@ -239,13 +247,13 @@ registerBidder(spec);
function groupImpressionsByHostZone(bidRequests, refererInfo) {
let secure = (refererInfo && refererInfo.page?.indexOf('https:') === 0);
return Object.values(
bidRequests.map(bidRequest => buildImp(bidRequest, secure))
bidRequests.map(bidRequest => buildImps(bidRequest, secure))
.reduce((acc, curr, index) => {
let bidRequest = bidRequests[index];
let {zoneId, host} = bidRequest.params;
let key = `${host}_${zoneId}`;
acc[key] = acc[key] || {host: host, zoneId: zoneId, imps: []};
acc[key].imps.push(curr);
acc[key].imps.push(...curr);
return acc;
}, {})
);
Expand All @@ -264,61 +272,90 @@ function getBidFloor(bid, mediaType, sizes) {
}

/**
* Builds rtb imp object for single adunit
* Builds rtb imp object(s) for single adunit
* @param bidRequest {BidRequest}
* @param secure {boolean}
*/
function buildImp(bidRequest, secure) {
const imp = {
function buildImps(bidRequest, secure) {
let imp = {
'id': bidRequest.bidId,
'tagid': bidRequest.adUnitCode
};
var mediaType;
if (secure) {
imp.secure = 1;
}
var sizes = [];

if (bidRequest.mediaTypes?.banner) {
let mediaTypes = bidRequest.mediaTypes;
let isMultiformat = (~~!!mediaTypes?.banner + ~~!!mediaTypes?.video + ~~!!mediaTypes?.native) > 1;
let result = [];
let typedImp;

if (mediaTypes?.banner) {
if (isMultiformat) {
typedImp = {...imp};
typedImp.id = imp.id + MULTI_FORMAT_SUFFIX_BANNER;
} else {
typedImp = imp;
}
sizes = getAdUnitSizes(bidRequest);
let pbBanner = bidRequest.mediaTypes.banner;
imp.banner = {
let pbBanner = mediaTypes.banner;
typedImp.banner = {
...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, BANNER_FPD),
...getDefinedParamsOrEmpty(pbBanner, BANNER_PARAMS),
format: sizes.map(wh => parseGPTSingleSizeArrayToRtbSize(wh)),
topframe: 0
};
mediaType = BANNER;
} else if (bidRequest.mediaTypes?.video) {
let pbVideo = bidRequest.mediaTypes.video;
imp.video = {
initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : BANNER);
result.push(typedImp);
}

if (mediaTypes?.video) {
if (isMultiformat) {
typedImp = {...imp};
typedImp.id = typedImp.id + MULTI_FORMAT_SUFFIX_VIDEO;
} else {
typedImp = imp;
}
let pbVideo = mediaTypes.video;
typedImp.video = {
...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, VIDEO_FPD),
...getDefinedParamsOrEmpty(pbVideo, VIDEO_PARAMS)
};
if (pbVideo.playerSize) {
sizes = pbVideo.playerSize[0];
imp.video = Object.assign(imp.video, parseGPTSingleSizeArrayToRtbSize(sizes) || {});
typedImp.video = Object.assign(typedImp.video, parseGPTSingleSizeArrayToRtbSize(sizes) || {});
} else if (pbVideo.w && pbVideo.h) {
imp.video.w = pbVideo.w;
imp.video.h = pbVideo.h;
typedImp.video.w = pbVideo.w;
typedImp.video.h = pbVideo.h;
}
mediaType = VIDEO;
} else if (bidRequest.mediaTypes?.native) {
let nativeRequest = buildNativeRequest(bidRequest.mediaTypes.native);
imp.native = {
initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : VIDEO);
result.push(typedImp);
}

if (mediaTypes?.native) {
if (isMultiformat) {
typedImp = {...imp};
typedImp.id = typedImp.id + MULTI_FORMAT_SUFFIX_NATIVE;
} else {
typedImp = imp;
}
let nativeRequest = buildNativeRequest(mediaTypes.native);
typedImp.native = {
...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, NATIVE_FPD),
ver: '1.1',
request: JSON.stringify(nativeRequest)
};
mediaType = NATIVE;
} else {
throw new Error('Unsupported bid received');
}
let floor = getBidFloor(bidRequest, mediaType, sizes);
if (floor) {
imp.bidfloor = floor;
initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : NATIVE);
result.push(typedImp);
}
if (secure) {
imp.secure = 1;
return result;
}

function initImpBidfloor(imp, bid, sizes, mediaType) {
let bidfloor = getBidFloor(bid, mediaType, sizes);
if (bidfloor) {
imp.bidfloor = bidfloor;
}
return imp;
}

function getDefinedParamsOrEmpty(object, params) {
Expand Down Expand Up @@ -643,3 +680,7 @@ function buildNativeAd(nativeResp) {
});
return cleanObj(nativeAd);
}

function stripMultiformatSuffix(impid) {
return impid.substr(0, impid.length - MULTI_FORMAT_SUFFIX.length - 1);
}
48 changes: 42 additions & 6 deletions test/spec/modules/adkernelBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,31 @@ describe('Adkernel adapter', function () {
}],
bidid: 'pTuOlf5KHUo',
cur: 'EUR'
},
multiformat_response = {
id: '47ce4badcf7482',
seatbid: [{
bid: [{
id: 'sZSYq5zYMxo_0',
impid: 'Bid_01b__mf',
crid: '100_003',
price: 0.00145,
adid: '158801',
adm: '<!-- admarkup -->',
nurl: 'https://rtb.com/win?i=sZSYq5zYMxo_0&f=nurl',
cid: '16855'
}, {
id: 'sZSYq5zYMxo_1',
impid: 'Bid_01v__mf',
crid: '100_003',
price: 0.25,
adid: '158801',
nurl: 'https://rtb.com/win?i=sZSYq5zYMxo_1&f=nurl',
cid: '16855'
}]
}],
bidid: 'pTuOlf5KHUo',
cur: 'USD'
};

var sandbox;
Expand Down Expand Up @@ -460,18 +485,29 @@ describe('Adkernel adapter', function () {
});

describe('multiformat request building', function () {
let _, bidRequests;
let pbRequests, bidRequests;
before(function () {
[_, bidRequests] = buildRequest([bid_multiformat]);
[pbRequests, bidRequests] = buildRequest([bid_multiformat]);
});
it('should contain single request', function () {
expect(bidRequests).to.have.length(1);
expect(bidRequests[0].imp).to.have.length(1);
});
it('should contain banner-only impression', function () {
expect(bidRequests[0].imp).to.have.length(1);
it('should contain both impression', function () {
expect(bidRequests[0].imp).to.have.length(2);
expect(bidRequests[0].imp[0]).to.have.property('banner');
expect(bidRequests[0].imp[0]).to.not.have.property('video');
expect(bidRequests[0].imp[1]).to.have.property('video');
// check that splitted imps do not share same impid
expect(bidRequests[0].imp[0].id).to.be.not.eql('Bid_01');
expect(bidRequests[0].imp[1].id).to.be.not.eql('Bid_01');
expect(bidRequests[0].imp[1].id).to.be.not.eql(bidRequests[0].imp[0].id);
});
it('x', function() {
let bids = spec.interpretResponse({body: multiformat_response}, pbRequests[0]);
expect(bids).to.have.length(2);
expect(bids[0].requestId).to.be.eql('Bid_01');
expect(bids[0].mediaType).to.be.eql('banner');
expect(bids[1].requestId).to.be.eql('Bid_01');
expect(bids[1].mediaType).to.be.eql('video');
});
});

Expand Down