Skip to content

Commit 39a92a4

Browse files
Alex's review part 2
1 parent 0528035 commit 39a92a4

File tree

3 files changed

+139
-63
lines changed

3 files changed

+139
-63
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Calculates the Time to First Byte (TTFB) for the given window object.
3+
*
4+
* This function attempts to use the Navigation Timing Level 2 API first, and falls back to
5+
* the Navigation Timing Level 1 API if the former is not available.
6+
*
7+
* @param {Window} win - The window object from which to retrieve performance timing information.
8+
* @returns {string} The TTFB in milliseconds as a string, or an empty string if the TTFB cannot be determined.
9+
*/
10+
export function getTimeToFirstByte(win) {
11+
const performance = win.performance || win.webkitPerformance || win.msPerformance || win.mozPerformance;
12+
13+
const ttfbWithTimingV2 = performance &&
14+
typeof performance.getEntriesByType === 'function' &&
15+
Object.prototype.toString.call(performance.getEntriesByType) === '[object Function]' &&
16+
performance.getEntriesByType('navigation')[0] &&
17+
performance.getEntriesByType('navigation')[0].responseStart &&
18+
performance.getEntriesByType('navigation')[0].requestStart &&
19+
performance.getEntriesByType('navigation')[0].responseStart > 0 &&
20+
performance.getEntriesByType('navigation')[0].requestStart > 0 &&
21+
Math.round(
22+
performance.getEntriesByType('navigation')[0].responseStart - performance.getEntriesByType('navigation')[0].requestStart
23+
);
24+
25+
if (ttfbWithTimingV2) {
26+
return ttfbWithTimingV2.toString();
27+
}
28+
29+
const ttfbWithTimingV1 = performance &&
30+
performance.timing.responseStart &&
31+
performance.timing.requestStart &&
32+
performance.timing.responseStart > 0 &&
33+
performance.timing.requestStart > 0 &&
34+
performance.timing.responseStart - performance.timing.requestStart;
35+
36+
return ttfbWithTimingV1 ? ttfbWithTimingV1.toString() : '';
37+
}

modules/greenbidsBidAdapter.js

Lines changed: 100 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getValue, logError, deepAccess, parseSizesInput, getBidIdParameter, log
22
import { registerBidder } from '../src/adapters/bidderFactory.js';
33
import { getStorageManager } from '../src/storageManager.js';
44
import { getDM, getHC, getHLen } from '../libraries/navigatorData/navigatorData.js';
5+
import { getTimeToFirstByte } from '../libraries/timeToFirstBytesUtils/timeToFirstBytesUtils.js';
56

