Skip to content

Commit

Permalink
Magnite Analytics Adapter : add seat non bid handling (#9696)
Browse files Browse the repository at this point in the history
* Return all bids

* Adjust findMatch function

* Return all buds unit testing

* Responds to review comments

* Unit test adjustments

* Remove extra line for lint

* minor changes

* doh

---------

Co-authored-by: Robert Ray Martinez III <[email protected]>
  • Loading branch information
spotxslagle and robertrmartinez authored Apr 13, 2023
1 parent 584af55 commit 7f43556
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 4 deletions.
79 changes: 76 additions & 3 deletions modules/magniteAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ const {
BIDDER_DONE,
BID_TIMEOUT,
BID_WON,
BILLABLE_EVENT
BILLABLE_EVENT,
SEAT_NON_BID
},
STATUS: {
GOOD,
Expand Down Expand Up @@ -483,7 +484,7 @@ const findMatchingAdUnitFromAuctions = (matchesFunction, returnFirstMatch) => {
}
}
return matches;
}
};

const getRenderingIds = bidWonData => {
// if bid caching off -> return the bidWon auction id
Expand Down Expand Up @@ -840,7 +841,16 @@ magniteAdapter.track = ({ eventType, args }) => {
auctionEntry.floors.dealsEnforced = args.floorData.enforcements.floorDeals;
}

// Log error if no matching bid!
// no-bid from server. report it!
if (!bid && args.seatBidId) {
bid = adUnit.bids[args.seatBidId] = {
bidder: args.bidderCode,
source: 'server',
bidId: args.seatBidId,
unknownBid: true
};
}

if (!bid) {
logError(`${MODULE_NAME}: Could not find associated bid request for bid response with requestId: `, args.requestId);
break;
Expand Down Expand Up @@ -871,6 +881,9 @@ magniteAdapter.track = ({ eventType, args }) => {
bid.pbsBidId = pbsBidId;
}
break;
case SEAT_NON_BID:
handleNonBidEvent(args);
break;
case BIDDER_DONE:
const serverError = deepAccess(args, 'serverErrors.0');
const serverResponseTimeMs = args.serverResponseTimeMs;
Expand Down Expand Up @@ -958,6 +971,66 @@ magniteAdapter.track = ({ eventType, args }) => {
}
};

const handleNonBidEvent = function(args) {
const {seatnonbid, auctionId} = args;
const auction = deepAccess(cache, `auctions.${auctionId}.auction`);
// if no auction just bail
if (!auction) {
logWarn(`Unable to match nonbid to auction`);
return;
}
const adUnits = auction.adUnits;
seatnonbid.forEach(seatnonbid => {
let {seat} = seatnonbid;
seatnonbid.nonbid.forEach(nonbid => {
try {
const {status, impid} = nonbid;
const matchingTid = Object.keys(adUnits).find(tid => adUnits[tid].adUnitCode === impid);
const adUnit = adUnits[matchingTid];
const statusInfo = statusMap[status] || { status: 'no-bid' };
adUnit.bids[generateUUID()] = {
bidder: seat,
source: 'server',
isSeatNonBid: true,
clientLatencyMillis: Date.now() - auction.auctionStart,
...statusInfo
};
} catch (error) {
logWarn(`Unable to match nonbid to adUnit`);
}
});
});
};

const statusMap = {
0: {
status: 'no-bid'
},
100: {
status: 'error',
error: {
code: 'request-error',
description: 'general error'
}
},
101: {
status: 'error',
error: {
code: 'timeout-error',
description: 'prebid server timeout'
}
},
200: {
status: 'rejected'
},
202: {
status: 'rejected'
},
301: {
status: 'rejected-ipf'
}
};

adapterManager.registerAnalyticsAdapter({
adapter: magniteAdapter,
code: 'magnite',
Expand Down
130 changes: 129 additions & 1 deletion test/spec/modules/magniteAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ const {
BIDDER_DONE,
BID_WON,
BID_TIMEOUT,
BILLABLE_EVENT
BILLABLE_EVENT,
SEAT_NON_BID
}
} = CONSTANTS;

Expand Down Expand Up @@ -160,6 +161,16 @@ const MOCK = {
'status': 'rendered',
getStatusCode: () => 1,
},
SEAT_NON_BID: {
auctionId: '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
seatnonbid: [{
seat: 'rubicon',
nonbid: [{
status: 1,
impid: 'box'
}]
}]
},
AUCTION_END: {
'auctionId': '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d',
'auctionEnd': 1658868384019,
Expand Down Expand Up @@ -2039,4 +2050,121 @@ describe('magnite analytics adapter', function () {
}
})
});

