diff --git a/package.json b/package.json index e6f80177e..c756a41b1 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "dependencies": { "@changesets/cli": "^2.26.2", "@guardian/ophan-tracker-js": "2.1.1", - "@guardian/prebid.js": "8.52.0", + "@guardian/prebid.js": "8.52.0-1", "@octokit/core": "^6.1.2", "fastdom": "^1.0.11", "lodash-es": "^4.17.21", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d23e62410..583ea0a7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ dependencies: specifier: 2.1.1 version: 2.1.1 '@guardian/prebid.js': - specifier: 8.52.0 - version: 8.52.0(babel-core@7.0.0-bridge.0)(tslib@2.6.3)(typescript@5.5.3) + specifier: 8.52.0-1 + version: 8.52.0-1(babel-core@7.0.0-bridge.0)(tslib@2.6.3)(typescript@5.3.3) '@octokit/core': specifier: ^6.1.2 version: 6.1.2 @@ -2070,8 +2070,8 @@ packages: engines: {node: '>=16'} dev: false - /@guardian/prebid.js@8.52.0(babel-core@7.0.0-bridge.0)(tslib@2.6.3)(typescript@5.5.3): - resolution: {integrity: sha512-ttN87kbWNDeIfyBBd9zEoFDK4ofY3fa3Tqgi0vSa6L+EiQBvZ6WfXk3dvHBsFssXYM3Zz6jNcbq3XnGY1C5iRA==} + /@guardian/prebid.js@8.52.0-1(babel-core@7.0.0-bridge.0)(tslib@2.6.3)(typescript@5.3.3): + resolution: {integrity: sha512-nXEKVwJ0dGpKFZp31tiAHQ4gQAgLq6Ob/e3X6cxx1P87FeFBg7zFj3pgk6VfxySz6R6SXIL5BOts/SSTkVwlUQ==} engines: {node: '>=12.0.0'} dependencies: '@babel/core': 7.24.7 diff --git a/src/experiments/ab-tests.ts b/src/experiments/ab-tests.ts index 8eee1e54f..18170864a 100644 --- a/src/experiments/ab-tests.ts +++ b/src/experiments/ab-tests.ts @@ -1,5 +1,6 @@ import type { ABTest } from '@guardian/ab-core'; import { mpuWhenNoEpic } from './tests/mpu-when-no-epic'; +import { prebidMagnite } from './tests/prebid-magnite'; /** * You only need to add tests to this file if the code you are testing is here in @@ -9,4 +10,5 @@ import { mpuWhenNoEpic } from './tests/mpu-when-no-epic'; export const concurrentTests: ABTest[] = [ // one test per line mpuWhenNoEpic, + prebidMagnite, ]; diff --git a/src/experiments/tests/prebid-magnite.ts b/src/experiments/tests/prebid-magnite.ts new file mode 100644 index 000000000..40cb11c9d --- /dev/null +++ b/src/experiments/tests/prebid-magnite.ts @@ -0,0 +1,28 @@ +import type { ABTest } from '@guardian/ab-core'; + +export const prebidMagnite: ABTest = { + id: 'PrebidMagnite', + author: '@commercial-dev', + start: '2024-07-18', + expiry: '2024-09-30', + audience: 0 / 100, + audienceOffset: 0 / 100, + audienceCriteria: '', + successMeasure: '', + description: 'Test Magnite as a prebid bidder.', + 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 fb36bb844..69a0ad4ab 100644 --- a/src/lib/header-bidding/prebid-types.ts +++ b/src/lib/header-bidding/prebid-types.ts @@ -119,6 +119,13 @@ export type PrebidKargoParams = { placementId: string; }; +//This is used to be called Rubicon but now it's called Magnite. You can find it in the Prebid.js codebase as Rubicon +export type PrebidMagniteParams = { + accountId: number; + siteId: number; + zoneId: number; +}; + export type BidderCode = | 'adyoulike' | 'and' @@ -127,6 +134,7 @@ export type BidderCode = | 'improvedigital' | 'ix' | 'kargo' + | 'magnite' | 'oxd' | 'ozone' | 'pubmatic' @@ -143,6 +151,7 @@ export type PrebidParams = | PrebidImproveParams | PrebidIndexExchangeParams | PrebidKargoParams + | PrebidMagniteParams | PrebidOpenXParams | PrebidOzoneParams | PrebidPubmaticParams diff --git a/src/lib/header-bidding/prebid/bid-config.ts b/src/lib/header-bidding/prebid/bid-config.ts index 13cfdb187..d281dfcea 100644 --- a/src/lib/header-bidding/prebid/bid-config.ts +++ b/src/lib/header-bidding/prebid/bid-config.ts @@ -24,6 +24,7 @@ import type { PrebidImproveParams, PrebidIndexExchangeParams, PrebidKargoParams, + PrebidMagniteParams, PrebidOpenXParams, PrebidOzoneParams, PrebidPubmaticParams, @@ -41,6 +42,8 @@ import { containsMobileSticky, containsMpu, containsMpuOrDmpu, + containsPortraitInterstitial, + containsWS, getBreakpointKey, shouldIncludeAdYouLike, shouldIncludeAppNexus, @@ -48,6 +51,7 @@ import { shouldIncludeImproveDigital, shouldIncludeImproveDigitalSkin, shouldIncludeKargo, + shouldIncludeMagnite, shouldIncludeOpenx, shouldIncludeSmart, shouldIncludeSonobi, @@ -384,6 +388,184 @@ const getKargoPlacementId = (sizes: HeaderBiddingSize[]): string => { return '_y9LINEsbfh'; }; +const getMagniteZoneId = ( + slotId: string, + sizes: HeaderBiddingSize[], +): number => { + if (isInUk()) { + switch (getBreakpointKey()) { + case 'D': + if (containsMpuOrDmpu(sizes) || containsWS(sizes)) { + return 3426780; + } + // top-above-nav on desktop + if ( + containsLeaderboardOrBillboard(sizes) && + slotId === 'dfp-ad--top-above-nav' + ) { + return 3426786; + } + // Fronts-banners on desktop + if ( + containsBillboard(sizes) && + slotId.includes('fronts-banner') + ) { + return 3426790; + } + break; + case 'M': + if (containsMpu(sizes) || containsPortraitInterstitial(sizes)) { + return 3426778; + } + break; + default: + return -1; + } + } + + if (isInRow()) { + switch (getBreakpointKey()) { + case 'D': + if (containsMpuOrDmpu(sizes) || containsWS(sizes)) { + return 3426822; + } + // top-above-nav on desktop + if ( + containsLeaderboardOrBillboard(sizes) && + slotId === 'dfp-ad--top-above-nav' + ) { + return 3426828; + } + // Fronts-banners on desktop + if ( + containsBillboard(sizes) && + slotId.includes('fronts-banner') + ) { + return 3426834; + } + break; + case 'M': + if (containsMpu(sizes) || containsPortraitInterstitial(sizes)) { + return 3426836; + } + if (containsMobileSticky(sizes)) { + return 3477560; + } + break; + default: + return -1; + } + } + + if (isInUsOrCa()) { + switch (getBreakpointKey()) { + case 'D': + if (containsMpuOrDmpu(sizes) || containsWS(sizes)) { + return 3471422; + } + // top-above-nav on desktop + if ( + containsLeaderboardOrBillboard(sizes) && + slotId === 'dfp-ad--top-above-nav' + ) { + return 3471428; + } + // Fronts-banners on desktop + if ( + containsBillboard(sizes) && + slotId.includes('fronts-banner') + ) { + return 3471434; + } + break; + case 'M': + if (containsMpu(sizes) || containsPortraitInterstitial(sizes)) { + return 3471436; + } + if (containsMobileSticky(sizes)) { + return 3471440; + } + break; + default: + return -1; + } + } + + if (isInAuOrNz()) { + switch (getBreakpointKey()) { + case 'D': + if (containsMpuOrDmpu(sizes) || containsWS(sizes)) { + return 3471452; + } + // top-above-nav on desktop + if ( + containsLeaderboardOrBillboard(sizes) && + slotId === 'dfp-ad--top-above-nav' + ) { + return 3471458; + } + // Fronts-banners on desktop + if ( + containsBillboard(sizes) && + slotId.includes('fronts-banner') + ) { + return 3471462; + } + break; + case 'M': + if (containsMpu(sizes) || containsPortraitInterstitial(sizes)) { + return 3471464; + } + if (containsMobileSticky(sizes)) { + return 3471468; + } + break; + default: + return -1; + } + } + return -1; +}; + +const getMagniteSiteId = (): number => { + if (isInUk()) { + switch (getBreakpointKey()) { + case 'D': + return 549358; + case 'M': + return 549374; + } + } + + if (isInRow()) { + switch (getBreakpointKey()) { + case 'D': + return 549496; + case 'M': + return 549498; + } + } + + if (isInUsOrCa()) { + switch (getBreakpointKey()) { + case 'D': + return 554244; + case 'M': + return 554248; + } + } + + if (isInAuOrNz()) { + switch (getBreakpointKey()) { + case 'D': + return 554256; + case 'M': + return 554258; + } + } + return -1; +}; + const pubmaticBidder = (slotSizes: HeaderBiddingSize[]): PrebidBidder => { const defaultParams = { name: 'pubmatic' as BidderCode, @@ -533,6 +715,19 @@ const kargoBidder: PrebidBidder = { }), }; +const magniteBidder: PrebidBidder = { + name: 'magnite', + switchName: 'prebidMagnite', + bidParams: ( + _slotId: string, + sizes: HeaderBiddingSize[], + ): PrebidMagniteParams => ({ + accountId: 26644, + siteId: getMagniteSiteId(), + zoneId: getMagniteZoneId(_slotId, sizes), + }), +}; + // There's an IX bidder for every size that the slot can take const indexExchangeBidders = ( slotSizes: HeaderBiddingSize[], @@ -577,6 +772,7 @@ const currentBidders = ( [shouldUseOzoneAdaptor(), ozoneClientSideBidder(pageTargeting)], [shouldIncludeOpenx(), openxClientSideBidder(pageTargeting)], [shouldIncludeKargo(), kargoBidder], + [shouldIncludeMagnite(), magniteBidder], ]; const otherBidders = biddersToCheck diff --git a/src/lib/header-bidding/prebid/prebid.ts b/src/lib/header-bidding/prebid/prebid.ts index 7c1554d5e..3143cbb72 100644 --- a/src/lib/header-bidding/prebid/prebid.ts +++ b/src/lib/header-bidding/prebid/prebid.ts @@ -142,6 +142,7 @@ type BidderSettings = { ozone?: Partial; criteo?: Partial; kargo?: Partial; + magnite?: Partial; }; class PrebidAdUnit { @@ -443,6 +444,12 @@ const initialise = ( }; } + if (window.guardian.config.switches.prebidMagnite) { + window.pbjs.bidderSettings.magnite = { + storageAllowed: true, + }; + } + window.pbjs.setConfig(pbjsConfig); // Adjust slot size when prebid ad loads diff --git a/src/lib/header-bidding/utils.ts b/src/lib/header-bidding/utils.ts index 66e464331..1d8af566a 100644 --- a/src/lib/header-bidding/utils.ts +++ b/src/lib/header-bidding/utils.ts @@ -1,6 +1,8 @@ import { isString } from '@guardian/libs'; import { once } from 'lodash-es'; import { createAdSize } from 'core/ad-sizes'; +import { isUserInVariant } from 'experiments/ab'; +import { prebidMagnite } from 'experiments/tests/prebid-magnite'; import { getCurrentTweakpoint, matchesBreakpoints, @@ -104,6 +106,10 @@ export const containsLeaderboardOrBillboard = ( sizes: HeaderBiddingSize[], ): boolean => containsLeaderboard(sizes) || containsBillboard(sizes); +export const containsPortraitInterstitial = ( + sizes: HeaderBiddingSize[], +): boolean => contains(sizes, createAdSize(320, 480)); + export const getLargestSize = ( sizes: HeaderBiddingSize[], ): HeaderBiddingSize | null => { @@ -194,6 +200,12 @@ export const shouldIncludeSmart = (): boolean => isInUk() || isInRow(); export const shouldIncludeKargo = (): boolean => isInUsa(); +//Add the switch here and the AB test checks +export const shouldIncludeMagnite = (): boolean => + (isInUk() || isInRow() || isInUsOrCa() || isInAuOrNz()) && + !!window.guardian.config.switches.prebidMagnite && + isUserInVariant(prebidMagnite, 'variant'); + export const shouldIncludeMobileSticky = once( (): boolean => window.location.hash.includes('#mobile-sticky') ||