From ef125485459bcb8d25916e0e1ccac26d78010c59 Mon Sep 17 00:00:00 2001 From: Jake Date: Fri, 31 Jan 2025 15:15:36 +0000 Subject: [PATCH] Add The Trade Desk to prebid (#1773) Co-authored-by: Emma Imber <108270776+emma-imber@users.noreply.github.com> --- .changeset/sour-chairs-clean.md | 5 ++++ package.json | 2 +- pnpm-lock.yaml | 10 +++---- src/experiments/ab-tests.ts | 2 ++ src/experiments/tests/ttd-prebid.ts | 28 +++++++++++++++++++ src/lib/header-bidding/prebid-types.ts | 12 ++++++-- .../header-bidding/prebid/bid-config.spec.ts | 13 +++++++++ src/lib/header-bidding/prebid/bid-config.ts | 24 +++++++++++++--- src/lib/header-bidding/prebid/prebid.ts | 3 +- src/lib/header-bidding/utils.ts | 5 ++++ 10 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 .changeset/sour-chairs-clean.md create mode 100644 src/experiments/tests/ttd-prebid.ts diff --git a/.changeset/sour-chairs-clean.md b/.changeset/sour-chairs-clean.md new file mode 100644 index 000000000..553faa094 --- /dev/null +++ b/.changeset/sour-chairs-clean.md @@ -0,0 +1,5 @@ +--- +'@guardian/commercial': minor +--- + +Add The Trade Desk behind 0% test diff --git a/package.json b/package.json index d82640165..fedb1340e 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "webpack-merge": "^6.0.1" }, "dependencies": { - "@guardian/prebid.js": "8.52.0-12", + "@guardian/prebid.js": "8.52.0-13", "fastdom": "^1.0.12", "lodash-es": "^4.17.21", "process": "^0.11.10", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7b34a9e05..ed3fbb71c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@guardian/prebid.js': - specifier: 8.52.0-12 - version: 8.52.0-12(babel-core@7.0.0-bridge.0(@babel/core@7.26.0))(ejs@3.1.10)(tslib@2.8.1)(typescript@5.5.4) + specifier: 8.52.0-13 + version: 8.52.0-13(babel-core@7.0.0-bridge.0(@babel/core@7.26.0))(ejs@3.1.10)(tslib@2.8.1)(typescript@5.5.4) fastdom: specifier: ^1.0.12 version: 1.0.12 @@ -1022,8 +1022,8 @@ packages: typescript: optional: true - '@guardian/prebid.js@8.52.0-12': - resolution: {integrity: sha512-bfLXYjZPCKgBwARQpuF4o5v37Yw3GlsHrU9mbMCzsuvMuFxZfX1TWXMPgIdvMUCsvuFYnTBRX9bJm+kCLsFp4w==} + '@guardian/prebid.js@8.52.0-13': + resolution: {integrity: sha512-2FilF6ZV1iO/AtI8+xuKhsA+dJXe/BnR2iukfQezr4TM4D/tkkUInKXAEs46oI2H+6K7jRZjvIkt49tzWkIpaA==} engines: {node: '>=12.0.0'} '@guardian/prettier@8.0.1': @@ -6183,7 +6183,7 @@ snapshots: optionalDependencies: typescript: 5.5.4 - '@guardian/prebid.js@8.52.0-12(babel-core@7.0.0-bridge.0(@babel/core@7.26.0))(ejs@3.1.10)(tslib@2.8.1)(typescript@5.5.4)': + '@guardian/prebid.js@8.52.0-13(babel-core@7.0.0-bridge.0(@babel/core@7.26.0))(ejs@3.1.10)(tslib@2.8.1)(typescript@5.5.4)': dependencies: '@babel/core': 7.26.0 '@babel/plugin-transform-runtime': 7.25.9(@babel/core@7.26.0) diff --git a/src/experiments/ab-tests.ts b/src/experiments/ab-tests.ts index 34824f1b0..0632b74fc 100644 --- a/src/experiments/ab-tests.ts +++ b/src/experiments/ab-tests.ts @@ -2,6 +2,7 @@ import type { ABTest } from '@guardian/ab-core'; import { mpuWhenNoEpic } from './tests/mpu-when-no-epic'; import { optOutFrequencyCap } from './tests/opt-out-frequency-cap'; import { prebidKeywords } from './tests/prebid-keywords'; +import { ttdPrebidBidder } from './tests/ttd-prebid'; /** * You only need to add tests to this file if the code you are testing is here in @@ -13,4 +14,5 @@ export const concurrentTests: ABTest[] = [ mpuWhenNoEpic, optOutFrequencyCap, prebidKeywords, + ttdPrebidBidder, ]; diff --git a/src/experiments/tests/ttd-prebid.ts b/src/experiments/tests/ttd-prebid.ts new file mode 100644 index 000000000..a981717a8 --- /dev/null +++ b/src/experiments/tests/ttd-prebid.ts @@ -0,0 +1,28 @@ +import type { ABTest } from '@guardian/ab-core'; + +export const ttdPrebidBidder: ABTest = { + id: 'TheTradeDesk', + author: '@commercial-dev', + start: '2025-01-30', + expiry: '2025-02-28', + audience: 0 / 100, + audienceOffset: 0 / 100, + audienceCriteria: '', + successMeasure: 'Verify The Trade Desk bidder is added to Prebid.js', + description: 'Verify The Trade Desk bidder is added to Prebid.js', + variants: [ + { + id: 'control', + test: (): void => { + /* no-op */ + }, + }, + { + id: 'variant', + test: (): void => { + /* no-op */ + }, + }, + ], + canRun: () => true, +}; diff --git a/src/lib/header-bidding/prebid-types.ts b/src/lib/header-bidding/prebid-types.ts index a494f09b9..89d0d8fbd 100644 --- a/src/lib/header-bidding/prebid-types.ts +++ b/src/lib/header-bidding/prebid-types.ts @@ -113,6 +113,12 @@ export type PrebidMagniteParams = { keywords: string[]; }; +export type PrebidTheTradeDeskParams = { + supplySourceId: number; + publisherId: number; + placementId: string; +}; + export type BidderCode = | 'adyoulike' | 'and' @@ -127,7 +133,8 @@ export type BidderCode = | 'pubmatic' | 'triplelift' | 'trustx' - | 'xhb'; + | 'xhb' + | 'ttd'; export type PrebidParams = | PrebidAdYouLikeParams @@ -142,7 +149,8 @@ export type PrebidParams = | PrebidPubmaticParams | PrebidTripleLiftParams | PrebidTrustXParams - | PrebidXaxisParams; + | PrebidXaxisParams + | PrebidTheTradeDeskParams; export type PrebidBidder = { name: BidderCode; diff --git a/src/lib/header-bidding/prebid/bid-config.spec.ts b/src/lib/header-bidding/prebid/bid-config.spec.ts index c6492821b..ff27dd6bf 100644 --- a/src/lib/header-bidding/prebid/bid-config.spec.ts +++ b/src/lib/header-bidding/prebid/bid-config.spec.ts @@ -35,6 +35,7 @@ const getBidders = () => 'dfp-ad--top-above-nav', [createAdSize(728, 90)], mockPageTargeting, + 'gpid', ).map((bid) => bid.bidder); const { @@ -289,6 +290,7 @@ describe('bids', () => { 'dfp-right', [createAdSize(300, 600), createAdSize(300, 250)], mockPageTargeting, + 'gpid', ).map((bid) => bid.bidder); expect(rightSlotBidders()).toEqual(['ix', 'ix', 'criteo', 'adyoulike']); }); @@ -332,6 +334,7 @@ describe('bids', () => { 'dfp-ad--top-above-nav', [createAdSize(728, 90)], mockPageTargeting, + 'gpid', )[3]; expect(openXBid?.params).toEqual({ customParams: 'someAppNexusTargetingObject', @@ -347,6 +350,7 @@ describe('bids', () => { 'dfp-ad--top-above-nav', [createAdSize(728, 90)], mockPageTargeting, + 'gpid', )[3]; expect(openXBid?.params).toEqual({ customParams: 'someAppNexusTargetingObject', @@ -362,6 +366,7 @@ describe('bids', () => { 'dfp-ad--top-above-nav', [createAdSize(728, 90)], mockPageTargeting, + 'gpid', )[3]; expect(openXBid?.params).toEqual({ customParams: 'someAppNexusTargetingObject', @@ -377,6 +382,7 @@ describe('bids', () => { 'dfp-ad--top-above-nav', [createAdSize(728, 90)], mockPageTargeting, + 'gpid', )[3]; expect(openXBid?.params).toEqual({ customParams: 'someAppNexusTargetingObject', @@ -392,6 +398,7 @@ describe('bids', () => { 'dfp-ad--mobile-sticky', [createAdSize(320, 50)], mockPageTargeting, + 'gpid', )[3]; expect(openXBid?.params).toEqual({ customParams: 'someAppNexusTargetingObject', @@ -431,6 +438,7 @@ describe('triplelift adapter', () => { 'dfp-ad--top-above-nav', [createAdSize(728, 90)], mockPageTargeting, + 'gpid', )[2]?.params; expect(tripleLiftBids).toEqual({ inventoryCode: 'theguardian_topbanner_728x90_prebid', @@ -447,6 +455,7 @@ describe('triplelift adapter', () => { 'dfp-ad--top-above-nav', [createAdSize(728, 90)], mockPageTargeting, + 'gpid', )[2]?.params; expect(tripleLiftBids).toEqual({ inventoryCode: 'theguardian_topbanner_728x90_prebid_AU', @@ -464,6 +473,7 @@ describe('triplelift adapter', () => { 'dfp-ad--inline1', [createAdSize(300, 250)], mockPageTargeting, + 'gpid', )[2]?.params; expect(tripleLiftBids).toEqual({ inventoryCode: 'theguardian_sectionfront_300x250_prebid', @@ -481,6 +491,7 @@ describe('triplelift adapter', () => { 'dfp-ad--inline1', [createAdSize(300, 250)], mockPageTargeting, + 'gpid', )[2]?.params; expect(tripleLiftBids).toEqual({ inventoryCode: 'theguardian_sectionfront_300x250_prebid_AU', @@ -498,6 +509,7 @@ describe('triplelift adapter', () => { 'dfp-ad--top-above-nav', [createAdSize(320, 50)], mockPageTargeting, + 'gpid', )[2]?.params; expect(tripleLiftBids).toEqual({ inventoryCode: 'theguardian_320x50_HDX', @@ -515,6 +527,7 @@ describe('triplelift adapter', () => { 'dfp-ad--top-above-nav', [createAdSize(320, 50)], mockPageTargeting, + 'gpid', )[2]?.params; expect(tripleLiftBids).toEqual({ inventoryCode: 'theguardian_320x50_HDX_AU', diff --git a/src/lib/header-bidding/prebid/bid-config.ts b/src/lib/header-bidding/prebid/bid-config.ts index 7ca6b4ca0..d48547117 100644 --- a/src/lib/header-bidding/prebid/bid-config.ts +++ b/src/lib/header-bidding/prebid/bid-config.ts @@ -47,6 +47,7 @@ import { shouldIncludeKargo, shouldIncludeMagnite, shouldIncludeOpenx, + shouldIncludeTheTradeDesk, shouldIncludeTripleLift, shouldIncludeTrustX, shouldIncludeXaxis, @@ -629,6 +630,16 @@ const magniteBidder: PrebidBidder = { }), }; +const theTradeDeskBidder = (gpid: string): PrebidBidder => ({ + name: 'ttd', + switchName: 'prebidTheTradeDesk', + bidParams: () => ({ + supplySourceId: 'theguardian', + publisherId: '1', + placementId: gpid, + }), +}); + // There's an IX bidder for every size that the slot can take const indexExchangeBidders = ( slotSizes: HeaderBiddingSize[], @@ -657,6 +668,7 @@ const biddersSwitchedOn = (allBidders: PrebidBidder[]): PrebidBidder[] => { const currentBidders = ( slotSizes: HeaderBiddingSize[], pageTargeting: PageTargeting, + gpid: string, ): PrebidBidder[] => { const biddersToCheck: Array<[boolean, PrebidBidder]> = [ [true, criteoBidder(slotSizes)], @@ -672,6 +684,7 @@ const currentBidders = ( [shouldIncludeOpenx(), openxClientSideBidder(pageTargeting)], [shouldIncludeKargo(), kargoBidder], [shouldIncludeMagnite(), magniteBidder], + [shouldIncludeTheTradeDesk(), theTradeDeskBidder(gpid)], ]; const otherBidders = biddersToCheck @@ -688,11 +701,14 @@ export const bids = ( slotId: string, slotSizes: HeaderBiddingSize[], pageTargeting: PageTargeting, + gpid: string, ): PrebidBid[] => - currentBidders(slotSizes, pageTargeting).map((bidder: PrebidBidder) => ({ - bidder: bidder.name, - params: bidder.bidParams(slotId, slotSizes), - })); + currentBidders(slotSizes, pageTargeting, gpid).map( + (bidder: PrebidBidder) => ({ + bidder: bidder.name, + params: bidder.bidParams(slotId, slotSizes), + }), + ); export const _ = { getIndexSiteIdFromConfig, diff --git a/src/lib/header-bidding/prebid/prebid.ts b/src/lib/header-bidding/prebid/prebid.ts index 29a6b0b76..289cd6699 100644 --- a/src/lib/header-bidding/prebid/prebid.ts +++ b/src/lib/header-bidding/prebid/prebid.ts @@ -191,7 +191,6 @@ class PrebidAdUnit { pageTargeting: PageTargeting, ) { this.code = advert.id; - this.bids = bids(advert.id, slot.sizes, pageTargeting); this.mediaTypes = { banner: { sizes: slot.sizes } }; this.gpid = this.generateGpid(advert, slot); this.ortb2Imp = { @@ -203,6 +202,8 @@ class PrebidAdUnit { }, }; + this.bids = bids(advert.id, slot.sizes, pageTargeting, this.gpid); + advert.headerBiddingSizes = slot.sizes; log('commercial', `PrebidAdUnit ${this.code}`, this.bids); } diff --git a/src/lib/header-bidding/utils.ts b/src/lib/header-bidding/utils.ts index 5a676208e..7dcc005ce 100644 --- a/src/lib/header-bidding/utils.ts +++ b/src/lib/header-bidding/utils.ts @@ -1,5 +1,7 @@ import { isString } from '@guardian/libs'; import { once } from 'lodash-es'; +import { isUserInVariant } from '../../experiments/ab'; +import { ttdPrebidBidder } from '../../experiments/tests/ttd-prebid'; import { createAdSize } from '../../lib/ad-sizes'; import { isInAuOrNz, @@ -189,6 +191,9 @@ export const shouldIncludeKargo = (): boolean => isInUsa(); export const shouldIncludeMagnite = (): boolean => !!window.guardian.config.switches.prebidMagnite; +export const shouldIncludeTheTradeDesk = (): boolean => + isUserInVariant(ttdPrebidBidder, 'variant'); + export const shouldIncludeMobileSticky = once( (): boolean => window.location.hash.includes('#mobile-sticky') ||