From 5c458c2040ff6bebfb46af3e2dda09657a6eb286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Mon, 3 Mar 2025 11:41:11 +0100 Subject: [PATCH 1/5] enable previous auction info --- .../previousAuctionInfo/previousAuctionInfo.js | 4 ++++ modules/equativBidAdapter.js | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libraries/previousAuctionInfo/previousAuctionInfo.js b/libraries/previousAuctionInfo/previousAuctionInfo.js index 0b964d7abe4..5d43567013a 100644 --- a/libraries/previousAuctionInfo/previousAuctionInfo.js +++ b/libraries/previousAuctionInfo/previousAuctionInfo.js @@ -1,6 +1,7 @@ import {on as onEvent, off as offEvent} from '../../src/events.js'; import { EVENTS } from '../../src/constants.js'; import { config } from '../../src/config.js'; +import { logInfo } from '../../src/utils.js'; export let previousAuctionInfoEnabled = false; let enabledBidders = []; @@ -46,6 +47,7 @@ const deinitHandlers = () => { } export const onAuctionEndHandler = (auctionDetails) => { + logInfo('[PAI] Auction End', auctionDetails); try { const receivedBidsMap = {}; const rejectedBidsMap = {}; @@ -105,6 +107,7 @@ export const onAuctionEndHandler = (auctionDetails) => { } export const onBidWonHandler = (winningBid) => { + logInfo('[PAI] Bid Won', winningBid); const winningTid = winningBid.transactionId; Object.values(auctionState).flat().forEach(prevAuctPayload => { @@ -115,6 +118,7 @@ export const onBidWonHandler = (winningBid) => { }; export const onBidRequestedHandler = (bidRequest) => { + logInfo('[PAI] Bid Request', bidRequest); try { const enabledBidder = enabledBidders.find(bidder => bidder.bidderCode === bidRequest.bidderCode); if (enabledBidder && auctionState[bidRequest.bidderCode]) { diff --git a/modules/equativBidAdapter.js b/modules/equativBidAdapter.js index 39c7ee94586..7b09c933bb1 100644 --- a/modules/equativBidAdapter.js +++ b/modules/equativBidAdapter.js @@ -5,7 +5,8 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; -import { deepAccess, deepSetValue, logError, logWarn, mergeDeep } from '../src/utils.js'; +import { deepAccess, deepSetValue, logError, logInfo, logWarn, mergeDeep } from '../src/utils.js'; +import { enablePreviousAuctionInfo } from '../libraries/previousAuctionInfo/previousAuctionInfo.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid @@ -18,6 +19,8 @@ const COOKIE_SYNC_URL = `${COOKIE_SYNC_ORIGIN}/diff/templates/asset/csync.html`; const LOG_PREFIX = 'Equativ:'; const PID_COOKIE_NAME = 'eqt_pid'; +enablePreviousAuctionInfo({ bidderCode: BIDDER_CODE }); + /** * Evaluates impressions for validity. The entry evaluated is considered valid if NEITHER of these conditions are met: * 1) it has a `video` property defined for `mediaTypes.video` which is an empty object @@ -55,6 +58,12 @@ export const spec = { data, method: 'POST', url: 'https://ssb-global.smartadserver.com/api/bid?callerId=169', + // url: 'https://ssb-engine-argocd-dev.internal.smartadserver.com/api/bid?callerId=169', + // options: { + // customHeaders: { + // 'X-Eqtv-Debug': no === 1 ? '67c190809d44a9f4fd5ddf64' : '6708e3aeca04848e919e9c8c' + // } + // } }) }); @@ -104,6 +113,10 @@ export const spec = { } return []; + }, + + onBidWon: (bid) => { + logInfo('onBidWon', bid); } }; From d9e508ce9848526f6f9e36d604af4b86693aad04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Wed, 5 Mar 2025 13:50:58 +0100 Subject: [PATCH 2/5] remove logs --- .../previousAuctionInfo.js | 4 --- modules/equativBidAdapter.js | 26 +++++++++---------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/libraries/previousAuctionInfo/previousAuctionInfo.js b/libraries/previousAuctionInfo/previousAuctionInfo.js index 5d43567013a..0b964d7abe4 100644 --- a/libraries/previousAuctionInfo/previousAuctionInfo.js +++ b/libraries/previousAuctionInfo/previousAuctionInfo.js @@ -1,7 +1,6 @@ import {on as onEvent, off as offEvent} from '../../src/events.js'; import { EVENTS } from '../../src/constants.js'; import { config } from '../../src/config.js'; -import { logInfo } from '../../src/utils.js'; export let previousAuctionInfoEnabled = false; let enabledBidders = []; @@ -47,7 +46,6 @@ const deinitHandlers = () => { } export const onAuctionEndHandler = (auctionDetails) => { - logInfo('[PAI] Auction End', auctionDetails); try { const receivedBidsMap = {}; const rejectedBidsMap = {}; @@ -107,7 +105,6 @@ export const onAuctionEndHandler = (auctionDetails) => { } export const onBidWonHandler = (winningBid) => { - logInfo('[PAI] Bid Won', winningBid); const winningTid = winningBid.transactionId; Object.values(auctionState).flat().forEach(prevAuctPayload => { @@ -118,7 +115,6 @@ export const onBidWonHandler = (winningBid) => { }; export const onBidRequestedHandler = (bidRequest) => { - logInfo('[PAI] Bid Request', bidRequest); try { const enabledBidder = enabledBidders.find(bidder => bidder.bidderCode === bidRequest.bidderCode); if (enabledBidder && auctionState[bidRequest.bidderCode]) { diff --git a/modules/equativBidAdapter.js b/modules/equativBidAdapter.js index 7b09c933bb1..a9b2ce4dea6 100644 --- a/modules/equativBidAdapter.js +++ b/modules/equativBidAdapter.js @@ -1,12 +1,12 @@ import { getBidFloor } from '../libraries/equativUtils/equativUtils.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { enablePreviousAuctionInfo } from '../libraries/previousAuctionInfo/previousAuctionInfo.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; -import { deepAccess, deepSetValue, logError, logInfo, logWarn, mergeDeep } from '../src/utils.js'; -import { enablePreviousAuctionInfo } from '../libraries/previousAuctionInfo/previousAuctionInfo.js'; +import { deepAccess, deepSetValue, logError, logWarn, mergeDeep } from '../src/utils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid @@ -52,18 +52,20 @@ export const spec = { const requests = []; + let no = 1; + bidRequests.forEach(bid => { const data = converter.toORTB({bidRequests: [bid], bidderRequest}); requests.push({ data, method: 'POST', - url: 'https://ssb-global.smartadserver.com/api/bid?callerId=169', - // url: 'https://ssb-engine-argocd-dev.internal.smartadserver.com/api/bid?callerId=169', - // options: { - // customHeaders: { - // 'X-Eqtv-Debug': no === 1 ? '67c190809d44a9f4fd5ddf64' : '6708e3aeca04848e919e9c8c' - // } - // } + // url: 'https://ssb-global.smartadserver.com/api/bid?callerId=169', + url: 'https://ssb-engine-argocd-dev.internal.smartadserver.com/api/bid?callerId=169', + options: { + customHeaders: { + 'X-Eqtv-Debug': no++ === 1 ? '67c190809d44a9f4fd5ddf64' : '6708e3aeca04848e919e9c8c' + } + } }) }); @@ -113,10 +115,6 @@ export const spec = { } return []; - }, - - onBidWon: (bid) => { - logInfo('onBidWon', bid); } }; @@ -164,7 +162,7 @@ export const converter = ortbConverter({ if (deepAccess(bid, path)) { props.forEach(prop => { if (!deepAccess(bid, `${path}.${prop}`)) { - logWarn(`${LOG_PREFIX} Property "${path}.${prop}" is missing from request. Request will proceed, but the use of "${prop}" is strongly encouraged.`, bid); + logWarn(`${LOG_PREFIX} Property "${path}.${prop}" is missing from request. Request will proceed, but the use of "${prop}" is strongly encouraged.`, bid); } }); } From 9220be444b2cad6aa82648f1390f84c979e7044e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Wed, 16 Apr 2025 10:10:47 +0200 Subject: [PATCH 3/5] send previous auction info --- modules/equativBidAdapter.js | 63 +++++++-- test/spec/modules/equativBidAdapter_spec.js | 139 +++++++++++++++++++- 2 files changed, 189 insertions(+), 13 deletions(-) diff --git a/modules/equativBidAdapter.js b/modules/equativBidAdapter.js index c6f1d7745b0..0414129f652 100644 --- a/modules/equativBidAdapter.js +++ b/modules/equativBidAdapter.js @@ -17,7 +17,9 @@ const COOKIE_SYNC_URL = `${COOKIE_SYNC_ORIGIN}/diff/templates/asset/csync.html`; const LOG_PREFIX = 'Equativ:'; const PID_COOKIE_NAME = 'eqt_pid'; +let feedbackArray = []; let impIdMap = {}; +let tokens = {}; /** * Assigns values to new properties, removes temporary ones from an object @@ -59,6 +61,14 @@ function getFloor(bid, mediaType, width, height, currency) { .floor || bid.params.bidfloor || -1; } +/** + * Gets value of the local variable impIdMap + * @returns {*} Value of impIdMap + */ +export function getImpIdMap() { + return impIdMap; +}; + /** * Evaluates impressions for validity. The entry evaluated is considered valid if NEITHER of these conditions are met: * 1) it has a `video` property defined for `mediaTypes.video` which is an empty object @@ -70,6 +80,8 @@ function isValid(bidReq) { return !(bidReq.mediaTypes.video && JSON.stringify(bidReq.mediaTypes.video) === '{}') && !(bidReq.mediaTypes.native && JSON.stringify(bidReq.mediaTypes.native) === '{}'); } +enablePreviousAuctionInfo({ bidderCode: BIDDER_CODE }); + /** * Generates a 14-char string id * @returns {string} @@ -87,6 +99,36 @@ function makeId() { return str; } +/** + * Updates bid request with data from previous auction + * @param {*} req A bid request object to be updated + * @returns {*} Updated bid request object + */ +function updateFeedbackData(req) { + if (req?.ext?.prebid?.previousauctioninfo) { + req.ext.prebid.previousauctioninfo.forEach(info => { + if (tokens[info?.bidId]) { + feedbackArray.push({ + feedback_token: tokens[info.bidId], + loss: info.bidderCpm == info.highestBidCpm ? 0 : 102, + price: info.highestBidCpm + }); + + delete tokens[info.bidId]; + } + }); + + delete req.ext.prebid; + } + + if (feedbackArray.length) { + deepSetValue(req, 'ext.bid_feedback', feedbackArray[0]); + feedbackArray.shift(); + } + + return req; +} + export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { @@ -107,20 +149,12 @@ export const spec = { const requests = []; - let no = 1; - bidRequests.forEach(bid => { const data = converter.toORTB({bidRequests: [bid], bidderRequest}); requests.push({ data, method: 'POST', - // url: 'https://ssb-global.smartadserver.com/api/bid?callerId=169', - url: 'https://ssb-engine-argocd-dev.internal.smartadserver.com/api/bid?callerId=169', - options: { - customHeaders: { - 'X-Eqtv-Debug': no++ === 1 ? '67c190809d44a9f4fd5ddf64' : '6708e3aeca04848e919e9c8c' - } - } + url: 'https://ssb-global.smartadserver.com/api/bid?callerId=169' }) }); @@ -141,7 +175,12 @@ export const spec = { serverResponse.body.seatbid .filter(seat => seat?.bid?.length) .forEach(seat => - seat.bid.forEach(bid => bid.impid = impIdMap[bid.impid]) + seat.bid.forEach(bid => { + bid.impid = impIdMap[bid.impid]; + if (deepAccess(bid, 'ext.feedback_token')) { + tokens[bid.impid] = bid.ext.feedback_token; + } + }) ); } @@ -263,7 +302,7 @@ export const converter = ortbConverter({ }); }); - const req = buildRequest(splitImps, bidderRequest, context); + let req = buildRequest(splitImps, bidderRequest, context); let env = ['ortb2.site.publisher', 'ortb2.app.publisher', 'ortb2.dooh.publisher'].find(propPath => deepAccess(bid, propPath)) || 'ortb2.site.publisher'; deepSetValue(req, env.replace('ortb2.', '') + '.id', deepAccess(bid, env + '.id') || bid.params.networkId); @@ -287,6 +326,8 @@ export const converter = ortbConverter({ deepSetValue(req, 'user.buyeruid', pid); } + req = updateFeedbackData(req); + return req; } }); diff --git a/test/spec/modules/equativBidAdapter_spec.js b/test/spec/modules/equativBidAdapter_spec.js index 3cc730d39d1..79d8cf3c707 100644 --- a/test/spec/modules/equativBidAdapter_spec.js +++ b/test/spec/modules/equativBidAdapter_spec.js @@ -1,4 +1,4 @@ -import { converter, spec, storage } from 'modules/equativBidAdapter.js'; +import { converter, getImpIdMap, spec, storage } from 'modules/equativBidAdapter.js'; import * as utils from '../../../src/utils.js'; describe('Equativ bid adapter tests', () => { @@ -748,7 +748,142 @@ describe('Equativ bid adapter tests', () => { expect(secondImp).to.not.have.property('native'); expect(secondImp).to.have.property('video'); } - }) + }); + + it('should not send ext.prebid', () => { + const request = spec.buildRequests( + DEFAULT_BANNER_BID_REQUESTS, + { + ...DEFAULT_BANNER_BIDDER_REQUEST, + ortb2: { + ext: { + prebid: { + previousauctioninfo: [ + { + bidId: 'abcd1234', + bidderCpm: 5, + highestBidCpm: 6 + } + ] + } + } + } + } + )[0]; + expect(request.data.ext).not.to.have.property('prebid'); + }); + + it('should send feedback data when lost', () => { + const bidId = 'abcd1234'; + const cpm = 3.7; + const impIdMap = getImpIdMap(); + const token = 'y7hd87dw8'; + const RESPONSE_WITH_FEEDBACK = { + body: { + seatbid: [ + { + bid: [ + { + ext: { + feedback_token: token + }, + impid: Object.keys(impIdMap).find(key => impIdMap[key] === bidId) + } + ] + } + ] + } + }; + + let request = spec.buildRequests( + DEFAULT_BANNER_BID_REQUESTS, + DEFAULT_BANNER_BIDDER_REQUEST + )[0]; + + spec.interpretResponse(RESPONSE_WITH_FEEDBACK, request); + + request = spec.buildRequests( + DEFAULT_BANNER_BID_REQUESTS, + { + ...DEFAULT_BANNER_BIDDER_REQUEST, + ortb2: { + ext: { + prebid: { + previousauctioninfo: [ + { + bidId, + bidderCpm: 2.41, + highestBidCpm: cpm + } + ] + } + } + } + } + )[0]; + + expect(request.data.ext).to.have.property('bid_feedback').and.to.deep.equal({ + feedback_token: token, + loss: 102, + price: cpm + }); + }); + + it('should send feedback data when won', () => { + const bidId = 'abcd1234'; + const cpm = 2.34; + const impIdMap = getImpIdMap(); + const token = '87187y83'; + const RESPONSE_WITH_FEEDBACK = { + body: { + seatbid: [ + { + bid: [ + { + ext: { + feedback_token: token + }, + impid: Object.keys(impIdMap).find(key => impIdMap[key] === bidId) + } + ] + } + ] + } + }; + + let request = spec.buildRequests( + DEFAULT_BANNER_BID_REQUESTS, + DEFAULT_BANNER_BIDDER_REQUEST + )[0]; + + spec.interpretResponse(RESPONSE_WITH_FEEDBACK, request); + + request = spec.buildRequests( + DEFAULT_BANNER_BID_REQUESTS, + { + ...DEFAULT_BANNER_BIDDER_REQUEST, + ortb2: { + ext: { + prebid: { + previousauctioninfo: [ + { + bidId, + bidderCpm: 2.34, + highestBidCpm: cpm + } + ] + } + } + } + } + )[0]; + + expect(request.data.ext).to.have.property('bid_feedback').and.to.deep.equal({ + feedback_token: token, + loss: 0, + price: cpm + }); + }); }); describe('getUserSyncs', () => { From 40ee36c6c54e048a2871a618961f833efd9e147f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Thu, 17 Apr 2025 12:05:37 +0200 Subject: [PATCH 4/5] remove enablePreviousAuctionInfo library --- modules/equativBidAdapter.js | 48 +----------------------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/modules/equativBidAdapter.js b/modules/equativBidAdapter.js index 9ed2a840e9a..48c761d8a70 100644 --- a/modules/equativBidAdapter.js +++ b/modules/equativBidAdapter.js @@ -1,5 +1,4 @@ import { ortbConverter } from '../libraries/ortbConverter/converter.js'; -import { enablePreviousAuctionInfo } from '../libraries/previousAuctionInfo/previousAuctionInfo.js'; import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; @@ -18,52 +17,9 @@ const COOKIE_SYNC_URL = `${COOKIE_SYNC_ORIGIN}/diff/templates/asset/csync.html`; const LOG_PREFIX = 'Equativ:'; const PID_STORAGE_NAME = 'eqt_pid'; -let nwid = 0; - -let impIdMap = {}; - -/** - * Assigns values to new properties, removes temporary ones from an object - * and remove temporary default bidfloor of -1 - * @param {*} obj An object - * @param {string} key A name of the new property - * @param {string} tempKey A name of the temporary property to be removed - * @returns {*} An updated object - */ -function cleanObject(obj, key, tempKey) { - const newObj = {}; - - for (const prop in obj) { - if (prop === key) { - if (Object.prototype.hasOwnProperty.call(obj, tempKey)) { - newObj[key] = obj[tempKey]; - } - } else if (prop !== tempKey) { - newObj[prop] = obj[prop]; - } - } - - newObj.bidfloor === -1 && delete newObj.bidfloor; - - return newObj; -} - -/** - * Returns a floor price provided by the Price Floors module or the floor price set in the publisher parameters - * @param {*} bid - * @param {string} mediaType A media type - * @param {number} width A width of the ad - * @param {number} height A height of the ad - * @param {string} currency A floor price currency - * @returns {number} Floor price - */ -function getFloor(bid, mediaType, width, height, currency) { - return bid.getFloor?.({ currency, mediaType, size: [width, height] }) - .floor || bid.params.bidfloor || -1; -} - let feedbackArray = []; let impIdMap = {}; +let nwid = 0; let tokens = {}; /** @@ -125,8 +81,6 @@ function isValid(bidReq) { return !(bidReq.mediaTypes.video && JSON.stringify(bidReq.mediaTypes.video) === '{}') && !(bidReq.mediaTypes.native && JSON.stringify(bidReq.mediaTypes.native) === '{}'); } -enablePreviousAuctionInfo({ bidderCode: BIDDER_CODE }); - /** * Generates a 14-char string id * @returns {string} From b214b7126129a6be2c2813c84e9ffaf07a7b496b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Sok=C3=B3=C5=82?= Date: Thu, 24 Apr 2025 10:39:22 +0200 Subject: [PATCH 5/5] fix linter --- modules/equativBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/equativBidAdapter.js b/modules/equativBidAdapter.js index ca539c60e76..0f6648fc509 100644 --- a/modules/equativBidAdapter.js +++ b/modules/equativBidAdapter.js @@ -181,7 +181,7 @@ export const spec = { if (deepAccess(bid, 'ext.feedback_token')) { tokens[bid.impid] = bid.ext.feedback_token; } - + bid.ttl = typeof bid.exp === 'number' && bid.exp > 0 ? bid.exp : DEFAULT_TTL; }) );