Skip to content
Open
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
43 changes: 39 additions & 4 deletions modules/sevioBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,39 @@ const getReferrerInfo = (bidderRequest) => {
return bidderRequest?.refererInfo?.page ?? '';
}

function resolveDataType(asset) {
if (typeof asset?.data?.type === 'number') {
return asset.data.type;
}

if (typeof asset?.id === 'number') {
return asset.id;
}

return null;
}

// Helper: resolve the "image type" for an asset
// Returns 1 (icon), 3 (image) or null if unknown
function resolveImageType(asset) {
if (!asset) return null;

// 1) explicit image type in the img block (preferred)
if (typeof asset.img?.type === 'number') return asset.img.type;

// 2) fallback to data.type (some bidders put the type here)
if (typeof asset.data?.type === 'number') return asset.data.type;

// 3) last resort: map legacy asset.id values to image types
// (13 -> icon, 14 -> image) — keep this mapping isolated here
if (typeof asset.id === 'number') {
if (asset.id === 13) return 1; // icon
if (asset.id === 14) return 3; // image
}

return null;
}

const parseNativeAd = function (bid) {
try {
const nativeAd = JSON.parse(bid.ad);
Expand All @@ -112,7 +145,8 @@ const parseNativeAd = function (bid) {
}
if (asset.data) {
const value = asset.data.value;
switch (asset.data.type) {
const type = resolveDataType(asset);
switch (type) {
case 1: if (value) native.sponsored = value; break;
case 2: if (value) native.desc = value; break;
case 3: if (value) native.rating = value; break;
Expand All @@ -129,13 +163,14 @@ const parseNativeAd = function (bid) {
}
}
if (asset.img) {
const { url, w = 0, h = 0, type } = asset.img;
const { url, w = 0, h = 0 } = asset.img;
const imgType = resolveImageType(asset);

if (type === 1 && url) {
if (imgType === 1 && url) {
native.icon = url;
native.icon_width = w;
native.icon_height = h;
} else if (type === 3 && url) {
} else if (imgType === 3 && url) {
native.image = url;
native.image_width = w;
native.image_height = h;
Expand Down
104 changes: 104 additions & 0 deletions test/spec/modules/sevioBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -510,4 +510,108 @@ describe('sevioBidAdapter', function () {
expect(requests[0].data.keywords.tokens).to.deep.equal(['play', 'games', 'fun']);
});
});

describe('native parsing', function () {
it('parses native assets: title, data->desc (type 2), image (asset.img), clickUrl and trackers', function () {
const serverResponseNative = {
body: {
bids: [
{
requestId: 'native-1',
cpm: 1.0,
currency: 'EUR',
width: 1,
height: 1,
creativeId: 'native-creative-1',
ad: JSON.stringify({
ver: '1.2',
assets: [
{ id: 2, title: { text: 'Native Title' } },
{ id: 4, data: { type: 2, value: 'Native body text' } },
{ id: 5, img: { type: 3, url: 'https://img.example/x.png', w: 120, h: 60 } }
],
eventtrackers: [
{ event: 1, method: 1, url: 'https://impr.example/1' },
{ event: 2, method: 1, url: 'https://view.example/1' }
],
link: { url: 'https://click.example', clicktrackers: ['https://clickt.example/1'] }
}),
ttl: 300,
netRevenue: true,
mediaType: 'NATIVE',
meta: { advertiserDomains: ['adv.example'] },
bidder: 'sevio'
}
]
}
};

const result = spec.interpretResponse(serverResponseNative);
expect(result).to.be.an('array').with.lengthOf(1);

const out = result[0];
expect(out).to.have.property('native');

const native = out.native;
expect(native.title).to.equal('Native Title');
expect(native.image).to.equal('https://img.example/x.png');
expect(native.image_width).to.equal(120);
expect(native.image_height).to.equal(60);
expect(native.clickUrl).to.equal('https://click.example');

expect(native.impressionTrackers).to.be.an('array').that.includes('https://impr.example/1');
expect(native.viewableTrackers).to.be.an('array').that.includes('https://view.example/1');
expect(native.clickTrackers).to.be.an('array').that.includes('https://clickt.example/1');

// meta preserved
expect(out.meta).to.have.property('advertiserDomains').that.deep.equals(['adv.example']);
});

it('maps legacy asset.id -> image types (13 -> icon, 14 -> image) and sets icon fields', function () {
const serverResponseIcon = {
body: {
bids: [
{
requestId: 'native-icon',
cpm: 1.0,
currency: 'EUR',
width: 1,
height: 1,
creativeId: 'native-creative-icon',
ad: JSON.stringify({
ver: '1.2',
assets: [
// legacy asset id 13 should map to icon (img type 1)
{ id: 13, img: { url: 'https://img.example/icon.png', w: 50, h: 50 } },
// legacy asset id 14 should map to image (img type 3)
{ id: 14, img: { url: 'https://img.example/img.png', w: 200, h: 100 } },
{ id: 2, title: { text: 'Legacy Mapping Test' } }
],
link: { url: 'https://click.example/leg' }
}),
ttl: 300,
netRevenue: true,
mediaType: 'NATIVE',
meta: { advertiserDomains: ['legacy.example'] },
bidder: 'sevio'
}
]
}
};

const result = spec.interpretResponse(serverResponseIcon);
expect(result).to.be.an('array').with.lengthOf(1);
const native = result[0].native;

// icon mapped from id 13
expect(native.icon).to.equal('https://img.example/icon.png');
expect(native.icon_width).to.equal(50);
expect(native.icon_height).to.equal(50);

// image mapped from id 14
expect(native.image).to.equal('https://img.example/img.png');
expect(native.image_width).to.equal(200);
expect(native.image_height).to.equal(100);
});
});
});