Skip to content

Commit 35eb3d4

Browse files
rayn rtd provider module (#11054)
Co-authored-by: Berislav Kovacki <[email protected]>
1 parent 6342942 commit 35eb3d4

File tree

4 files changed

+791
-0
lines changed

4 files changed

+791
-0
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<html>
2+
<head>
3+
<script>
4+
var segments = {
5+
"4": {
6+
"3": ["264", "267", "261"],
7+
"4": ["438"]
8+
},
9+
"903555595": {
10+
"7": {
11+
"2": ["51", "246"]
12+
}
13+
},
14+
};
15+
window.localStorage.setItem(
16+
"rayn-segtax",
17+
JSON.stringify(segments)
18+
);
19+
</script>
20+
<script>
21+
var FAILSAFE_TIMEOUT = 2000;
22+
23+
var test_div_sizes = [
24+
[300, 250],
25+
[300, 600]
26+
];
27+
28+
var adUnits = [
29+
{
30+
code: "test-div",
31+
mediaTypes: {
32+
banner: {
33+
sizes: test_div_sizes,
34+
},
35+
},
36+
bids: [
37+
{
38+
bidder: "appnexus",
39+
params: {
40+
placementId: 13144370,
41+
},
42+
},
43+
],
44+
},
45+
];
46+
47+
var pbjs = pbjs || {};
48+
pbjs.que = pbjs.que || [];
49+
</script>
50+
<script src="../../build/dev/prebid.js" async></script>
51+
52+
<script>
53+
var googletag = googletag || {};
54+
var AUCTION_DELAY = 2000;
55+
googletag.cmd = googletag.cmd || [];
56+
googletag.cmd.push(function () {
57+
googletag.pubads().disableInitialLoad();
58+
});
59+
60+
pbjs.que.push(function () {
61+
pbjs.setConfig({
62+
debug: true,
63+
realTimeData: {
64+
auctionDelay: AUCTION_DELAY,
65+
dataProviders: [
66+
{
67+
name: "rayn",
68+
waitForIt: true,
69+
params: {
70+
bidders: ['appnexus'],
71+
integration: {
72+
iabAudienceCategories: {
73+
v1_1: {
74+
tier: 3,
75+
enabled: true,
76+
},
77+
},
78+
iabContentCategories: {
79+
v3_0: {
80+
tier: 2,
81+
enabled: true,
82+
},
83+
v2_2: {
84+
tier: 1,
85+
enabled: false,
86+
},
87+
},
88+
}
89+
},
90+
},
91+
],
92+
},
93+
});
94+
pbjs.setBidderConfig({
95+
bidders: ["appnexus", "pubmatic"],
96+
config: {
97+
ortb2: {
98+
user: {
99+
ext: {
100+
data: {
101+
registered: true,
102+
interests: ["cars"],
103+
},
104+
},
105+
},
106+
},
107+
},
108+
});
109+
110+
pbjs.addAdUnits(adUnits);
111+
pbjs.requestBids({ bidsBackHandler: sendAdserverRequest });
112+
});
113+
114+
function sendAdserverRequest() {
115+
document.getElementById("rayn-segments").innerHTML =
116+
window.localStorage.getItem("rayn-segtax");
117+
118+
if (pbjs.adserverRequestSent) return;
119+
pbjs.adserverRequestSent = true;
120+
googletag.cmd.push(function () {
121+
pbjs.que.push(function () {
122+
pbjs.setTargetingForGPTAsync();
123+
googletag.pubads().refresh();
124+
});
125+
});
126+
}
127+
128+
setTimeout(function () {
129+
sendAdserverRequest();
130+
}, FAILSAFE_TIMEOUT);
131+
</script>
132+
133+
<script>
134+
(function () {
135+
var gads = document.createElement("script");
136+
gads.async = true;
137+
gads.type = "text/javascript";
138+
gads.src = 'https://securepubads.g.doubleclick.net/tag/js/gpt.js';
139+
var node = document.getElementsByTagName("script")[0];
140+
node.parentNode.insertBefore(gads, node);
141+
})();
142+
</script>
143+
144+
<script>
145+
googletag.cmd.push(function () {
146+
googletag.defineSlot('/19968336/header-bid-tag-1', test_div_sizes, "test-div").addService(googletag.pubads());
147+
googletag.pubads().enableSingleRequest();
148+
googletag.enableServices();
149+
});
150+
</script>
151+
</head>
152+
153+
<body>
154+
<h2>Rayn RTD Prebid</h2>
155+
156+
<div id="test-div">
157+
<script>
158+
googletag.cmd.push(function () {
159+
googletag.display("test-div");
160+
});
161+
</script>
162+
</div>
163+
164+
Rayn Segments:
165+
<div id="rayn-segments"></div>
166+
</body>
167+
</html>

modules/raynRtdProvider.js

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/**
2+
* This module adds the Rayn provider to the real time data module
3+
* The {@link module:modules/realTimeData} module is required
4+
* The module will fetch real-time audience and context data from Rayn
5+
* @module modules/raynRtdProvider
6+
* @requires module:modules/realTimeData
7+
*/
8+
9+
import { MODULE_TYPE_RTD } from '../src/activities/modules.js';
10+
import { submodule } from '../src/hook.js';
11+
import { getStorageManager } from '../src/storageManager.js';
12+
import { deepAccess, deepSetValue, logError, logMessage, mergeDeep } from '../src/utils.js';
13+
14+
const MODULE_NAME = 'realTimeData';
15+
const SUBMODULE_NAME = 'rayn';
16+
const RAYN_TCF_ID = 1220;
17+
const LOG_PREFIX = 'RaynJS: ';
18+
export const SEGMENTS_RESOLVER = 'rayn.io';
19+
export const RAYN_LOCAL_STORAGE_KEY = 'rayn-segtax';
20+
21+
const defaultIntegration = {
22+
iabAudienceCategories: {
23+
v1_1: {
24+
tier: 6,
25+
enabled: true,
26+
},
27+
},
28+
iabContentCategories: {
29+
v3_0: {
30+
tier: 4,
31+
enabled: true,
32+
},
33+
v2_2: {
34+
tier: 4,
35+
enabled: true,
36+
},
37+
},
38+
};
39+
40+
export const storage = getStorageManager({
41+
moduleType: MODULE_TYPE_RTD,
42+
moduleName: SUBMODULE_NAME,
43+
});
44+
45+
function init(moduleConfig, userConsent) {
46+
return true;
47+
}
48+
49+
/**
50+
* Create and return ORTB2 object with segtax and segments
51+
* @param {number} segtax
52+
* @param {Array} segmentIds
53+
* @param {number} maxTier
54+
* @return {Array}
55+
*/
56+
export function generateOrtbDataObject(segtax, segment, maxTier) {
57+
const segmentIds = [];
58+
59+
try {
60+
Object.keys(segment).forEach(tier => {
61+
if (tier <= maxTier) {
62+
segmentIds.push(...segment[tier].map((id) => {
63+
return { id };
64+
}))
65+
}
66+
});
67+
} catch (error) {
68+
logError(LOG_PREFIX, error);
69+
}
70+
71+
return {
72+
name: SEGMENTS_RESOLVER,
73+
ext: {
74+
segtax,
75+
},
76+
segment: segmentIds,
77+
};
78+
}
79+
80+
/**
81+
* Generates checksum
82+
* @param {string} url
83+
* @returns {string}
84+
*/
85+
export function generateChecksum(stringValue) {
86+
const l = stringValue.length;
87+
let i = 0;
88+
let h = 0;
89+
if (l > 0) while (i < l) h = ((h << 5) - h + stringValue.charCodeAt(i++)) | 0;
90+
return h.toString();
91+
};
92+
93+
/**
94+
* Gets an object of segtax and segment IDs from LocalStorage
95+
* or return the default value provided.
96+
* @param {string} key
97+
* @return {Object}
98+
*/
99+
export function readSegments(key) {
100+
try {
101+
return JSON.parse(storage.getDataFromLocalStorage(key));
102+
} catch (error) {
103+
logError(LOG_PREFIX, error);
104+
return null;
105+
}
106+
}
107+
108+
/**
109+
* Pass segments to configured bidders, using ORTB2
110+
* @param {Object} bidConfig
111+
* @param {Array} bidders
112+
* @param {Object} integrationConfig
113+
* @param {Array} segments
114+
* @return {void}
115+
*/
116+
export function setSegmentsAsBidderOrtb2(bidConfig, bidders, integrationConfig, segments, checksum) {
117+
const raynOrtb2 = {};
118+
119+
const raynContentData = [];
120+
if (integrationConfig.iabContentCategories.v2_2.enabled && segments[checksum] && segments[checksum][6]) {
121+
raynContentData.push(generateOrtbDataObject(6, segments[checksum][6], integrationConfig.iabContentCategories.v2_2.tier));
122+
}
123+
if (integrationConfig.iabContentCategories.v3_0.enabled && segments[checksum] && segments[checksum][7]) {
124+
raynContentData.push(generateOrtbDataObject(7, segments[checksum][7], integrationConfig.iabContentCategories.v3_0.tier));
125+
}
126+
if (raynContentData.length > 0) {
127+
deepSetValue(raynOrtb2, 'site.content.data', raynContentData);
128+
}
129+
130+
if (integrationConfig.iabAudienceCategories.v1_1.enabled && segments[4]) {
131+
const raynUserData = [generateOrtbDataObject(4, segments[4], integrationConfig.iabAudienceCategories.v1_1.tier)];
132+
deepSetValue(raynOrtb2, 'user.data', raynUserData);
133+
}
134+
135+
if (!bidders || bidders.length === 0 || !segments || Object.keys(segments).length <= 0) {
136+
mergeDeep(bidConfig?.ortb2Fragments?.global, raynOrtb2);
137+
} else {
138+
const bidderConfig = Object.fromEntries(
139+
bidders.map((bidder) => [bidder, raynOrtb2]),
140+
);
141+
mergeDeep(bidConfig?.ortb2Fragments?.bidder, bidderConfig);
142+
}
143+
}
144+
145+
/**
146+
* Real-time data retrieval from Rayn
147+
* @param {Object} reqBidsConfigObj
148+
* @param {function} callback
149+
* @param {Object} config
150+
* @param {Object} userConsent
151+
* @return {void}
152+
*/
153+
function alterBidRequests(reqBidsConfigObj, callback, config, userConsent) {
154+
try {
155+
const checksum = generateChecksum(window.location.href);
156+
157+
const segments = readSegments(RAYN_LOCAL_STORAGE_KEY);
158+
159+
const bidders = deepAccess(config, 'params.bidders');
160+
const integrationConfig = mergeDeep(defaultIntegration, deepAccess(config, 'params.integration'));
161+
162+
if (segments && Object.keys(segments).length > 0 && (
163+
segments[checksum] || (segments[4] &&
164+
integrationConfig.iabAudienceCategories.v1_1.enabled &&
165+
!integrationConfig.iabContentCategories.v2_2.enabled &&
166+
!integrationConfig.iabContentCategories.v3_0.enabled
167+
)
168+
)) {
169+
logMessage(LOG_PREFIX, `Segtax data from localStorage: ${JSON.stringify(segments)}`);
170+
setSegmentsAsBidderOrtb2(reqBidsConfigObj, bidders, integrationConfig, segments, checksum);
171+
callback();
172+
} else if (window.raynJS && typeof window.raynJS.getSegtax === 'function') {
173+
window.raynJS.getSegtax().then((segtaxData) => {
174+
logMessage(LOG_PREFIX, `Segtax data from RaynJS: ${JSON.stringify(segtaxData)}`);
175+
setSegmentsAsBidderOrtb2(reqBidsConfigObj, bidders, integrationConfig, segtaxData, checksum);
176+
callback();
177+
}).catch((error) => {
178+
logError(LOG_PREFIX, error);
179+
callback();
180+
});
181+
} else {
182+
logMessage(LOG_PREFIX, 'No segtax data');
183+
callback();
184+
}
185+
} catch (error) {
186+
logError(LOG_PREFIX, error);
187+
callback();
188+
}
189+
}
190+
191+
export const raynSubmodule = {
192+
name: SUBMODULE_NAME,
193+
init: init,
194+
getBidRequestData: alterBidRequests,
195+
gvlid: RAYN_TCF_ID,
196+
};
197+
198+
submodule(MODULE_NAME, raynSubmodule);

0 commit comments

Comments
 (0)