describe('BID_RESPONSE events', () => {
beforeEach(() => {
magniteAdapter.enableAnalytics({
options: {
endpoint: '//localhost:9999/event',
accountId: 1001
}
});
config.setConfig({ rubicon: { updatePageView: true } });
});

it('should add a no-bid bid to the add unit if it recieves one from the server', () => {
const bidResponse = utils.deepClone(MOCK.BID_RESPONSE);
const auctionInit = utils.deepClone(MOCK.AUCTION_INIT);

bidResponse.requestId = 'fakeId';
bidResponse.seatBidId = 'fakeId';

bidResponse.requestId = 'fakeId';
events.emit(AUCTION_INIT, auctionInit);
events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
events.emit(BID_RESPONSE, bidResponse)
events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
events.emit(AUCTION_END, MOCK.AUCTION_END);
clock.tick(rubiConf.analyticsBatchTimeout + 1000);

let message = JSON.parse(server.requests[0].requestBody);
expect(utils.generateUUID.called).to.equal(true);

expect(message.auctions[0].adUnits[0].bids[1]).to.deep.equal(
{
bidder: 'rubicon',
source: 'server',
status: 'success',
bidResponse: {
'bidPriceUSD': 3.4,
'dimensions': {
'height': 250,
'width': 300
},
'mediaType': 'banner'
},
oldBidId: 'fakeId',
unknownBid: true,
bidId: 'fakeId',
clientLatencyMillis: 271
}
);
});
});

describe('SEAT_NON_BID events', () => {
let seatnonbid;

const runNonBidAuction = () => {
events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
events.emit(SEAT_NON_BID, seatnonbid)
events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
events.emit(AUCTION_END, MOCK.AUCTION_END);
clock.tick(rubiConf.analyticsBatchTimeout + 1000);
};
const checkStatusAgainstCode = (status, code, error, index) => {
seatnonbid.seatnonbid[0].nonbid[0].status = code;
runNonBidAuction();
let message = JSON.parse(server.requests[index].requestBody);
let bid = message.auctions[0].adUnits[0].bids[1];

if (error) {
expect(bid.error).to.deep.equal(error);
} else {
expect(bid.error).to.equal(undefined);
}
expect(bid.source).to.equal('server');
expect(bid.status).to.equal(status);
expect(bid.isSeatNonBid).to.equal(true);
};
beforeEach(() => {
magniteAdapter.enableAnalytics({
options: {
endpoint: '//localhost:9999/event',
accountId: 1001
}
});
seatnonbid = utils.deepClone(MOCK.SEAT_NON_BID);
});

it('adds seatnonbid info to bids array', () => {
runNonBidAuction();
let message = JSON.parse(server.requests[0].requestBody);

expect(message.auctions[0].adUnits[0].bids[1]).to.deep.equal(
{
bidder: 'rubicon',
source: 'server',
status: 'no-bid',
isSeatNonBid: true,
clientLatencyMillis: -139101369960
}
);
});

it('adjusts the status according to the status map', () => {
const statuses = [
{code: 0, status: 'no-bid'},
{code: 100, status: 'error', error: {code: 'request-error', description: 'general error'}},
{code: 101, status: 'error', error: {code: 'timeout-error', description: 'prebid server timeout'}},
{code: 200, status: 'rejected'},
{code: 202, status: 'rejected'},
{code: 301, status: 'rejected-ipf'}
];
statuses.forEach((info, index) => {
checkStatusAgainstCode(info.status, info.code, info.error, index);
});
});
});
});

0 comments on commit 7f43556

Please sign in to comment.