67
/**
78
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -10,7 +11,7 @@ import { getDM, getHC, getHLen } from '../libraries/navigatorData/navigatorData.
1011

1112
const BIDDER_CODE = 'greenbids';
1213
const GVL_ID = 1232;
13-
const ENDPOINT_URL = 'https://d.greenbids.ai/hb/bid-request';
14+
const ENDPOINT_URL = 'https://hb.greenbids.ai';
1415
export const storage = getStorageManager({ bidderCode: BIDDER_CODE });
1516

1617
export const spec = {
@@ -40,7 +41,18 @@ export const spec = {
4041
* @return ServerRequest Info describing the request to the server.
4142
*/
4243
buildRequests: function (validBidRequests, bidderRequest) {
43-
const bids = validBidRequests.map(cleanBidsInfo);
44+
const bids = validBidRequests.map(bids => {
45+
const reqObj = {};
46+
let placementId = getValue(bids.params, 'placementId');
47+
const gpid = deepAccess(bids, 'ortb2Imp.ext.gpid');
48+
reqObj.sizes = getSizes(bids);
49+
reqObj.bidId = getBidIdParameter('bidId', bids);
50+
reqObj.bidderRequestId = getBidIdParameter('bidderRequestId', bids);
51+
reqObj.placementId = parseInt(placementId, 10);
52+
reqObj.adUnitCode = getBidIdParameter('adUnitCode', bids);
53+
reqObj.transactionId = bids.ortb2Imp?.ext?.tid || '';
54+
if (gpid) { reqObj.gpid = gpid; }
55+
});
4456
const topWindow = window.top;
4557

4658
const payload = {
@@ -70,8 +82,8 @@ export const spec = {
7082
payload.schain = firstBidRequest.schain;
7183
}
7284

73-
hydratePayloadWithGppData(payload, bidderRequest.gppConsent);
74-
hydratePayloadWithGdprData(payload, bidderRequest.gdprConsent);
85+
hydratePayloadWithGppConsentData(payload, bidderRequest.gppConsent);
86+
hydratePayloadWithGdprConsentData(payload, bidderRequest.gdprConsent);
7587
hydratePayloadWithUspConsentData(payload, bidderRequest.uspConsent);
7688

7789
const userAgentClientHints = deepAccess(firstBidRequest, 'ortb2.device.sua');
@@ -133,6 +145,15 @@ export const spec = {
133145
registerBidder(spec);
134146

135147
// Page info retrival
148+
149+
/**
150+
* Retrieves the referrer information from the bidder request.
151+
*
152+
* @param {Object} bidderRequest - The bidder request object.
153+
* @param {Object} [bidderRequest.refererInfo] - The referer information object.
154+
* @param {string} [bidderRequest.refererInfo.page] - The page URL of the referer.
155+
* @returns {string} The referrer URL if available, otherwise an empty string.
156+
*/
136157
function getReferrerInfo(bidderRequest) {
137158
let ref = '';
138159
if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.page) {
@@ -141,6 +162,15 @@ function getReferrerInfo(bidderRequest) {
141162
return ref;
142163
}
143164

165+
/**
166+
* Retrieves the title of the current web page.
167+
*
168+
* This function attempts to get the title from the top-level window's document.
169+
* If an error occurs (e.g., due to cross-origin restrictions), it falls back to the current document.
170+
* It first tries to get the title from the `og:title` meta tag, and if that is not available, it uses the document's title.
171+
*
172+
* @returns {string} The title of the current web page, or an empty string if no title is found.
173+
*/
144174
function getPageTitle() {
145175
try {
146176
const ogTitle = window.top.document.querySelector('meta[property="og:title"]');
@@ -151,6 +181,15 @@ function getPageTitle() {
151181
}
152182
}
153183

184+
/**
185+
* Retrieves the content of the page description meta tag.
186+
*
187+
* This function attempts to get the description from the top-level window's document.
188+
* If it fails (e.g., due to cross-origin restrictions), it falls back to the current document.
189+
* It looks for meta tags with either the name "description" or the property "og:description".
190+
*
191+
* @returns {string} The content of the description meta tag, or an empty string if not found.
192+
*/
154193
function getPageDescription() {
155194
try {
156195
const element = window.top.document.querySelector('meta[name="description"]') ||
@@ -163,73 +202,60 @@ function getPageDescription() {
163202
}
164203
}
165204

205+
/**
206+
* Retrieves the downlink speed of the user's network connection.
207+
*
208+
* @param {object} nav - The navigator object, typically `window.navigator`.
209+
* @returns {string} The downlink speed as a string if available, otherwise an empty string.
210+
*/
166211
function getConnectionDownLink(nav) {
167212
return nav && nav.connection && nav.connection.downlink >= 0 ? nav.connection.downlink.toString() : '';
168213
}
169214

170-
function getTimeToFirstByte(win) {
171-
const performance = win.performance || win.webkitPerformance || win.msPerformance || win.mozPerformance;
172-
173-
const ttfbWithTimingV2 = performance &&
174-
typeof performance.getEntriesByType === 'function' &&
175-
Object.prototype.toString.call(performance.getEntriesByType) === '[object Function]' &&
176-
performance.getEntriesByType('navigation')[0] &&
177-
performance.getEntriesByType('navigation')[0].responseStart &&
178-
performance.getEntriesByType('navigation')[0].requestStart &&
179-
performance.getEntriesByType('navigation')[0].responseStart > 0 &&
180-
performance.getEntriesByType('navigation')[0].requestStart > 0 &&
181-
Math.round(
182-
performance.getEntriesByType('navigation')[0].responseStart - performance.getEntriesByType('navigation')[0].requestStart
183-
);
184-
185-
if (ttfbWithTimingV2) {
186-
return ttfbWithTimingV2.toString();
187-
}
188-
189-
const ttfbWithTimingV1 = performance &&
190-
performance.timing.responseStart &&
191-
performance.timing.requestStart &&
192-
performance.timing.responseStart > 0 &&
193-
performance.timing.requestStart > 0 &&
194-
performance.timing.responseStart - performance.timing.requestStart;
195-
196-
return ttfbWithTimingV1 ? ttfbWithTimingV1.toString() : '';
197-
}
198-
199-
function cleanBidsInfo(bids) {
200-
const reqObj = {};
201-
let placementId = getValue(bids.params, 'placementId');
202-
const gpid = deepAccess(bids, 'ortb2Imp.ext.gpid');
203-
reqObj.sizes = getSizes(bids);
204-
reqObj.bidId = getBidIdParameter('bidId', bids);
205-
reqObj.bidderRequestId = getBidIdParameter('bidderRequestId', bids);
206-
reqObj.placementId = parseInt(placementId, 10);
207-
reqObj.adUnitCode = getBidIdParameter('adUnitCode', bids);
208-
reqObj.transactionId = bids.ortb2Imp?.ext?.tid || '';
209-
if (gpid) { reqObj.gpid = gpid; }
210-
return reqObj;
211-
}
212-
215+
/**
216+
* Converts the sizes from the bid object to the required format.
217+
*
218+
* @param {Object} bid - The bid object containing size information.
219+
* @param {Array} bid.sizes - The sizes array from the bid object.
220+
* @returns {Array} - The parsed sizes in the required format.
221+
*/
213222
function getSizes(bid) {
214223
return parseSizesInput(bid.sizes);
215224
}
216225

217226
// Privacy handling
218227

219-
export function hydratePayloadWithGppData(payload, gppData) {
220-
if (gppData) {
221-
let isValidConsentString = typeof gppData.gppString === 'string';
222-
let validateApplicableSections =
228+
/**
229+
* Hydrates the given payload with GPP consent data if available.
230+
*
231+
* @param {Object} payload - The payload object to be hydrated.
232+
* @param {Object} gppData - The GPP consent data object.
233+
* @param {string} gppData.gppString - The GPP consent string.
234+
* @param {number[]} gppData.applicableSections - An array of applicable section IDs.
235+
*/
236+
function hydratePayloadWithGppConsentData(payload, gppData) {
237+
if (!gppData) { return; }
238+
let isValidConsentString = typeof gppData.gppString === 'string';
239+
let validateApplicableSections =
223240
Array.isArray(gppData.applicableSections) &&
224241
gppData.applicableSections.every((section) => typeof (section) === 'number')
225-
payload.gpp = {
226-
consentString: isValidConsentString ? gppData.gppString : '',
227-
applicableSectionIds: validateApplicableSections ? gppData.applicableSections : [],
228-
};
229-
}
242+
payload.gpp = {
243+
consentString: isValidConsentString ? gppData.gppString : '',
244+
applicableSectionIds: validateApplicableSections ? gppData.applicableSections : [],
245+
};
230246
}
231247

232-
export function hydratePayloadWithGdprData(payload, gdprData) {
248+
/**
249+
* Hydrates the given payload with GDPR consent data if available.
250+
*
251+
* @param {Object} payload - The payload object to be hydrated with GDPR consent data.
252+
* @param {Object} gdprData - The GDPR data object containing consent information.
253+
* @param {boolean} gdprData.gdprApplies - Indicates if GDPR applies.
254+
* @param {string} gdprData.consentString - The GDPR consent string.
255+
* @param {number} gdprData.apiVersion - The version of the GDPR API being used.
256+
* @param {Object} gdprData.vendorData - Additional vendor data related to GDPR.
257+
*/
258+
function hydratePayloadWithGdprConsentData(payload, gdprData) {
233259
if (!gdprData) { return; }
234260
let isCmp = typeof gdprData.gdprApplies === 'boolean';
235261
let isConsentString = typeof gdprData.consentString === 'string';
@@ -243,10 +269,15 @@ export function hydratePayloadWithGdprData(payload, gdprData) {
243269
};
244270
}
245271

246-
export function hydratePayloadWithUspConsentData(payload, uspConsentData) {
247-
if (uspConsentData) {
248-
payload.us_privacy = uspConsentData;
249-
}
272+
/**
273+
* Adds USP (CCPA) consent data to the payload if available.
274+
*
275+
* @param {Object} payload - The payload object to be hydrated with USP consent data.
276+
* @param {string} uspConsentData - The USP consent string to be added to the payload.
277+
*/
278+
function hydratePayloadWithUspConsentData(payload, uspConsentData) {
279+
if (!uspConsentData) { return; }
280+
payload.us_privacy = uspConsentData;
250281
}
251282

252283
const gdprStatus = {
@@ -256,6 +287,14 @@ const gdprStatus = {
256287
CMP_NOT_FOUND_OR_ERROR: 22
257288
};
258289

290+
/**
291+
* Determines the GDPR status based on whether GDPR applies and the provided GDPR data.
292+
*
293+
* @param {boolean} gdprApplies - Indicates if GDPR applies.
294+
* @param {Object} gdprData - The GDPR data object.
295+
* @param {boolean} gdprData.isServiceSpecific - Indicates if the GDPR data is service-specific.
296+
* @returns {string} The GDPR status.
297+
*/
259298
function findGdprStatus(gdprApplies, gdprData) {
260299
let status = gdprStatus.GDPR_APPLIES_PUBLISHER;
261300
if (gdprApplies) {

modules/greenbidsBidAdapter.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Use `greenbids` as bidder.
1616
bids: [{
1717
bidder: 'greenbids',
1818
params: {
19-
gbPlacementId: 12345,
19+
placementId: 12345,
2020
}
2121
}]
2222
},{
@@ -25,7 +25,7 @@ Use `greenbids` as bidder.
2525
bids: [{
2626
bidder: 'greenbids',
2727
params: {
28-
gbPlacementId: 12345,
28+
placementId: 12345,
2929
}
3030
}]
3131
}];

0 commit comments

Comments
 (0)