From af8a67d4a4a255911e119d03ffcfc22da9656765 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Fri, 16 Oct 2020 13:29:03 -0700 Subject: [PATCH 1/4] Expose pbs reported errors Expose serverLatencyMillis always --- modules/prebidServerBidAdapter/index.js | 23 +++++++++++++++++ modules/rubiconAnalyticsAdapter.js | 34 ++++++++++++++++++++----- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index b153d0bf8db..4cbe31e0a05 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -676,6 +676,27 @@ const OPEN_RTB_PROTOCOL = { interpretResponse(response, bidderRequests) { const bids = []; + // Should log all bidders server response times, not only if they had successful seatBid + const serverResponseTimeMs = utils.deepAccess(response, 'ext.responsetimemillis'); + if (serverResponseTimeMs) { + Object.keys(serverResponseTimeMs).forEach(bidder => { + let biddersReq = find(bidderRequests, bidderReq => bidderReq.bidderCode === bidder); + if (biddersReq) { + biddersReq.serverResponseTimeMs = serverResponseTimeMs[bidder]; + } + }); + } + + // Attach any errors to a biddersRequest object + const bidderErrors = utils.deepAccess(response, 'ext.errors'); + if (bidderErrors) { + Object.keys(bidderErrors).forEach(bidder => { + let biddersReq = find(bidderRequests, bidderReq => bidderReq.bidderCode === bidder); + if (biddersReq) { + biddersReq.serverErrors = bidderErrors[bidder]; + } + }); + } if (response.seatbid) { // a seatbid object contains a `bid` array and a `seat` string response.seatbid.forEach(seatbid => { @@ -698,6 +719,8 @@ const OPEN_RTB_PROTOCOL = { bidObject.cpm = cpm; + // temporarily leaving attaching it to each bidResponse so no breaking change + // BUT: this is a flat map, so it should be only attached to bidderRequest, a the change above does let serverResponseTimeMs = utils.deepAccess(response, ['ext', 'responsetimemillis', seatbid.seat].join('.')); if (bidRequest && serverResponseTimeMs) { bidRequest.serverResponseTimeMs = serverResponseTimeMs; diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index f6d30e06e9a..af1b8c944c4 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -13,6 +13,14 @@ const COOKIE_NAME = 'rpaSession'; const LAST_SEEN_EXPIRE_TIME = 1800000; // 30 mins const END_EXPIRE_TIME = 21600000; // 6 hours +const pbsErrorMap = { + 1: 'timeout-error', + 2: 'input-error', + 3: 'connect-error', + 4: 'request-error', + 999: 'generic-error' +} + let prebidGlobal = getGlobal(); const { EVENTS: { @@ -628,10 +636,20 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { bid.bidResponse = parseBidResponse(args, bid.bidResponse); break; case BIDDER_DONE: + const serverError = utils.deepAccess(args, 'serverErrors.0'); + const serverResponseTimeMs = utils.deepAccess(args, 'serverResponseTimeMs'); args.bids.forEach(bid => { let cachedBid = cache.auctions[bid.auctionId].bids[bid.bidId || bid.requestId]; - if (typeof bid.serverResponseTimeMs !== 'undefined') { - cachedBid.serverLatencyMillis = bid.serverResponseTimeMs; + if (serverResponseTimeMs) { + cachedBid.serverLatencyMillis = serverResponseTimeMs; + } + // if PBS said we had an error, and this bid has not been processed by BID_RESPONSE YET + if (serverError && (!cachedBid.status || ['no-bid', 'error'].indexOf(cachedBid.status) !== -1)) { + cachedBid.status = 'error'; + cachedBid.error = { + code: pbsErrorMap[serverError.code] || pbsErrorMap[999], + description: serverError.message + } } if (!cachedBid.status) { cachedBid.status = 'no-bid'; @@ -672,10 +690,14 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { args.forEach(badBid => { let auctionCache = cache.auctions[badBid.auctionId]; let bid = auctionCache.bids[badBid.bidId || badBid.requestId]; - bid.status = 'error'; - bid.error = { - code: 'timeout-error' - }; + // might be set already by bidder-done, so do not overwrite + if (bid.status !== 'error') { + bid.status = 'error'; + bid.error = { + code: 'timeout-error', + message: 'marked by prebid.js as timeout' // will help us diff if timeout was set by PBS or PBJS + }; + } }); break; } From cf3fca220e383f8ada540caef6afc8df1950d453 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Mon, 19 Oct 2020 12:30:01 -0700 Subject: [PATCH 2/4] clean up logic for other weird edge case --- modules/rubiconAnalyticsAdapter.js | 6 ++++-- test/spec/modules/rubiconAnalyticsAdapter_spec.js | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index af1b8c944c4..9c762f9dc6b 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -637,10 +637,12 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { break; case BIDDER_DONE: const serverError = utils.deepAccess(args, 'serverErrors.0'); - const serverResponseTimeMs = utils.deepAccess(args, 'serverResponseTimeMs'); + const serverResponseTimeMs = args.serverResponseTimeMs; args.bids.forEach(bid => { let cachedBid = cache.auctions[bid.auctionId].bids[bid.bidId || bid.requestId]; - if (serverResponseTimeMs) { + if (typeof bid.serverResponseTimeMs !== 'undefined') { + cachedBid.serverLatencyMillis = bid.serverResponseTimeMs; + } else if (serverResponseTimeMs && bid.source === 's2s') { cachedBid.serverLatencyMillis = serverResponseTimeMs; } // if PBS said we had an error, and this bid has not been processed by BID_RESPONSE YET diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index 2bbab506b34..15575d67d0f 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -256,6 +256,7 @@ const MOCK = { ], BIDDER_DONE: { 'bidderCode': 'rubicon', + 'serverResponseTimeMs': 42, 'bids': [ BID, Object.assign({}, BID2, { From 06bb14e177ef0d475472964fef3a111d85b8d9d5 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Fri, 13 Nov 2020 09:38:42 -0800 Subject: [PATCH 3/4] review comment for make function --- modules/prebidServerBidAdapter/index.js | 34 ++++++++++--------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 4cbe31e0a05..8dc700f8099 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -369,6 +369,18 @@ function addWurl(auctionId, adId, wurl) { } } +function getPbsResponseData(bidderRequests, response, pbsName, pbjsName) { + const bidderValues = utils.deepAccess(response, `ext.${pbsName}`); + if (bidderValues) { + Object.keys(bidderValues).forEach(bidder => { + let biddersReq = find(bidderRequests, bidderReq => bidderReq.bidderCode === bidder); + if (biddersReq) { + biddersReq[pbjsName] = bidderValues[bidder]; + } + }); + } +} + /** * @param {string} auctionId * @param {string} adId generated value set to bidObject.adId by bidderFactory Bid() @@ -676,27 +688,9 @@ const OPEN_RTB_PROTOCOL = { interpretResponse(response, bidderRequests) { const bids = []; - // Should log all bidders server response times, not only if they had successful seatBid - const serverResponseTimeMs = utils.deepAccess(response, 'ext.responsetimemillis'); - if (serverResponseTimeMs) { - Object.keys(serverResponseTimeMs).forEach(bidder => { - let biddersReq = find(bidderRequests, bidderReq => bidderReq.bidderCode === bidder); - if (biddersReq) { - biddersReq.serverResponseTimeMs = serverResponseTimeMs[bidder]; - } - }); - } + [('errors', 'serverErrors'), ('responsetimemillis', 'serverResponseTimeMs')] + .forEach(info => getPbsResponseData(bidderRequests, response, info[0], info[1])) - // Attach any errors to a biddersRequest object - const bidderErrors = utils.deepAccess(response, 'ext.errors'); - if (bidderErrors) { - Object.keys(bidderErrors).forEach(bidder => { - let biddersReq = find(bidderRequests, bidderReq => bidderReq.bidderCode === bidder); - if (biddersReq) { - biddersReq.serverErrors = bidderErrors[bidder]; - } - }); - } if (response.seatbid) { // a seatbid object contains a `bid` array and a `seat` string response.seatbid.forEach(seatbid => { From eaa8a0e898a6c4a01f61d0d92a53c1c5a6cf2e1b Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Fri, 13 Nov 2020 10:15:54 -0800 Subject: [PATCH 4/4] not sure how parenthesis got there --- modules/prebidServerBidAdapter/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 8dc700f8099..7c7962781d2 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -688,7 +688,7 @@ const OPEN_RTB_PROTOCOL = { interpretResponse(response, bidderRequests) { const bids = []; - [('errors', 'serverErrors'), ('responsetimemillis', 'serverResponseTimeMs')] + [['errors', 'serverErrors'], ['responsetimemillis', 'serverResponseTimeMs']] .forEach(info => getPbsResponseData(bidderRequests, response, info[0], info[1])) if (response.seatbid) {