From 9e14c1366d9cb4df974bf0fc1e568328a07673fa Mon Sep 17 00:00:00 2001 From: Marcin Komorski Date: Mon, 8 Jul 2024 19:28:29 +0200 Subject: [PATCH] update --- modules/dfpAdServerVideo.js | 30 ++++++++--------- src/pps.js | 17 ++++++++-- src/targeting.js | 60 ++++++++++++++------------------- test/spec/unit/core/pps_spec.js | 54 +++++++++++++++++++++++------ 4 files changed, 98 insertions(+), 63 deletions(-) diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 3cb3f5f1f60..3d1fbfa0788 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -2,8 +2,18 @@ * This module adds [DFP support]{@link https://www.doubleclickbygoogle.com/} for Video to Prebid. */ -import {registerVideoSupport} from '../src/adServerManager.js'; -import {targeting} from '../src/targeting.js'; +import { DEFAULT_DFP_PARAMS, DFP_ENDPOINT } from '../libraries/dfpUtils/dfpUtils.js'; +import { registerVideoSupport } from '../src/adServerManager.js'; +import { gdprDataHandler } from '../src/adapterManager.js'; +import { getPPID } from '../src/adserver.js'; +import { auctionManager } from '../src/auctionManager.js'; +import { config } from '../src/config.js'; +import { EVENTS } from '../src/constants.js'; +import * as events from '../src/events.js'; +import { getHook } from '../src/hook.js'; +import { getSignals } from '../src/pps.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { targeting } from '../src/targeting.js'; import { buildUrl, deepAccess, @@ -12,20 +22,8 @@ import { isNumber, logError, parseSizesInput, - parseUrl, - uniques + parseUrl } from '../src/utils.js'; -import {config} from '../src/config.js'; -import {getHook} from '../src/hook.js'; -import {auctionManager} from '../src/auctionManager.js'; -import {gdprDataHandler} from '../src/adapterManager.js'; -import * as events from '../src/events.js'; -import {EVENTS} from '../src/constants.js'; -import {getPPID} from '../src/adserver.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {CLIENT_SECTIONS} from '../src/fpd/oneClient.js'; -import {DEFAULT_DFP_PARAMS, DFP_ENDPOINT} from '../libraries/dfpUtils/dfpUtils.js'; -import { getSegments, getSignals } from '../src/pps.js'; /** * @typedef {Object} DfpVideoParams * @@ -171,7 +169,7 @@ export function buildDfpVideoUrl(options) { }); const fpd = auctionManager.index.getBidRequest(options.bid || {})?.ortb2 ?? auctionManager.index.getAuction(options.bid || {})?.getFPD()?.global; - + const signals = getSignals(fpd); if (signals.length) { diff --git a/src/pps.js b/src/pps.js index ad2e4887e60..352673b3a1a 100644 --- a/src/pps.js +++ b/src/pps.js @@ -1,4 +1,5 @@ import { auctionManager } from './auctionManager.js'; +import { TARGETING_KEYS } from './constants.js'; import { CLIENT_SECTIONS } from './fpd/oneClient.js'; import { deepAccess, uniques } from './utils.js'; @@ -21,15 +22,16 @@ export function getSignals(fpd) { return signals; } -export function getSignalsArray(auctionIds) { +export function getSignalsArrayByAuctionsIds(auctionIds) { const signals = auctionIds .map(auctionId => auctionManager.index.getAuction({ auctionId })?.getFPD()?.global) .map(getSignals) .filter(fpd => fpd); - return signals + + return signals; } -export function getSegmentsIntersection(signals) { +export function getSignalsIntersection(signals) { const taxonomies = ['IAB_AUDIENCE_1_1', 'IAB_CONTENT_2_2']; const result = {}; taxonomies.forEach((taxonomy) => { @@ -42,6 +44,15 @@ export function getSegmentsIntersection(signals) { return commonElements.filter(element => subArray.includes(element)); }) ) : [] + result[taxonomy] = { values: result[taxonomy] }; }) return result; } + +export function getAuctionsIdsFromTargeting(targeting) { + return Object.values(targeting) + .flatMap(x => Object.entries(x)) + .filter((entry) => (entry[0] || '').includes(TARGETING_KEYS.AD_ID)) + .flatMap(entry => entry[1]) + .filter(uniques); +} diff --git a/src/targeting.js b/src/targeting.js index e7761e972c4..f88de6652f8 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -1,3 +1,22 @@ +import { auctionManager } from './auctionManager.js'; +import { getTTL } from './bidTTL.js'; +import { bidderSettings } from './bidderSettings.js'; +import { config } from './config.js'; +import { + BID_STATUS, + DEFAULT_TARGETING_KEYS, + EVENTS, + JSON_MAPPING, + NATIVE_KEYS, + STATUS, + TARGETING_KEYS +} from './constants.js'; +import * as events from './events.js'; +import { hook } from './hook.js'; +import { ADPOD } from './mediaTypes.js'; +import { NATIVE_TARGETING_KEYS } from './native.js'; +import { find, includes } from './polyfill.js'; +import { getAuctionsIdsFromTargeting, getSignalsArrayByAuctionsIds, getSignalsIntersection } from './pps.js'; import { deepAccess, deepClone, @@ -14,26 +33,7 @@ import { timestamp, uniques, } from './utils.js'; -import {config} from './config.js'; -import {NATIVE_TARGETING_KEYS} from './native.js'; -import {auctionManager} from './auctionManager.js'; -import {ADPOD} from './mediaTypes.js'; -import {hook} from './hook.js'; -import {bidderSettings} from './bidderSettings.js'; -import {find, includes} from './polyfill.js'; -import { - BID_STATUS, - DEFAULT_TARGETING_KEYS, - EVENTS, - JSON_MAPPING, - NATIVE_KEYS, - STATUS, - TARGETING_KEYS -} from './constants.js'; -import {getHighestCpm, getOldestHighestCpmBid} from './utils/reducers.js'; -import {getTTL} from './bidTTL.js'; -import * as events from './events.js'; -import { getSignalsArray } from './pps.js'; +import { getHighestCpm, getOldestHighestCpmBid } from './utils/reducers.js'; var pbTargetingKeys = []; @@ -436,16 +436,6 @@ export function newTargeting(auctionManager) { // get our ad unit codes let targetingSet = targeting.getAllTargeting(adUnit); - const auctionIds = targetingSet - .flatMap(entry => Object.values(entry)) - .flatMap(x => x) - .filter(x => Object.keys(x).some(key => key.includes(TARGETING_KEYS.AD_ID))) - .map(entry => Object.values(entry)) - .flatMap(x => x[0]) - - const signals = getSignalsArray(auctionIds) - googletag.setConfig({pps: signals}) - let resetMap = Object.fromEntries(pbTargetingKeys.map(key => [key, null])); Object.entries(getGPTSlotsForAdUnits(Object.keys(targetingSet), customSlotMatching)).forEach(([targetId, slots]) => { @@ -472,6 +462,11 @@ export function newTargeting(auctionManager) { }); }); + // set gpt config + const auctionsIds = getAuctionsIdsFromTargeting(targetingSet); + const signals = getSignalsIntersection(getSignalsArrayByAuctionsIds(auctionsIds)); + window.googletag.setConfig && window.googletag.setConfig({pps: { taxonomies: signals }}); + // emit event events.emit(EVENTS.SET_TARGETING, targetingSet); }, 'setTargetingForGPT'); @@ -676,10 +671,7 @@ export function newTargeting(auctionManager) { function getCustomBidTargeting(adUnitCodes, bidsReceived) { return bidsReceived .filter(bid => includes(adUnitCodes, bid.adUnitCode)) - .map(bid => { - const newBid = Object.assign({}, bid); - return newBid; - }) + .map(bid => Object.assign({}, bid)) .reduce(mergeAdServerTargeting, []) .map(truncateCustomKeys) .filter(bid => bid); // removes empty elements in array; diff --git a/test/spec/unit/core/pps_spec.js b/test/spec/unit/core/pps_spec.js index 1f8d981260d..d71c4369d87 100644 --- a/test/spec/unit/core/pps_spec.js +++ b/test/spec/unit/core/pps_spec.js @@ -1,8 +1,9 @@ import { auctionManager } from '../../../../src/auctionManager'; -import { getFpdIntersection, getSegmentsIntersection, getSignalsArray } from '../../../../src/pps' +import { getAuctionsIdsFromTargeting, getSegments, getSignals, getSignalsArrayByAuctionsIds, getSignalsIntersection } from '../../../../src/pps'; describe('pps', () => { let getAuctionStub; + const mockTargeting = {'/123456/header-bid-tag-0': {'hb_deal_rubicon': '1234', 'hb_deal': '1234', 'hb_pb': '0.53', 'hb_adid': '148018fe5e', 'hb_bidder': 'rubicon', 'foobar': '300x250', 'hb_pb_rubicon': '0.53', 'hb_adid_rubicon': '148018fe5e', 'hb_bidder_rubicon': 'rubicon', 'hb_deal_appnexus': '4321', 'hb_pb_appnexus': '0.1', 'hb_adid_appnexus': '567891011', 'hb_bidder_appnexus': 'appnexus'}} const mocksAuctions = [ { @@ -64,19 +65,52 @@ describe('pps', () => { }, ] - beforeEach(function () { + it('should parse segments from fpd', () => { + const twoSegments = getSegments(mocksAuctions[0].getFPD().global, ['user.data'], 4); + expect(JSON.stringify(twoSegments)).to.equal(JSON.stringify(['1', '2'])) + const zeroSegments = getSegments(mocksAuctions[0].getFPD().global, ['user.data'], 6); + expect(zeroSegments).to.length(0); + }) + + it('should return signals from fpd', () => { + const signals = getSignals(mocksAuctions[0].getFPD().global); + const expectedSignals = [{ taxonomy: 'IAB_AUDIENCE_1_1', values: ['1', '2'] }]; + expect(JSON.stringify(signals)).to.equal(JSON.stringify(expectedSignals)) + }) + + it('should properly get auctions ids from targeting', () => { + const auctionsIds = getAuctionsIdsFromTargeting(mockTargeting); + expect(JSON.stringify(auctionsIds)).to.equal(JSON.stringify(['148018fe5e', '567891011'])) + }) + + it('should properly return empty array of auction ids for invalid targeting', () => { + let auctionsIds = getAuctionsIdsFromTargeting({}); + expect(Array.isArray(auctionsIds)).to.equal(true); + expect(auctionsIds).to.length(0); + auctionsIds = getAuctionsIdsFromTargeting({'/123456/header-bid-tag-0/bg': {'invalidContent': '123'}}); + expect(Array.isArray(auctionsIds)).to.equal(true); + expect(auctionsIds).to.length(0); + }) + + it('should properly get signals from auctions', () => { getAuctionStub = sinon.stub(auctionManager.index, 'getAuction').callsFake(({ auctionId }) => { return mocksAuctions.find(auction => auction.auctionId === auctionId) }); - }); - - afterEach(function () { + const signals = getSignalsArrayByAuctionsIds(['1111', '234234', '234324234']); + const intersection = getSignalsIntersection(signals) + const expectedResult = { IAB_AUDIENCE_1_1: { values: ['2'] }, IAB_CONTENT_2_2: { values: [] } }; + expect(JSON.stringify(intersection)).to.be.equal(JSON.stringify(expectedResult)); getAuctionStub.restore(); - }); + }) + + it('should return empty signals array for empty auctions ids array', () => { + const signals = getSignalsArrayByAuctionsIds([]); + expect(Array.isArray(signals)).to.equal(true); + expect(signals).to.length(0); + }) - it('should properly find intersection', () => { - const signals = getSignalsArray(['1111', '234234', '234324234']); - const expectedResult = { IAB_AUDIENCE_1_1:["2"], IAB_CONTENT_2_2:[]}; - expect(signals).to.be.equal(JSON.stringify(expectedResult)); + it('should return properly formatted object for getSignalsIntersection invoked with empty array', () => { + const signals = getSignalsIntersection([]); + expect(Object.keys(signals)).to.contain.members(['IAB_AUDIENCE_1_1', 'IAB_CONTENT_2_2']); }) })