From 69a9ea7008adb7316b86769c784ff793bfc747dd Mon Sep 17 00:00:00 2001 From: Gabriel Chicoye Date: Wed, 8 Oct 2025 11:01:40 +0200 Subject: [PATCH 1/3] typescript conversion & ybidder alias added --- libraries/nexx360Utils/{index.js => index.ts} | 124 ++++++++++------ modules/adgridBidAdapter.js | 133 ------------------ modules/adgridBidAdapter.ts | 110 +++++++++++++++ ...x360BidAdapter.js => nexx360BidAdapter.ts} | 122 +++++++--------- test/spec/modules/adgridBidAdapter_spec.js | 17 +-- test/spec/modules/nexx360BidAdapter_spec.js | 16 ++- 6 files changed, 263 insertions(+), 259 deletions(-) rename libraries/nexx360Utils/{index.js => index.ts} (53%) delete mode 100644 modules/adgridBidAdapter.js create mode 100644 modules/adgridBidAdapter.ts rename modules/{nexx360BidAdapter.js => nexx360BidAdapter.ts} (64%) diff --git a/libraries/nexx360Utils/index.js b/libraries/nexx360Utils/index.ts similarity index 53% rename from libraries/nexx360Utils/index.js rename to libraries/nexx360Utils/index.ts index b7423148204..60c0a9418fb 100644 --- a/libraries/nexx360Utils/index.js +++ b/libraries/nexx360Utils/index.ts @@ -1,31 +1,63 @@ -import { deepAccess, deepSetValue, logInfo } from '../../src/utils.js'; +import { deepAccess, deepSetValue, generateUUID, logInfo } from '../../src/utils.js'; import {Renderer} from '../../src/Renderer.js'; import { getCurrencyFromBidderRequest } from '../ortb2Utils/currency.js'; import { INSTREAM, OUTSTREAM } from '../../src/video.js'; -import { BANNER, NATIVE } from '../../src/mediaTypes.js'; +import { BANNER, MediaType, NATIVE, VIDEO } from '../../src/mediaTypes.js'; +import { BidResponse } from '../../src/bidfactory.js'; +import { StorageManager } from '../../src/storageManager.js'; +import { ORTBRequest } from '../../src/prebid.public.js'; const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; -/** - * Register the user sync pixels which should be dropped after the auction. - * - /** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - * - */ +let sessionId:string | null = null; + +const getSessionId = ():string => { + if (!sessionId) { + sessionId = generateUUID(); + } + return sessionId; +} + +let lastPageUrl:string = ''; +let requestCounter:number = 0; + +const getRequestCount = ():number => { + if (lastPageUrl === window.location.pathname) { + return ++requestCounter; + } + lastPageUrl = window.location.pathname; + return 0; +} + +export const getLocalStorageFunctionGenerator = < + T extends Record +>( + storage: StorageManager, + bidderCode: string, + storageKey: string, + jsonKey: keyof T + ): (() => T | null) => { + return () => { + if (!storage.localStorageIsEnabled()) { + logInfo(`localstorage not enabled for ${bidderCode}`); + return null; + } + + const output = storage.getDataFromLocalStorage(storageKey); + if (output === null) { + const storageElement: T = { [jsonKey]: generateUUID() } as T; + storage.setDataInLocalStorage(storageKey, JSON.stringify(storageElement)); + return storageElement; + } + try { + return JSON.parse(output) as T; + } catch (e) { + logInfo(`failed to parse localstorage for ${bidderCode}:`, e); + return null; + } + }; +}; -/** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ export function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { if (typeof serverResponses === 'object' && serverResponses != null && @@ -40,19 +72,19 @@ export function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConse } }; -function outstreamRender(response) { - response.renderer.push(() => { - window.ANOutstreamVideo.renderAd({ - sizes: [response.width, response.height], - targetId: response.divId, - adResponse: response.vastXml, +const outstreamRender = (response: BidResponse): void => { + (response as any).renderer.push(() => { + (window as any).ANOutstreamVideo.renderAd({ + sizes: [(response as any).width, (response as any).height], + targetId: (response as any).divId, + adResponse: (response as any).vastXml, rendererOptions: { showBigPlayButton: false, showProgressBar: 'bar', showVolume: false, allowFullscreen: true, skippable: false, - content: response.vastXml + content: (response as any).vastXml } }); }); @@ -84,13 +116,17 @@ export function enrichImp(imp, bidRequest) { return imp; } -export function enrichRequest(request, amxId, bidderRequest, pageViewId, bidderVersion) { +export const enrichRequest = ( + request: ORTBRequest, + amxId: string | null, + pageViewId: string, + bidderVersion: string):ORTBRequest => { if (amxId) { deepSetValue(request, 'ext.localStorage.amxId', amxId); if (!request.user) request.user = {}; if (!request.user.ext) request.user.ext = {}; if (!request.user.ext.eids) request.user.ext.eids = []; - request.user.ext.eids.push({ + (request.user.ext.eids as any).push({ source: 'amxdt.net', uids: [{ id: `${amxId}`, @@ -102,22 +138,27 @@ export function enrichRequest(request, amxId, bidderRequest, pageViewId, bidderV deepSetValue(request, 'ext.source', 'prebid.js'); deepSetValue(request, 'ext.pageViewId', pageViewId); deepSetValue(request, 'ext.bidderVersion', bidderVersion); - deepSetValue(request, 'cur', [getCurrencyFromBidderRequest(bidderRequest) || 'USD']); + deepSetValue(request, 'ext.sessionId', getSessionId()); + deepSetValue(request, 'ext.requestCounter', getRequestCount()); + deepSetValue(request, 'cur', [getCurrencyFromBidderRequest(request) || 'USD']); if (!request.user) request.user = {}; return request; }; -export function createResponse(bid, respBody) { - const response = { +export function createResponse(bid:any, ortbResponse:any): BidResponse { + let mediaType: MediaType = BANNER; + if ([INSTREAM, OUTSTREAM].includes(bid.ext.mediaType as string)) mediaType = VIDEO; + if (bid.ext.mediaType === NATIVE) mediaType = NATIVE; + const response:any = { requestId: bid.impid, cpm: bid.price, width: bid.w, height: bid.h, creativeId: bid.crid, - currency: respBody.cur, + currency: ortbResponse.cur, netRevenue: true, ttl: 120, - mediaType: [OUTSTREAM, INSTREAM].includes(bid.ext.mediaType) ? 'video' : bid.ext.mediaType, + mediaType, meta: { advertiserDomains: bid.adomain, demandSource: bid.ext.ssp, @@ -126,7 +167,7 @@ export function createResponse(bid, respBody) { if (bid.dealid) response.dealid = bid.dealid; if (bid.ext.mediaType === BANNER) response.ad = bid.adm; - if ([INSTREAM, OUTSTREAM].includes(bid.ext.mediaType)) response.vastXml = bid.adm; + if ([INSTREAM, OUTSTREAM].includes(bid.ext.mediaType as string)) response.vastXml = bid.adm; if (bid.ext.mediaType === OUTSTREAM) { response.renderer = createRenderer(bid, OUTSTREAM_RENDERER_URL); @@ -138,18 +179,21 @@ export function createResponse(bid, respBody) { response.native = { ortb: JSON.parse(bid.adm) } } catch (e) {} } - return response; + return response as BidResponse; } /** * Get the AMX ID * @return { string | false } false if localstorageNotEnabled */ -export function getAmxId(storage, bidderCode) { +export const getAmxId = ( + storage: StorageManager, + bidderCode: string +): string | null => { if (!storage.localStorageIsEnabled()) { logInfo(`localstorage not enabled for ${bidderCode}`); - return false; + return null; } const amxId = storage.getDataFromLocalStorage('__amuidpb'); - return amxId || false; + return amxId || null; } diff --git a/modules/adgridBidAdapter.js b/modules/adgridBidAdapter.js deleted file mode 100644 index d1cccf21c52..00000000000 --- a/modules/adgridBidAdapter.js +++ /dev/null @@ -1,133 +0,0 @@ -import { deepSetValue, generateUUID, logInfo } from '../src/utils.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { ortbConverter } from '../libraries/ortbConverter/converter.js' -import { createResponse, enrichImp, enrichRequest, getAmxId, getUserSyncs } from '../libraries/nexx360Utils/index.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - -const BIDDER_CODE = 'adgrid'; -const REQUEST_URL = 'https://fast.nexx360.io/adgrid'; -const PAGE_VIEW_ID = generateUUID(); -const BIDDER_VERSION = '2.0'; -const ADGRID_KEY = 'adgrid'; - -const ALIASES = []; - -// Define the storage manager for the Adgrid bidder -export const STORAGE = getStorageManager({ - bidderCode: BIDDER_CODE, -}); - -/** - * Get the agdridId from local storage - * @return {object | false } false if localstorageNotEnabled - */ -export function getLocalStorage() { - if (!STORAGE.localStorageIsEnabled()) { - logInfo(`localstorage not enabled for Adgrid`); - return false; - } - const output = STORAGE.getDataFromLocalStorage(ADGRID_KEY); - if (output === null) { - const adgridStorage = { adgridId: generateUUID() }; - STORAGE.setDataInLocalStorage(ADGRID_KEY, JSON.stringify(adgridStorage)); - return adgridStorage; - } - try { - return JSON.parse(output); - } catch (e) { - return false; - } -} - -const converter = ortbConverter({ - context: { - netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false - ttl: 90, // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) - }, - imp(buildImp, bidRequest, context) { - let imp = buildImp(bidRequest, context); - imp = enrichImp(imp, bidRequest); - if (bidRequest.params.domainId) deepSetValue(imp, 'ext.adgrid.domainId', bidRequest.params.domainId); - if (bidRequest.params.placement) deepSetValue(imp, 'ext.adgrid.placement', bidRequest.params.placement); - return imp; - }, - request(buildRequest, imps, bidderRequest, context) { - let request = buildRequest(imps, bidderRequest, context); - const amxId = getAmxId(STORAGE, BIDDER_CODE); - request = enrichRequest(request, amxId, bidderRequest, PAGE_VIEW_ID, BIDDER_VERSION); - return request; - }, -}); - -/** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ -function isBidRequestValid(bid) { - if (!bid || !bid.params) return false; - if (typeof bid.params.domainId !== 'number') return false; - if (typeof bid.params.placement !== 'string') return false; - return true; -} - -/** - * Make a server request from the list of BidRequests. - * - * @return ServerRequest Info describing the request to the server. - */ -function buildRequests(bidRequests, bidderRequest) { - const data = converter.toORTB({ bidRequests, bidderRequest }) - return { - method: 'POST', - url: REQUEST_URL, - data, - } -} - -/** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ -function interpretResponse(serverResponse) { - const respBody = serverResponse.body; - if (!respBody || !Array.isArray(respBody.seatbid)) { - return []; - } - - const responses = []; - for (let i = 0; i < respBody.seatbid.length; i++) { - const seatbid = respBody.seatbid[i]; - for (let j = 0; j < seatbid.bid.length; j++) { - const bid = seatbid.bid[j]; - const response = createResponse(bid, respBody); - responses.push(response); - } - } - return responses; -} - -export const spec = { - code: BIDDER_CODE, - aliases: ALIASES, - supportedMediaTypes: [BANNER, VIDEO], - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs, -}; - -registerBidder(spec); diff --git a/modules/adgridBidAdapter.ts b/modules/adgridBidAdapter.ts new file mode 100644 index 00000000000..aa1b0752be9 --- /dev/null +++ b/modules/adgridBidAdapter.ts @@ -0,0 +1,110 @@ +import { deepSetValue, generateUUID } from '../src/utils.js'; +import { getStorageManager, StorageManager } from '../src/storageManager.js'; +import { AdapterRequest, AdapterResponse, BidderSpec, registerBidder, ServerResponse } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' +import { BidRequest, ClientBidderRequest } from '../src/adapterManager.js'; +import { createResponse, enrichImp, enrichRequest, getAmxId, getLocalStorageFunctionGenerator, getUserSyncs } from '../libraries/nexx360Utils/index.js'; +import { ORTBRequest, ORTBResponse } from '../src/prebid.public.js'; +import { BidResponse } from '../src/bidfactory.js'; + +const BIDDER_CODE = 'adgrid'; +const REQUEST_URL = 'https://fast.nexx360.io/adgrid'; +const PAGE_VIEW_ID = generateUUID(); +const BIDDER_VERSION = '2.0'; +const ADGRID_KEY = 'adgrid'; + +type AdgridBidParams = { + domainId?: string; + placement?: string; + allBids?: boolean; +} + +declare module '../src/adUnits' { + interface BidderParams { + [BIDDER_CODE]: AdgridBidParams; + } +} + +const ALIASES = []; + +// Define the storage manager for the Adgrid bidder +export const STORAGE: StorageManager = getStorageManager({ + bidderCode: BIDDER_CODE, +}); + +export const getAdgridLocalStorage = getLocalStorageFunctionGenerator<{ adgridId: string }>( + STORAGE, + BIDDER_CODE, + ADGRID_KEY, + 'adgridId' +); + +const converter = ortbConverter({ + context: { + netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false + ttl: 90, // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) + }, + imp(buildImp, bidRequest, context) { + let imp = buildImp(bidRequest, context); + imp = enrichImp(imp, bidRequest); + if (bidRequest.params.domainId) deepSetValue(imp, 'ext.adgrid.domainId', bidRequest.params.domainId); + if (bidRequest.params.placement) deepSetValue(imp, 'ext.adgrid.placement', bidRequest.params.placement); + return imp; + }, + request(buildRequest, imps, bidderRequest, context) { + let request = buildRequest(imps, bidderRequest, context); + const amxId = getAmxId(STORAGE, BIDDER_CODE); + request = enrichRequest(request, amxId, PAGE_VIEW_ID, BIDDER_VERSION); + return request; + }, +}); + +const isBidRequestValid = (bid:BidRequest): boolean => { + if (!bid || !bid.params) return false; + if (typeof bid.params.domainId !== 'number') return false; + if (typeof bid.params.placement !== 'string') return false; + return true; +} + +const buildRequests = ( + bidRequests: BidRequest[], + bidderRequest: ClientBidderRequest, +): AdapterRequest => { + const data:ORTBRequest = converter.toORTB({bidRequests, bidderRequest}) + const adapterRequest:AdapterRequest = { + method: 'POST', + url: REQUEST_URL, + data, + } + return adapterRequest; +} + +const interpretResponse = (serverResponse: ServerResponse): AdapterResponse => { + if (!serverResponse.body) return []; + const respBody = serverResponse.body as ORTBResponse; + if (!respBody.seatbid || respBody.seatbid.length === 0) return []; + + const responses: BidResponse[] = []; + for (let i = 0; i < respBody.seatbid.length; i++) { + const seatbid = respBody.seatbid[i]; + for (let j = 0; j < seatbid.bid.length; j++) { + const bid = seatbid.bid[j]; + const response:BidResponse = createResponse(bid, respBody); + responses.push(response); + } + } + return responses; +} + +export const spec:BidderSpec = { + code: BIDDER_CODE, + aliases: ALIASES, + supportedMediaTypes: [BANNER, VIDEO], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs, +}; + +registerBidder(spec); diff --git a/modules/nexx360BidAdapter.js b/modules/nexx360BidAdapter.ts similarity index 64% rename from modules/nexx360BidAdapter.js rename to modules/nexx360BidAdapter.ts index c89238b9242..345b419cc26 100644 --- a/modules/nexx360BidAdapter.js +++ b/modules/nexx360BidAdapter.ts @@ -1,29 +1,41 @@ -import { deepSetValue, generateUUID, logError, logInfo } from '../src/utils.js'; +import { deepSetValue, generateUUID, logError } from '../src/utils.js'; import {getStorageManager} from '../src/storageManager.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {AdapterRequest, AdapterResponse, BidderSpec, registerBidder, ServerResponse} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {getGlobal} from '../src/prebidGlobal.js'; import {ortbConverter} from '../libraries/ortbConverter/converter.js' -import { createResponse, enrichImp, enrichRequest, getAmxId, getUserSyncs } from '../libraries/nexx360Utils/index.js'; +import { createResponse, enrichImp, enrichRequest, getAmxId, getLocalStorageFunctionGenerator, getUserSyncs } from '../libraries/nexx360Utils/index.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ +import { BidRequest, ClientBidderRequest } from '../src/adapterManager.js'; +import { ORTBImp, ORTBRequest, ORTBResponse } from '../src/prebid.public.js'; +import { BidResponse } from '../src/bidfactory.js'; const BIDDER_CODE = 'nexx360'; const REQUEST_URL = 'https://fast.nexx360.io/booster'; const PAGE_VIEW_ID = generateUUID(); -const BIDDER_VERSION = '6.3'; +const BIDDER_VERSION = '7.0'; const GVLID = 965; const NEXXID_KEY = 'nexx360_storage'; +type Nexx360BidParams = { + tagId?: string; + placement?: string; + videoTagId?: string; + nativeTagId?: string; + adUnitPath?: string; + adUnitName?: string; + divId?: string; + allBids?: boolean; + customId?: string; +} + +declare module '../src/adUnits' { + interface BidderParams { + [BIDDER_CODE]: Nexx360BidParams; + } +} + const ALIASES = [ { code: 'revenuemaker' }, { code: 'first-id', gvlid: 1178 }, @@ -41,35 +53,19 @@ const ALIASES = [ { code: 'glomexbidder', gvlid: 967 }, { code: 'revnew', gvlid: 1468 }, { code: 'pubxai', gvlid: 1485 }, + { code: 'ybidder', gvlid: 1253 }, ]; export const STORAGE = getStorageManager({ bidderCode: BIDDER_CODE, }); -/** - * Get the NexxId - * @param - * @return {object | false } false if localstorageNotEnabled - */ - -export function getNexx360LocalStorage() { - if (!STORAGE.localStorageIsEnabled()) { - logInfo(`localstorage not enabled for Nexx360`); - return false; - } - const output = STORAGE.getDataFromLocalStorage(NEXXID_KEY); - if (output === null) { - const nexx360Storage = { nexx360Id: generateUUID() }; - STORAGE.setDataInLocalStorage(NEXXID_KEY, JSON.stringify(nexx360Storage)); - return nexx360Storage; - } - try { - return JSON.parse(output) - } catch (e) { - return false; - } -} +export const getNexx360LocalStorage = getLocalStorageFunctionGenerator<{ nexx360Id: string }>( + STORAGE, + BIDDER_CODE, + NEXXID_KEY, + 'nexx360Id' +); const converter = ortbConverter({ context: { @@ -77,10 +73,10 @@ const converter = ortbConverter({ ttl: 90, // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) }, imp(buildImp, bidRequest, context) { - let imp = buildImp(bidRequest, context); + let imp:ORTBImp = buildImp(bidRequest, context); imp = enrichImp(imp, bidRequest); const divId = bidRequest.params.divId || bidRequest.adUnitCode; - const slotEl = document.getElementById(divId); + const slotEl:HTMLElement | null = typeof divId === 'string' ? document.getElementById(divId) : null; if (slotEl) { const { width, height } = getBoundingClientRect(slotEl); deepSetValue(imp, 'ext.dimensions.slotW', width); @@ -97,20 +93,14 @@ const converter = ortbConverter({ return imp; }, request(buildRequest, imps, bidderRequest, context) { - let request = buildRequest(imps, bidderRequest, context); + let request:ORTBRequest = buildRequest(imps, bidderRequest, context); const amxId = getAmxId(STORAGE, BIDDER_CODE); - request = enrichRequest(request, amxId, bidderRequest, PAGE_VIEW_ID, BIDDER_VERSION); + request = enrichRequest(request, amxId, PAGE_VIEW_ID, BIDDER_VERSION); return request; }, }); -/** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ -function isBidRequestValid(bid) { +const isBidRequestValid = (bid:BidRequest): boolean => { if (bid.params.adUnitName && (typeof bid.params.adUnitName !== 'string' || bid.params.adUnitName === '')) { logError('bid.params.adUnitName needs to be a string'); return false; @@ -134,43 +124,33 @@ function isBidRequestValid(bid) { return true; }; -/** - * Make a server request from the list of BidRequests. - * - * @return ServerRequest Info describing the request to the server. - */ - -function buildRequests(bidRequests, bidderRequest) { - const data = converter.toORTB({bidRequests, bidderRequest}) - return { +const buildRequests = ( + bidRequests: BidRequest[], + bidderRequest: ClientBidderRequest, +): AdapterRequest => { + const data:ORTBRequest = converter.toORTB({bidRequests, bidderRequest}) + const adapterRequest:AdapterRequest = { method: 'POST', url: REQUEST_URL, data, } + return adapterRequest; } -/** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ - -function interpretResponse(serverResponse) { - const respBody = serverResponse.body; - if (!respBody || !Array.isArray(respBody.seatbid)) { - return []; - } +const interpretResponse = (serverResponse: ServerResponse): AdapterResponse => { + if (!serverResponse.body) return []; + const respBody = serverResponse.body as ORTBResponse; + if (!respBody.seatbid || respBody.seatbid.length === 0) return []; const { bidderSettings } = getGlobal(); const allowAlternateBidderCodes = bidderSettings && bidderSettings.standard ? bidderSettings.standard.allowAlternateBidderCodes : false; - const responses = []; + const responses: BidResponse[] = []; for (let i = 0; i < respBody.seatbid.length; i++) { const seatbid = respBody.seatbid[i]; for (let j = 0; j < seatbid.bid.length; j++) { const bid = seatbid.bid[j]; - const response = createResponse(bid, respBody); + const response:BidResponse = createResponse(bid, respBody); if (allowAlternateBidderCodes) response.bidderCode = `n360_${bid.ext.ssp}`; responses.push(response); } @@ -178,7 +158,7 @@ function interpretResponse(serverResponse) { return responses; } -export const spec = { +export const spec:BidderSpec = { code: BIDDER_CODE, gvlid: GVLID, aliases: ALIASES, diff --git a/test/spec/modules/adgridBidAdapter_spec.js b/test/spec/modules/adgridBidAdapter_spec.js index 5b6d658e1ca..65f0e45a6ae 100644 --- a/test/spec/modules/adgridBidAdapter_spec.js +++ b/test/spec/modules/adgridBidAdapter_spec.js @@ -1,9 +1,8 @@ import { expect } from 'chai'; import { - spec, STORAGE, getLocalStorage, + spec, STORAGE, getAdgridLocalStorage, } from 'modules/adgridBidAdapter.js'; import sinon from 'sinon'; -import { getAmxId } from '../../../libraries/nexx360Utils/index.js'; const sandbox = sinon.createSandbox(); describe('adgrid bid adapter tests', () => { @@ -74,8 +73,8 @@ describe('adgrid bid adapter tests', () => { sandbox.stub(STORAGE, 'localStorageIsEnabled').callsFake(() => false); }); it('We test if we get the adgridId', () => { - const output = getLocalStorage(); - expect(output).to.be.eql(false); + const output = getAdgridLocalStorage(); + expect(output).to.be.eql(null); }); after(() => { sandbox.restore() @@ -89,7 +88,7 @@ describe('adgrid bid adapter tests', () => { sandbox.stub(STORAGE, 'getDataFromLocalStorage').callsFake((key) => null); }); it('We test if we get the adgridId', () => { - const output = getLocalStorage(); + const output = getAdgridLocalStorage(); expect(typeof output.adgridId).to.be.eql('string'); }); after(() => { @@ -104,8 +103,8 @@ describe('adgrid bid adapter tests', () => { sandbox.stub(STORAGE, 'getDataFromLocalStorage').callsFake((key) => '{"adgridId":"5ad89a6e-7801-48e7-97bb-fe6f251f6cb4",}'); }); it('We test if we get the adgridId', () => { - const output = getLocalStorage(); - expect(output).to.be.eql(false); + const output = getAdgridLocalStorage(); + expect(output).to.be.eql(null); }); after(() => { sandbox.restore() @@ -119,7 +118,7 @@ describe('adgrid bid adapter tests', () => { sandbox.stub(STORAGE, 'getDataFromLocalStorage').callsFake((key) => '{"adgridId":"5ad89a6e-7801-48e7-97bb-fe6f251f6cb4"}'); }); it('We test if we get the adgridId', () => { - const output = getLocalStorage(); + const output = getAdgridLocalStorage(); expect(output.adgridId).to.be.eql('5ad89a6e-7801-48e7-97bb-fe6f251f6cb4'); }); after(() => { @@ -299,6 +298,8 @@ describe('adgrid bid adapter tests', () => { source: 'prebid.js', pageViewId: requestContent.ext.pageViewId, bidderVersion: '2.0', + requestCounter: 0, + sessionId: requestContent.ext.sessionId, }, cur: [ 'USD', diff --git a/test/spec/modules/nexx360BidAdapter_spec.js b/test/spec/modules/nexx360BidAdapter_spec.js index 7756e96bd99..93d1fcc6357 100644 --- a/test/spec/modules/nexx360BidAdapter_spec.js +++ b/test/spec/modules/nexx360BidAdapter_spec.js @@ -95,12 +95,12 @@ describe('Nexx360 bid adapter tests', () => { }); it('We test if we get the nexx360Id', () => { const output = getNexx360LocalStorage(); - expect(output).to.be.eql(false); + expect(output).to.be.eql(null); }); after(() => { sandbox.restore() }); - }) + }); describe('getNexx360LocalStorage enabled but nothing', () => { before(() => { @@ -115,7 +115,7 @@ describe('Nexx360 bid adapter tests', () => { after(() => { sandbox.restore() }); - }) + }); describe('getNexx360LocalStorage enabled but wrong payload', () => { before(() => { @@ -125,7 +125,7 @@ describe('Nexx360 bid adapter tests', () => { }); it('We test if we get the nexx360Id', () => { const output = getNexx360LocalStorage(); - expect(output).to.be.eql(false); + expect(output).to.be.eql(null); }); after(() => { sandbox.restore() @@ -155,7 +155,7 @@ describe('Nexx360 bid adapter tests', () => { }); it('We test if we get the amxId', () => { const output = getAmxId(STORAGE, 'nexx360'); - expect(output).to.be.eql(false); + expect(output).to.be.eql(null); }); after(() => { sandbox.restore() @@ -335,8 +335,10 @@ describe('Nexx360 bid adapter tests', () => { version: requestContent.ext.version, source: 'prebid.js', pageViewId: requestContent.ext.pageViewId, - bidderVersion: '6.3', - localStorage: { amxId: 'abcdef'} + bidderVersion: '7.0', + localStorage: { amxId: 'abcdef'}, + sessionId: requestContent.ext.sessionId, + requestCounter: 0, }, cur: [ 'USD', From 4397256ca85085fe4c4203dcfaffa069704e054d Mon Sep 17 00:00:00 2001 From: Gabriel Chicoye Date: Fri, 10 Oct 2025 16:57:19 +0200 Subject: [PATCH 2/3] fixes --- libraries/nexx360Utils/index.ts | 89 ++++++++++++++++----- modules/adgridBidAdapter.ts | 42 ++++------ modules/nexx360BidAdapter.ts | 58 +++++++------- test/spec/modules/adgridBidAdapter_spec.js | 2 + test/spec/modules/nexx360BidAdapter_spec.js | 9 ++- 5 files changed, 124 insertions(+), 76 deletions(-) diff --git a/libraries/nexx360Utils/index.ts b/libraries/nexx360Utils/index.ts index 60c0a9418fb..dec3ce47056 100644 --- a/libraries/nexx360Utils/index.ts +++ b/libraries/nexx360Utils/index.ts @@ -3,9 +3,10 @@ import {Renderer} from '../../src/Renderer.js'; import { getCurrencyFromBidderRequest } from '../ortb2Utils/currency.js'; import { INSTREAM, OUTSTREAM } from '../../src/video.js'; import { BANNER, MediaType, NATIVE, VIDEO } from '../../src/mediaTypes.js'; -import { BidResponse } from '../../src/bidfactory.js'; +import { BidResponse, VideoBidResponse } from '../../src/bidfactory.js'; import { StorageManager } from '../../src/storageManager.js'; -import { ORTBRequest } from '../../src/prebid.public.js'; +import { BidRequest, ORTBRequest, ORTBResponse } from '../../src/prebid.public.js'; +import { AdapterResponse, ServerResponse } from '../../src/adapters/bidderFactory.js'; const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; @@ -72,37 +73,56 @@ export function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConse } }; -const outstreamRender = (response: BidResponse): void => { - (response as any).renderer.push(() => { +const createOustreamRendererFunction = ( + divId: string, + width: number, + height: number +) => (bidResponse: VideoBidResponse) => { + bidResponse.renderer.push(() => { (window as any).ANOutstreamVideo.renderAd({ - sizes: [(response as any).width, (response as any).height], - targetId: (response as any).divId, - adResponse: (response as any).vastXml, + sizes: [width, height], + targetId: divId, + adResponse: bidResponse.vastXml, rendererOptions: { showBigPlayButton: false, showProgressBar: 'bar', showVolume: false, allowFullscreen: true, skippable: false, - content: (response as any).vastXml + content: bidResponse.vastXml } }); }); }; -export function createRenderer(bid, url) { - const renderer = Renderer.install({ - id: bid.id, - url: url, +export type CreateRenderPayload = { + requestId: string, + vastXml: string, + divId: string, + width: number, + height: number +} + +export const createRenderer = ( + { requestId, vastXml, divId, width, height }: CreateRenderPayload +): Renderer | undefined => { + if (!vastXml) { + logInfo('No VAST in bidResponse'); + return; + } + const installPayload = { + id: requestId, + url: OUTSTREAM_RENDERER_URL, loaded: false, - adUnitCode: bid.ext.adUnitCode, - targetId: bid.ext.divId, - }); - renderer.setRender(outstreamRender); + adUnitCode: divId, + targetId: divId, + }; + const renderer = Renderer.install(installPayload); + renderer.setRender(createOustreamRendererFunction(divId, width, height)); return renderer; }; -export function enrichImp(imp, bidRequest) { +export const enrichImp = (imp, bidRequest:BidRequest) => { deepSetValue(imp, 'tagid', bidRequest.adUnitCode); deepSetValue(imp, 'ext.adUnitCode', bidRequest.adUnitCode); const divId = bidRequest.params.divId || bidRequest.adUnitCode; @@ -168,10 +188,20 @@ export function createResponse(bid:any, ortbResponse:any): BidResponse { if (bid.ext.mediaType === BANNER) response.ad = bid.adm; if ([INSTREAM, OUTSTREAM].includes(bid.ext.mediaType as string)) response.vastXml = bid.adm; - - if (bid.ext.mediaType === OUTSTREAM) { - response.renderer = createRenderer(bid, OUTSTREAM_RENDERER_URL); - if (bid.ext.divId) response.divId = bid.ext.divId + if (bid.ext.mediaType === OUTSTREAM && (bid.ext.divId || bid.ext.adUnitCode)) { + const renderer = createRenderer({ + requestId: response.requestId, + vastXml: response.vastXml, + divId: bid.ext.divId || bid.ext.adUnitCode, + width: response.width, + height: response.height + }); + if (renderer) { + response.renderer = renderer; + response.divId = bid.ext.divId; + } else { + logInfo('Could not create renderer for outstream bid'); + } }; if (bid.ext.mediaType === NATIVE) { @@ -182,6 +212,23 @@ export function createResponse(bid:any, ortbResponse:any): BidResponse { return response as BidResponse; } +export const interpretResponse = (serverResponse: ServerResponse): AdapterResponse => { + if (!serverResponse.body) return []; + const respBody = serverResponse.body as ORTBResponse; + if (!respBody.seatbid || respBody.seatbid.length === 0) return []; + + const responses: BidResponse[] = []; + for (let i = 0; i < respBody.seatbid.length; i++) { + const seatbid = respBody.seatbid[i]; + for (let j = 0; j < seatbid.bid.length; j++) { + const bid = seatbid.bid[j]; + const response:BidResponse = createResponse(bid, respBody); + responses.push(response); + } + } + return responses; +} + /** * Get the AMX ID * @return { string | false } false if localstorageNotEnabled diff --git a/modules/adgridBidAdapter.ts b/modules/adgridBidAdapter.ts index aa1b0752be9..e5ba2c8b672 100644 --- a/modules/adgridBidAdapter.ts +++ b/modules/adgridBidAdapter.ts @@ -1,12 +1,11 @@ import { deepSetValue, generateUUID } from '../src/utils.js'; import { getStorageManager, StorageManager } from '../src/storageManager.js'; -import { AdapterRequest, AdapterResponse, BidderSpec, registerBidder, ServerResponse } from '../src/adapters/bidderFactory.js'; +import { AdapterRequest, BidderSpec, registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js' import { BidRequest, ClientBidderRequest } from '../src/adapterManager.js'; -import { createResponse, enrichImp, enrichRequest, getAmxId, getLocalStorageFunctionGenerator, getUserSyncs } from '../libraries/nexx360Utils/index.js'; -import { ORTBRequest, ORTBResponse } from '../src/prebid.public.js'; -import { BidResponse } from '../src/bidfactory.js'; +import { interpretResponse, enrichImp, enrichRequest, getAmxId, getLocalStorageFunctionGenerator, getUserSyncs } from '../libraries/nexx360Utils/index.js'; +import { ORTBRequest } from '../src/prebid.public.js'; const BIDDER_CODE = 'adgrid'; const REQUEST_URL = 'https://fast.nexx360.io/adgrid'; @@ -14,11 +13,18 @@ const PAGE_VIEW_ID = generateUUID(); const BIDDER_VERSION = '2.0'; const ADGRID_KEY = 'adgrid'; -type AdgridBidParams = { +type RequireAtLeastOne = + Omit & { + [K in Keys]-?: Required> & + Partial>> + }[Keys]; + +type AdgridBidParams = RequireAtLeastOne<{ domainId?: string; placement?: string; allBids?: boolean; -} + customId?: string; +}, "domainId" | "placement">; declare module '../src/adUnits' { interface BidderParams { @@ -48,8 +54,11 @@ const converter = ortbConverter({ imp(buildImp, bidRequest, context) { let imp = buildImp(bidRequest, context); imp = enrichImp(imp, bidRequest); - if (bidRequest.params.domainId) deepSetValue(imp, 'ext.adgrid.domainId', bidRequest.params.domainId); - if (bidRequest.params.placement) deepSetValue(imp, 'ext.adgrid.placement', bidRequest.params.placement); + const params = bidRequest.params as AdgridBidParams; + if (params.domainId) deepSetValue(imp, 'ext.adgrid.domainId', params.domainId); + if (params.placement) deepSetValue(imp, 'ext.adgrid.placement', params.placement); + if (params.allBids) deepSetValue(imp, 'ext.adgrid.allBids', params.allBids); + if (params.customId) deepSetValue(imp, 'ext.adgrid.customId', params.customId); return imp; }, request(buildRequest, imps, bidderRequest, context) { @@ -80,23 +89,6 @@ const buildRequests = ( return adapterRequest; } -const interpretResponse = (serverResponse: ServerResponse): AdapterResponse => { - if (!serverResponse.body) return []; - const respBody = serverResponse.body as ORTBResponse; - if (!respBody.seatbid || respBody.seatbid.length === 0) return []; - - const responses: BidResponse[] = []; - for (let i = 0; i < respBody.seatbid.length; i++) { - const seatbid = respBody.seatbid[i]; - for (let j = 0; j < seatbid.bid.length; j++) { - const bid = seatbid.bid[j]; - const response:BidResponse = createResponse(bid, respBody); - responses.push(response); - } - } - return responses; -} - export const spec:BidderSpec = { code: BIDDER_CODE, aliases: ALIASES, diff --git a/modules/nexx360BidAdapter.ts b/modules/nexx360BidAdapter.ts index 345b419cc26..f1e2d91546d 100644 --- a/modules/nexx360BidAdapter.ts +++ b/modules/nexx360BidAdapter.ts @@ -1,24 +1,31 @@ import { deepSetValue, generateUUID, logError } from '../src/utils.js'; import {getStorageManager} from '../src/storageManager.js'; -import {AdapterRequest, AdapterResponse, BidderSpec, registerBidder, ServerResponse} from '../src/adapters/bidderFactory.js'; +import {AdapterRequest, BidderSpec, registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {getGlobal} from '../src/prebidGlobal.js'; import {ortbConverter} from '../libraries/ortbConverter/converter.js' -import { createResponse, enrichImp, enrichRequest, getAmxId, getLocalStorageFunctionGenerator, getUserSyncs } from '../libraries/nexx360Utils/index.js'; +import { interpretResponse, enrichImp, enrichRequest, getAmxId, getLocalStorageFunctionGenerator, getUserSyncs } from '../libraries/nexx360Utils/index.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; import { BidRequest, ClientBidderRequest } from '../src/adapterManager.js'; -import { ORTBImp, ORTBRequest, ORTBResponse } from '../src/prebid.public.js'; -import { BidResponse } from '../src/bidfactory.js'; +import { ORTBImp, ORTBRequest } from '../src/prebid.public.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'nexx360'; -const REQUEST_URL = 'https://fast.nexx360.io/booster'; +const REQUEST_URL = 'http://fast.nexx360.io/booster'; const PAGE_VIEW_ID = generateUUID(); const BIDDER_VERSION = '7.0'; const GVLID = 965; const NEXXID_KEY = 'nexx360_storage'; -type Nexx360BidParams = { +const DEFAULT_GZIP_ENABLED = false; + +type RequireAtLeastOne = + Omit & { + [K in Keys]-?: Required> & + Partial>> + }[Keys]; + +type Nexx360BidParams = RequireAtLeastOne<{ tagId?: string; placement?: string; videoTagId?: string; @@ -28,7 +35,7 @@ type Nexx360BidParams = { divId?: string; allBids?: boolean; customId?: string; -} +}, "tagId" | "placement">; declare module '../src/adUnits' { interface BidderParams { @@ -67,6 +74,14 @@ export const getNexx360LocalStorage = getLocalStorageFunctionGenerator<{ nexx360 'nexx360Id' ); +export const getGzipSetting = (): boolean => { + const getBidderConfig = config.getBidderConfig(); + if (getBidderConfig.nexx360?.gzipEnabled === 'true') { + return getBidderConfig.nexx360?.gzipEnabled === 'true'; + } + return DEFAULT_GZIP_ENABLED; +} + const converter = ortbConverter({ context: { netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false @@ -90,6 +105,8 @@ const converter = ortbConverter({ if (bidRequest.params.adUnitPath) deepSetValue(imp, 'ext.adUnitPath', bidRequest.params.adUnitPath); if (bidRequest.params.adUnitName) deepSetValue(imp, 'ext.adUnitName', bidRequest.params.adUnitName); if (bidRequest.params.allBids) deepSetValue(imp, 'ext.nexx360.allBids', bidRequest.params.allBids); + if (bidRequest.params.nativeTagId) deepSetValue(imp, 'ext.nexx360.nativeTagId', bidRequest.params.nativeTagId); + if (bidRequest.params.customId) deepSetValue(imp, 'ext.nexx360.customId', bidRequest.params.customId); return imp; }, request(buildRequest, imps, bidderRequest, context) { @@ -133,29 +150,12 @@ const buildRequests = ( method: 'POST', url: REQUEST_URL, data, - } - return adapterRequest; -} + options: { + endpointCompression: getGzipSetting() + }, -const interpretResponse = (serverResponse: ServerResponse): AdapterResponse => { - if (!serverResponse.body) return []; - const respBody = serverResponse.body as ORTBResponse; - if (!respBody.seatbid || respBody.seatbid.length === 0) return []; - - const { bidderSettings } = getGlobal(); - const allowAlternateBidderCodes = bidderSettings && bidderSettings.standard ? bidderSettings.standard.allowAlternateBidderCodes : false; - - const responses: BidResponse[] = []; - for (let i = 0; i < respBody.seatbid.length; i++) { - const seatbid = respBody.seatbid[i]; - for (let j = 0; j < seatbid.bid.length; j++) { - const bid = seatbid.bid[j]; - const response:BidResponse = createResponse(bid, respBody); - if (allowAlternateBidderCodes) response.bidderCode = `n360_${bid.ext.ssp}`; - responses.push(response); - } } - return responses; + return adapterRequest; } export const spec:BidderSpec = { diff --git a/test/spec/modules/adgridBidAdapter_spec.js b/test/spec/modules/adgridBidAdapter_spec.js index 65f0e45a6ae..3dd5d1985f5 100644 --- a/test/spec/modules/adgridBidAdapter_spec.js +++ b/test/spec/modules/adgridBidAdapter_spec.js @@ -515,6 +515,7 @@ describe('adgrid bid adapter tests', () => { mediaType: 'outstream', ssp: 'test', adUnitCode: 'div-1', + divId: 'div-1', }, }, ], @@ -537,6 +538,7 @@ describe('adgrid bid adapter tests', () => { currency: 'USD', netRevenue: true, ttl: 120, + divId: 'div-1', mediaType: 'video', meta: { advertiserDomains: ['adgrid.com'], demandSource: 'test' }, vastXml: 'vast', diff --git a/test/spec/modules/nexx360BidAdapter_spec.js b/test/spec/modules/nexx360BidAdapter_spec.js index 93d1fcc6357..a5e8ab03d1a 100644 --- a/test/spec/modules/nexx360BidAdapter_spec.js +++ b/test/spec/modules/nexx360BidAdapter_spec.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { - spec, STORAGE, getNexx360LocalStorage, + spec, STORAGE, getNexx360LocalStorage, getGzipSetting, } from 'modules/nexx360BidAdapter.js'; import sinon from 'sinon'; import { getAmxId } from '../../../libraries/nexx360Utils/index.js'; @@ -33,6 +33,11 @@ describe('Nexx360 bid adapter tests', () => { }, }; + it('We test getGzipSettings', () => { + const output = getGzipSetting(); + expect(output).to.be.a('boolean'); + }); + describe('isBidRequestValid()', () => { let bannerBid; beforeEach(() => { @@ -566,6 +571,7 @@ describe('Nexx360 bid adapter tests', () => { mediaType: 'outstream', ssp: 'appnexus', adUnitCode: 'div-1', + divId: 'div-1', }, }, ], @@ -587,6 +593,7 @@ describe('Nexx360 bid adapter tests', () => { creativeId: '97517771', currency: 'USD', netRevenue: true, + divId: 'div-1', ttl: 120, mediaType: 'video', meta: { advertiserDomains: ['appnexus.com'], demandSource: 'appnexus' }, From e8432777cba4bdfd78d03f1a0fa2c333547419a4 Mon Sep 17 00:00:00 2001 From: Gabriel Chicoye Date: Fri, 10 Oct 2025 17:01:28 +0200 Subject: [PATCH 3/3] https fix --- modules/nexx360BidAdapter.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/nexx360BidAdapter.ts b/modules/nexx360BidAdapter.ts index f1e2d91546d..393bea2dbab 100644 --- a/modules/nexx360BidAdapter.ts +++ b/modules/nexx360BidAdapter.ts @@ -11,7 +11,7 @@ import { ORTBImp, ORTBRequest } from '../src/prebid.public.js'; import { config } from '../src/config.js'; const BIDDER_CODE = 'nexx360'; -const REQUEST_URL = 'http://fast.nexx360.io/booster'; +const REQUEST_URL = 'https://fast.nexx360.io/booster'; const PAGE_VIEW_ID = generateUUID(); const BIDDER_VERSION = '7.0'; const GVLID = 965; @@ -153,7 +153,6 @@ const buildRequests = ( options: { endpointCompression: getGzipSetting() }, - } return adapterRequest; }