Skip to content

Commit 1f04f55

Browse files
Category translation module for adpod (prebid#3513)
* category translation module * update function name, refactor code and add getIabSubCategory function * move hook init and import adpod * bugfix in getting adapter spec * add hook for adserver in use * updated getting iab subcategory code * bugfix * Use individual mapping file for translation * Update default mapping url * add hook only once * update mapping url * Update brandCategoryExclusion logic * wrap in try catch * update todos and add default refreshInDays * udpate hooks * update hook
1 parent d4ff1ec commit 1f04f55

File tree

7 files changed

+510
-46
lines changed

7 files changed

+510
-46
lines changed

modules/appnexusBidAdapter.js

+36-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Renderer } from '../src/Renderer';
22
import * as utils from '../src/utils';
3-
import { registerBidder } from '../src/adapters/bidderFactory';
4-
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes';
3+
import { registerBidder, getIabSubCategory } from '../src/adapters/bidderFactory';
4+
import { BANNER, NATIVE, VIDEO, ADPOD } from '../src/mediaTypes';
55
import find from 'core-js/library/fn/array/find';
66
import includes from 'core-js/library/fn/array/includes';
77

@@ -32,6 +32,7 @@ const NATIVE_MAPPING = {
3232
displayUrl: 'displayurl'
3333
};
3434
const SOURCE = 'pbjs';
35+
const mappingFileUrl = '//acdn.adnxs.com/prebid/appnexus-mapping/mappings.json';
3536

3637
export const spec = {
3738
code: BIDDER_CODE,
@@ -209,6 +210,24 @@ export const spec = {
209210
return bids;
210211
},
211212

213+
/**
214+
* @typedef {Object} mappingFileInfo
215+
* @property {string} url mapping file json url
216+
* @property {number} refreshInDays prebid stores mapping data in localstorage so you can return in how many days you want to update value stored in localstorage.
217+
* @property {string} localStorageKey unique key to store your mapping json in localstorage
218+
*/
219+
220+
/**
221+
* Returns mapping file info. This info will be used by bidderFactory to preload mapping file and store data in local storage
222+
* @returns {mappingFileInfo}
223+
*/
224+
getMappingFileInfo: function() {
225+
return {
226+
url: mappingFileUrl,
227+
refreshInDays: 7
228+
}
229+
},
230+
212231
getUserSyncs: function(syncOptions) {
213232
if (syncOptions.iframeEnabled) {
214233
return [{
@@ -316,6 +335,21 @@ function newBid(serverBid, rtbBid, bidderRequest) {
316335
vastImpUrl: rtbBid.notify_url,
317336
ttl: 3600
318337
});
338+
339+
const videoContext = utils.deepAccess(bidRequest, 'mediaTypes.video.context');
340+
if (videoContext === ADPOD) {
341+
const iabSubCatId = getIabSubCategory(bidRequest.bidder, rtbBid.brand_category_id);
342+
343+
bid.meta = {
344+
iabSubCatId
345+
};
346+
347+
bid.video = {
348+
context: ADPOD,
349+
durationSeconds: Math.ceil(rtbBid.rtb.video.duration_ms / 1000),
350+
};
351+
}
352+
319353
// This supports Outstream Video
320354
if (rtbBid.renderer_url) {
321355
const rendererOptions = utils.deepAccess(

modules/categoryTranslation.js

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* This module translates iab category to freewheel industry using translation mapping file
3+
* Publisher can set translation file by using setConfig method
4+
*
5+
* Example:
6+
* config.setConfig({
7+
* 'brandCategoryTranslation': {
8+
* 'translationFile': 'http://sample.com'
9+
* }
10+
* });
11+
* If publisher has not defined translation file than prebid will use default prebid translation file provided here //cdn.jsdelivr.net/gh/prebid/category-mapping-file@1/freewheel-mapping.json
12+
*/
13+
14+
import { config } from '../src/config';
15+
import { setupBeforeHookFnOnce, hook } from '../src/hook';
16+
import { ajax } from '../src/ajax';
17+
import { timestamp, logError, setDataInLocalStorage, getDataFromLocalStorage } from '../src/utils';
18+
import { addBidResponse } from '../src/auction';
19+
20+
const DEFAULT_TRANSLATION_FILE_URL = '//cdn.jsdelivr.net/gh/prebid/category-mapping-file@1/freewheel-mapping.json';
21+
const DEFAULT_IAB_TO_FW_MAPPING_KEY = 'iabToFwMappingkey';
22+
const DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB = 'iabToFwMappingkeyPub';
23+
const refreshInDays = 1;
24+
25+
export const registerAdserver = hook('async', function(adServer) {
26+
let url;
27+
if (adServer === 'freewheel') {
28+
url = DEFAULT_TRANSLATION_FILE_URL;
29+
}
30+
initTranslation(url, DEFAULT_IAB_TO_FW_MAPPING_KEY);
31+
}, 'registerAdserver');
32+
registerAdserver();
33+
34+
export function getAdserverCategoryHook(fn, adUnitCode, bid) {
35+
if (!bid) {
36+
return fn.call(this, adUnitCode); // if no bid, call original and let it display warnings
37+
}
38+
39+
if (!config.getConfig('adpod.brandCategoryExclusion')) {
40+
return fn.call(this, adUnitCode, bid);
41+
}
42+
43+
let localStorageKey = (config.getConfig('brandCategoryTranslation.translationFile')) ? DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB : DEFAULT_IAB_TO_FW_MAPPING_KEY;
44+
45+
if (bid.meta && !bid.meta.adServerCatId) {
46+
let mapping = getDataFromLocalStorage(localStorageKey);
47+
if (mapping) {
48+
try {
49+
mapping = JSON.parse(mapping);
50+
} catch (error) {
51+
logError('Failed to parse translation mapping file');
52+
}
53+
if (bid.meta.iabSubCatId && mapping['mapping'] && mapping['mapping'][bid.meta.iabSubCatId]) {
54+
bid.meta.adServerCatId = mapping['mapping'][bid.meta.iabSubCatId]['id'];
55+
} else {
56+
// This bid will be automatically ignored by adpod module as adServerCatId was not found
57+
bid.meta.adServerCatId = undefined;
58+
}
59+
} else {
60+
logError('Translation mapping data not found in local storage');
61+
}
62+
}
63+
fn.call(this, adUnitCode, bid);
64+
}
65+
66+
export function initTranslation(url, localStorageKey) {
67+
setupBeforeHookFnOnce(addBidResponse, getAdserverCategoryHook, 50);
68+
let mappingData = getDataFromLocalStorage(localStorageKey);
69+
if (!mappingData || timestamp() < mappingData.lastUpdated + refreshInDays * 24 * 60 * 60 * 1000) {
70+
ajax(url,
71+
{
72+
success: (response) => {
73+
try {
74+
response = JSON.parse(response);
75+
response['lastUpdated'] = timestamp();
76+
setDataInLocalStorage(localStorageKey, JSON.stringify(response));
77+
} catch (error) {
78+
logError('Failed to parse translation mapping file');
79+
}
80+
},
81+
error: () => {
82+
logError('Failed to load brand category translation file.')
83+
}
84+
},
85+
);
86+
}
87+
}
88+
89+
function setConfig(config) {
90+
if (config.translationFile) {
91+
// if publisher has defined the translation file, preload that file here
92+
initTranslation(config.translationFile, DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB);
93+
}
94+
}
95+
96+
config.getConfig('brandCategoryTranslation', config => setConfig(config.brandCategoryTranslation));

0 commit comments

Comments
 (0)