Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fledgeForGpt: use imp.ext.ae instead of defaultForSlots; add prebid.ortb2 and prebid.ortb2Imp auction signals #10649

Merged
merged 5 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions modules/fledgeForGpt.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,20 @@ function onAuctionEnd({auctionId, bidsReceived, bidderRequests}) {
}
}

export function addComponentAuctionHook(next, auctionId, adUnitCode, componentAuctionConfig) {
function setFPDSignals(auctionConfig, fpd) {
auctionConfig.auctionSignals = mergeDeep({}, {prebid: fpd}, auctionConfig.auctionSignals);
}

export function addComponentAuctionHook(next, request, componentAuctionConfig) {
const {adUnitCode, auctionId, ortb2, ortb2Imp} = request;
if (PENDING.hasOwnProperty(auctionId)) {
setFPDSignals(componentAuctionConfig, {ortb2, ortb2Imp});
!PENDING[auctionId].hasOwnProperty(adUnitCode) && (PENDING[auctionId][adUnitCode] = []);
PENDING[auctionId][adUnitCode].push(componentAuctionConfig);
} else {
logWarn(MODULE, `Received component auction config for auction that has closed (auction '${auctionId}', adUnit '${adUnitCode}')`, componentAuctionConfig)
}
next(auctionId, adUnitCode, componentAuctionConfig);
next(request, componentAuctionConfig);
}

function isFledgeSupported() {
Expand All @@ -114,25 +120,21 @@ export function markForFledge(next, bidderRequests) {
if (isFledgeSupported()) {
const globalFledgeConfig = config.getConfig('fledgeForGpt');
const bidders = globalFledgeConfig?.bidders ?? [];
bidderRequests.forEach((req) => {
const useGlobalConfig = globalFledgeConfig?.enabled && (bidders.length == 0 || bidders.includes(req.bidderCode));
Object.assign(req, config.runWithBidder(req.bidderCode, () => {
return {
fledgeEnabled: config.getConfig('fledgeEnabled') ?? (useGlobalConfig ? globalFledgeConfig.enabled : undefined),
defaultForSlots: config.getConfig('defaultForSlots') ?? (useGlobalConfig ? globalFledgeConfig?.defaultForSlots : undefined)
}
}));
bidderRequests.forEach((bidderReq) => {
const useGlobalConfig = globalFledgeConfig?.enabled && (bidders.length === 0 || bidders.includes(bidderReq.bidderCode));
config.runWithBidder(bidderReq.bidderCode, () => {
const fledgeEnabled = config.getConfig('fledgeEnabled') ?? (useGlobalConfig ? globalFledgeConfig.enabled : undefined);
const defaultForSlots = config.getConfig('defaultForSlots') ?? (useGlobalConfig ? globalFledgeConfig?.defaultForSlots : undefined);
Object.assign(bidderReq, {fledgeEnabled});
bidderReq.bids.forEach(bidReq => { deepSetValue(bidReq, 'ortb2Imp.ext.ae', bidReq.ortb2Imp?.ext?.ae ?? defaultForSlots) })
})
});
}
next(bidderRequests);
}

export function setImpExtAe(imp, bidRequest, context) {
if (context.bidderRequest.fledgeEnabled) {
imp.ext = Object.assign(imp.ext || {}, {
ae: imp.ext?.ae ?? context.bidderRequest.defaultForSlots
})
} else {
if (imp.ext?.ae && !context.bidderRequest.fledgeEnabled) {
delete imp.ext?.ae;
}
}
Expand Down
4 changes: 2 additions & 2 deletions modules/prebidServerBidAdapter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,8 @@ export function PrebidServer() {
}
}
},
onFledge: ({adUnitCode, config}) => {
addComponentAuction(bidRequests[0].auctionId, adUnitCode, config);
onFledge: (params) => {
addComponentAuction({auctionId: bidRequests[0].auctionId, ...params}, params.config);
}
})
}
Expand Down
11 changes: 10 additions & 1 deletion modules/prebidServerBidAdapter/ortbConverter.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,16 @@ const PBS_CONVERTER = ortbConverter({
},
fledgeAuctionConfigs(orig, response, ortbResponse, context) {
const configs = Object.values(context.impContext)
.flatMap((impCtx) => (impCtx.fledgeConfigs || []).map(cfg => ({adUnitCode: impCtx.adUnit.code, config: cfg.config})));
.flatMap((impCtx) => (impCtx.fledgeConfigs || []).map(cfg => {
const bidderReq = impCtx.actualBidderRequests.find(br => br.bidderCode === cfg.bidder);
const bidReq = impCtx.actualBidRequests.get(cfg.bidder);
return {
adUnitCode: impCtx.adUnit.code,
ortb2: bidderReq?.ortb2,
ortb2Imp: bidReq?.ortb2Imp,
config: cfg.config
};
}));
if (configs.length > 0) {
response.fledgeAuctionConfigs = configs;
}
Expand Down
4 changes: 2 additions & 2 deletions src/adapters/bidderFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ export function newBidder(spec) {
fledgeAuctionConfigs.forEach((fledgeAuctionConfig) => {
const bidRequest = bidRequestMap[fledgeAuctionConfig.bidId];
if (bidRequest) {
addComponentAuction(bidRequest.auctionId, bidRequest.adUnitCode, fledgeAuctionConfig.config);
addComponentAuction(bidRequest, fledgeAuctionConfig.config);
} else {
logWarn('Received fledge auction configuration for an unknown bidId', fledgeAuctionConfig);
}
Expand Down Expand Up @@ -525,7 +525,7 @@ export const registerSyncInner = hook('async', function(spec, responses, gdprCon
}
}, 'registerSyncs')

export const addComponentAuction = hook('sync', (adUnitCode, fledgeAuctionConfig) => {
export const addComponentAuction = hook('sync', (request, fledgeAuctionConfig) => {
}, 'addComponentAuction');

// check that the bid has a width and height set
Expand Down
147 changes: 68 additions & 79 deletions test/spec/modules/fledgeForGpt_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,17 @@ describe('fledgeForGpt module', () => {
});

it('should call next()', function () {
fledge.addComponentAuctionHook(nextFnSpy, 'aid', 'auc', fledgeAuctionConfig);
sinon.assert.calledWith(nextFnSpy, 'aid', 'auc', fledgeAuctionConfig);
const request = {auctionId: 'aid', adUnitCode: 'auc'};
fledge.addComponentAuctionHook(nextFnSpy, request, fledgeAuctionConfig);
sinon.assert.calledWith(nextFnSpy, request, fledgeAuctionConfig);
});

it('should collect auction configs and route them to GPT at end of auction', () => {
events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {auctionId: 'aid'});
const cf1 = {...fledgeAuctionConfig, id: 1, seller: 'b1'};
const cf2 = {...fledgeAuctionConfig, id: 2, seller: 'b2'};
fledge.addComponentAuctionHook(nextFnSpy, 'aid', 'au1', cf1);
fledge.addComponentAuctionHook(nextFnSpy, 'aid', 'au2', cf2);
fledge.addComponentAuctionHook(nextFnSpy, {auctionId: 'aid', adUnitCode: 'au1'}, cf1);
fledge.addComponentAuctionHook(nextFnSpy, {auctionId: 'aid', adUnitCode: 'au2'}, cf2);
events.emit(CONSTANTS.EVENTS.AUCTION_END, {auctionId: 'aid'});
sinon.assert.calledWith(gptUtils.getGptSlotForAdUnitCode, 'au1');
sinon.assert.calledWith(gptUtils.getGptSlotForAdUnitCode, 'au2');
Expand All @@ -75,11 +76,31 @@ describe('fledgeForGpt module', () => {
it('should drop auction configs after end of auction', () => {
events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {auctionId: 'aid'});
events.emit(CONSTANTS.EVENTS.AUCTION_END, {auctionId: 'aid'});
fledge.addComponentAuctionHook(nextFnSpy, 'aid', 'au', fledgeAuctionConfig);
fledge.addComponentAuctionHook(nextFnSpy, {auctionId: 'aid', adUnitCode: 'au'}, fledgeAuctionConfig);
events.emit(CONSTANTS.EVENTS.AUCTION_END, {auctionId: 'aid'});
sinon.assert.notCalled(mockGptSlot.setConfig);
});

it('should augment auctionSignals with FPD', () => {
events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {auctionId: 'aid'});
fledge.addComponentAuctionHook(nextFnSpy, {auctionId: 'aid', adUnitCode: 'au1', ortb2: {fpd: 1}, ortb2Imp: {fpd: 2}}, fledgeAuctionConfig);
events.emit(CONSTANTS.EVENTS.AUCTION_END, {auctionId: 'aid'});
sinon.assert.calledWith(mockGptSlot.setConfig, {
componentAuction: [{
configKey: 'bidder',
auctionConfig: {
...fledgeAuctionConfig,
auctionSignals: {
prebid: {
ortb2: {fpd: 1},
ortb2Imp: {fpd: 2}
}
}
},
}]
})
})

describe('floor signal', () => {
before(() => {
if (!getGlobal().convertCurrency) {
Expand Down Expand Up @@ -173,7 +194,7 @@ describe('fledgeForGpt module', () => {

it('should populate bidfloor/bidfloorcur', () => {
events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {auctionId: 'aid'});
fledge.addComponentAuctionHook(nextFnSpy, 'aid', 'au', fledgeAuctionConfig);
fledge.addComponentAuctionHook(nextFnSpy, {auctionId: 'aid', adUnitCode: 'au'}, fledgeAuctionConfig);
events.emit(CONSTANTS.EVENTS.AUCTION_END, payload);
sinon.assert.calledWith(mockGptSlot.setConfig, sinon.match(arg => {
return arg.componentAuction.some(au => au.auctionConfig.auctionSignals?.prebid?.bidfloor === bidfloor && au.auctionConfig.auctionSignals?.prebid?.bidfloorcur === bidfloorcur)
Expand Down Expand Up @@ -223,6 +244,24 @@ describe('fledgeForGpt module', () => {
},
]
}];
function expectFledgeFlags(...enableFlags) {
const bidRequests = adapterManager.makeBidRequests(
adUnits,
Date.now(),
utils.getUniqueIdentifierStr(),
function callback() {
},
[]
);

expect(bidRequests[0].bids[0].bidder).equals('appnexus');
expect(bidRequests[0].fledgeEnabled).to.eql(enableFlags[0].enabled)
bidRequests[0].bids.forEach(bid => expect(bid.ortb2Imp.ext.ae).to.eql(enableFlags[0].ae))

expect(bidRequests[1].bids[0].bidder).equals('rubicon');
expect(bidRequests[1].fledgeEnabled).to.eql(enableFlags[1].enabled)
bidRequests[1].bids.forEach(bid => expect(bid.ortb2Imp?.ext?.ae).to.eql(enableFlags[1].ae));
}

describe('with setBidderConfig()', () => {
it('should set fledgeEnabled correctly per bidder', function () {
Expand All @@ -234,23 +273,7 @@ describe('fledgeForGpt module', () => {
defaultForSlots: 1,
}
});

const bidRequests = adapterManager.makeBidRequests(
adUnits,
Date.now(),
utils.getUniqueIdentifierStr(),
function callback() {
},
[]
);

expect(bidRequests[0].bids[0].bidder).equals('appnexus');
expect(bidRequests[0].fledgeEnabled).to.be.true;
expect(bidRequests[0].defaultForSlots).to.equal(1);

expect(bidRequests[1].bids[0].bidder).equals('rubicon');
expect(bidRequests[1].fledgeEnabled).to.be.undefined;
expect(bidRequests[1].defaultForSlots).to.be.undefined;
expectFledgeFlags({enabled: true, ae: 1}, {enabled: void 0, ae: void 0});
});
});

Expand All @@ -264,23 +287,7 @@ describe('fledgeForGpt module', () => {
defaultForSlots: 1,
}
});

const bidRequests = adapterManager.makeBidRequests(
adUnits,
Date.now(),
utils.getUniqueIdentifierStr(),
function callback() {
},
[]
);

expect(bidRequests[0].bids[0].bidder).equals('appnexus');
expect(bidRequests[0].fledgeEnabled).to.be.true;
expect(bidRequests[0].defaultForSlots).to.equal(1);

expect(bidRequests[1].bids[0].bidder).equals('rubicon');
expect(bidRequests[1].fledgeEnabled).to.be.undefined;
expect(bidRequests[1].defaultForSlots).to.be.undefined;
expectFledgeFlags({enabled: true, ae: 1}, {enabled: void 0, ae: void 0});
});

it('should set fledgeEnabled correctly for all bidders', function () {
Expand All @@ -291,51 +298,33 @@ describe('fledgeForGpt module', () => {
defaultForSlots: 1,
}
});

const bidRequests = adapterManager.makeBidRequests(
adUnits,
Date.now(),
utils.getUniqueIdentifierStr(),
function callback() {
},
[]
);

expect(bidRequests[0].bids[0].bidder).equals('appnexus');
expect(bidRequests[0].fledgeEnabled).to.be.true;
expect(bidRequests[0].defaultForSlots).to.equal(1);

expect(bidRequests[1].bids[0].bidder).equals('rubicon');
expect(bidRequests[0].fledgeEnabled).to.be.true;
expect(bidRequests[0].defaultForSlots).to.equal(1);
expectFledgeFlags({enabled: true, ae: 1}, {enabled: true, ae: 1});
});

it('should not override pub-defined ext.ae', () => {
config.setConfig({
bidderSequence: 'fixed',
fledgeForGpt: {
enabled: true,
defaultForSlots: 1,
}
});
Object.assign(adUnits[0], {ortb2Imp: {ext: {ae: 0}}});
expectFledgeFlags({enabled: true, ae: 0}, {enabled: true, ae: 0});
})
});
});

describe('ortb processors for fledge', () => {
describe('when defaultForSlots is set', () => {
it('imp.ext.ae should be set if fledge is enabled', () => {
const imp = {};
setImpExtAe(imp, {}, {bidderRequest: {fledgeEnabled: true, defaultForSlots: 1}});
expect(imp.ext.ae).to.equal(1);
});
it('imp.ext.ae should be left intact if set on adunit and fledge is enabled', () => {
const imp = {ext: {ae: 2}};
setImpExtAe(imp, {}, {bidderRequest: {fledgeEnabled: true, defaultForSlots: 1}});
expect(imp.ext.ae).to.equal(2);
});
it('imp.ext.ae should be removed if fledge is not enabled', () => {
const imp = {ext: {ae: 1}};
setImpExtAe(imp, {}, {bidderRequest: {}});
expect(imp.ext.ae).to.not.exist;
});
describe('when defaultForSlots is not defined', () => {
it('imp.ext.ae should be removed if fledge is not enabled', () => {
const imp = {ext: {ae: 1}};
setImpExtAe(imp, {}, {bidderRequest: {}});
expect(imp.ext.ae).to.not.exist;
});
it('imp.ext.ae should be left intact if fledge is enabled', () => {
const imp = {ext: {ae: 2}};
setImpExtAe(imp, {}, {bidderRequest: {fledgeEnabled: true}});
expect(imp.ext.ae).to.equal(2);
});
it('imp.ext.ae should be left intact if fledge is enabled', () => {
const imp = {ext: {ae: 2}};
setImpExtAe(imp, {}, {bidderRequest: {fledgeEnabled: true}});
expect(imp.ext.ae).to.equal(2);
});
describe('parseExtPrebidFledge', () => {
function packageConfigs(configs) {
Expand Down
Loading