diff --git a/adapters/rhythmone/rhythmone.go b/adapters/rhythmone/rhythmone.go index 7dd94a494c8..a507e778550 100644 --- a/adapters/rhythmone/rhythmone.go +++ b/adapters/rhythmone/rhythmone.go @@ -132,9 +132,9 @@ func (a *RhythmoneAdapter) preProcess(req *openrtb.BidRequest, errors []error) ( errors = append(errors, err) return nil, "", errors } - bidderExtCopy := openrtb_ext.ExtBid{ - Bidder: rhythmoneExtCopy, - } + bidderExtCopy := struct { + Bidder json.RawMessage `json:"bidder,omitempty"` + }{rhythmoneExtCopy} impExtCopy, err := json.Marshal(&bidderExtCopy) if err != nil { errors = append(errors, err) diff --git a/exchange/bidder.go b/exchange/bidder.go index f916e205ace..6df5fafb5f6 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -83,10 +83,6 @@ type pbsOrtbSeatBid struct { // httpCalls is the list of debugging info. It should only be populated if the request.test == 1. // This will become response.ext.debug.httpcalls.{bidder} on the final Response. httpCalls []*openrtb_ext.ExtHttpCall - // ext contains the extension for this seatbid. - // if len(bids) > 0, this will become response.seatbid[i].ext.{bidder} on the final OpenRTB response. - // if len(bids) == 0, this will be ignored because the OpenRTB spec doesn't allow a SeatBid with 0 Bids. - ext json.RawMessage } // adaptBidder converts an adapters.Bidder into an exchange.adaptedBidder. diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index b829ddd787c..dbf8a022255 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -135,10 +135,6 @@ func TestSingleBidder(t *testing.T) { if len(seatBid.httpCalls) != test.httpCallsLen { t.Errorf("The bidder shouldn't log HttpCalls when request.test == 0. Found %d", len(seatBid.httpCalls)) } - - if len(seatBid.ext) != 0 { - t.Errorf("The bidder shouldn't define any seatBid.ext. Got %s", string(seatBid.ext)) - } } } diff --git a/exchange/exchange.go b/exchange/exchange.go index b4fa3a614f6..45081c16e71 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -788,25 +788,9 @@ func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pb // Return an openrtb seatBid for a bidder // BuildBidResponse is responsible for ensuring nil bid seatbids are not included func (e *exchange) makeSeatBid(adapterBid *pbsOrtbSeatBid, adapter openrtb_ext.BidderName, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, returnCreative bool) *openrtb.SeatBid { - seatBid := new(openrtb.SeatBid) - seatBid.Seat = adapter.String() - // Prebid cannot support roadblocking - seatBid.Group = 0 - - if len(adapterBid.ext) > 0 { - sbExt := ExtSeatBid{ - Bidder: adapterBid.ext, - } - - ext, err := json.Marshal(sbExt) - if err != nil { - extError := openrtb_ext.ExtBidderError{ - Code: errortypes.ReadCode(err), - Message: fmt.Sprintf("Error writing SeatBid.Ext: %s", err.Error()), - } - adapterExtra[adapter].Errors = append(adapterExtra[adapter].Errors, extError) - } - seatBid.Ext = ext + seatBid := &openrtb.SeatBid{ + Seat: adapter.String(), + Group: 0, // Prebid cannot support roadblocking } var errList []error @@ -818,39 +802,54 @@ func (e *exchange) makeSeatBid(adapterBid *pbsOrtbSeatBid, adapter openrtb_ext.B return seatBid } -// Create the Bid array inside of SeatBid -func (e *exchange) makeBid(Bids []*pbsOrtbBid, auc *auction, returnCreative bool) ([]openrtb.Bid, []error) { - bids := make([]openrtb.Bid, 0, len(Bids)) - errList := make([]error, 0, 1) - for _, thisBid := range Bids { - bidExt := &openrtb_ext.ExtBid{ - Bidder: thisBid.bid.Ext, - Prebid: &openrtb_ext.ExtBidPrebid{ - Targeting: thisBid.bidTargets, - Type: thisBid.bidType, - Video: thisBid.bidVideo, - Events: thisBid.bidEvents, - DealPriority: thisBid.dealPriority, - DealTierSatisfied: thisBid.dealTierSatisfied, - }, +func (e *exchange) makeBid(bids []*pbsOrtbBid, auc *auction, returnCreative bool) ([]openrtb.Bid, []error) { + result := make([]openrtb.Bid, 0, len(bids)) + errs := make([]error, 0, 1) + + for _, bid := range bids { + bidExtPrebid := &openrtb_ext.ExtBidPrebid{ + DealPriority: bid.dealPriority, + DealTierSatisfied: bid.dealTierSatisfied, + Events: bid.bidEvents, + Targeting: bid.bidTargets, + Type: bid.bidType, + Video: bid.bidVideo, } - if cacheInfo, found := e.getBidCacheInfo(thisBid, auc); found { - bidExt.Prebid.Cache = &openrtb_ext.ExtBidPrebidCache{ + + if cacheInfo, found := e.getBidCacheInfo(bid, auc); found { + bidExtPrebid.Cache = &openrtb_ext.ExtBidPrebidCache{ Bids: &cacheInfo, } } - ext, err := json.Marshal(bidExt) - if err != nil { - errList = append(errList, err) + + if bidExtJSON, err := makeBidExtJSON(bid.bid.Ext, bidExtPrebid); err != nil { + errs = append(errs, err) } else { - bids = append(bids, *thisBid.bid) - bids[len(bids)-1].Ext = ext + result = append(result, *bid.bid) + resultBid := &result[len(result)-1] + resultBid.Ext = bidExtJSON if !returnCreative { - bids[len(bids)-1].AdM = "" + resultBid.AdM = "" } } } - return bids, errList + return result, errs +} + +func makeBidExtJSON(ext json.RawMessage, prebid *openrtb_ext.ExtBidPrebid) (json.RawMessage, error) { + // no existing bid.ext. generate a bid.ext with just our prebid section populated. + if len(ext) == 0 { + bidExt := &openrtb_ext.ExtBid{Prebid: prebid} + return json.Marshal(bidExt) + } + + // update existing bid.ext with our prebid section. if bid.ext.prebid already exists, it will be overwritten. + var extMap map[string]interface{} + if err := json.Unmarshal(ext, &extMap); err != nil { + return nil, err + } + extMap[openrtb_ext.PrebidExtKey] = prebid + return json.Marshal(extMap) } // If bid got cached inside `(a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, targData *targetData, bidRequest *openrtb.BidRequest, ttlBuffer int64, defaultTTLs *config.DefaultTTLs, bidCategory map[string]string)`, diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 7507ecef89d..f615d412c71 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -1823,7 +1823,7 @@ func TestCategoryMapping(t *testing.T) { &bid1_4, } - seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil} + seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"} bidderName1 := openrtb_ext.BidderName("appnexus") adapterBids[bidderName1] = &seatBid @@ -1878,7 +1878,7 @@ func TestCategoryMappingNoIncludeBrandCategory(t *testing.T) { &bid1_4, } - seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil} + seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"} bidderName1 := openrtb_ext.BidderName("appnexus") adapterBids[bidderName1] = &seatBid @@ -1930,7 +1930,7 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) { &bid1_3, } - seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil} + seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"} bidderName1 := openrtb_ext.BidderName("appnexus") adapterBids[bidderName1] = &seatBid @@ -2012,7 +2012,7 @@ func TestCategoryMappingTranslateCategoriesFalse(t *testing.T) { &bid1_3, } - seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil} + seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"} bidderName1 := openrtb_ext.BidderName("appnexus") adapterBids[bidderName1] = &seatBid @@ -2082,7 +2082,7 @@ func TestCategoryDedupe(t *testing.T) { &bid1_5, } - seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil} + seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"} bidderName1 := openrtb_ext.BidderName("appnexus") adapterBids[bidderName1] = &seatBid @@ -2162,7 +2162,7 @@ func TestNoCategoryDedupe(t *testing.T) { &bid1_5, } - seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil} + seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"} bidderName1 := openrtb_ext.BidderName("appnexus") adapterBids[bidderName1] = &seatBid @@ -2223,10 +2223,10 @@ func TestCategoryMappingBidderName(t *testing.T) { &bid1_2, } - seatBid1 := pbsOrtbSeatBid{innerBids1, "USD", nil, nil} + seatBid1 := pbsOrtbSeatBid{bids: innerBids1, currency: "USD"} bidderName1 := openrtb_ext.BidderName("bidder1") - seatBid2 := pbsOrtbSeatBid{innerBids2, "USD", nil, nil} + seatBid2 := pbsOrtbSeatBid{bids: innerBids2, currency: "USD"} bidderName2 := openrtb_ext.BidderName("bidder2") adapterBids[bidderName1] = &seatBid1 @@ -2277,10 +2277,10 @@ func TestCategoryMappingBidderNameNoCategories(t *testing.T) { &bid1_2, } - seatBid1 := pbsOrtbSeatBid{innerBids1, "USD", nil, nil} + seatBid1 := pbsOrtbSeatBid{bids: innerBids1, currency: "USD"} bidderName1 := openrtb_ext.BidderName("bidder1") - seatBid2 := pbsOrtbSeatBid{innerBids2, "USD", nil, nil} + seatBid2 := pbsOrtbSeatBid{bids: innerBids2, currency: "USD"} bidderName2 := openrtb_ext.BidderName("bidder2") adapterBids[bidderName1] = &seatBid1 @@ -2384,7 +2384,7 @@ func TestBidRejectionErrors(t *testing.T) { innerBids = append(innerBids, ¤tBid) } - seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil} + seatBid := pbsOrtbSeatBid{bids: innerBids, currency: "USD"} adapterBids[bidderName] = &seatBid @@ -2442,10 +2442,10 @@ func TestCategoryMappingTwoBiddersOneBidEachNoCategorySamePrice(t *testing.T) { for i := 1; i < 10; i++ { adapterBids := make(map[openrtb_ext.BidderName]*pbsOrtbSeatBid) - seatBidApn1 := pbsOrtbSeatBid{innerBidsApn1, "USD", nil, nil} + seatBidApn1 := pbsOrtbSeatBid{bids: innerBidsApn1, currency: "USD"} bidderNameApn1 := openrtb_ext.BidderName("appnexus1") - seatBidApn2 := pbsOrtbSeatBid{innerBidsApn2, "USD", nil, nil} + seatBidApn2 := pbsOrtbSeatBid{bids: innerBidsApn2, currency: "USD"} bidderNameApn2 := openrtb_ext.BidderName("appnexus2") adapterBids[bidderNameApn1] = &seatBidApn1 diff --git a/exchange/exchangetest/bid-ext-prebid-collision.json b/exchange/exchangetest/bid-ext-prebid-collision.json new file mode 100644 index 00000000000..054671ce8d2 --- /dev/null +++ b/exchange/exchangetest/bid-ext-prebid-collision.json @@ -0,0 +1,90 @@ +{ + "description": "Verifies bid.ext values are left alone from the adapter, except for overwriting bid.ext.prebid if the adapter ext includes a collision.", + + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }] + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }] + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "pbsSeatBid": { + "pbsBids": [{ + "ortbBid": { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "ext": { + "someField": "someValue", + "prebid": { + "willBeOverwritten": "by core logic" + } + } + }, + "bidType": "video" + }] + } + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [{ + "seat": "appnexus", + "bid": [{ + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "ext": { + "someField": "someValue", + "prebid": { + "type": "video" + } + } + }] + }] + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/bid-ext.json b/exchange/exchangetest/bid-ext.json new file mode 100644 index 00000000000..8211ac88eac --- /dev/null +++ b/exchange/exchangetest/bid-ext.json @@ -0,0 +1,87 @@ +{ + "description": "Verifies bid.ext values are left alone from the adapter, except for adding in bid.ext.prebid.", + + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }] + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }] + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "pbsSeatBid": { + "pbsBids": [{ + "ortbBid": { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "ext": { + "someField": "someValue" + } + }, + "bidType": "video" + }] + } + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [{ + "seat": "appnexus", + "bid": [{ + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "ext": { + "someField": "someValue", + "prebid": { + "type": "video" + } + } + }] + }] + } + } +} \ No newline at end of file diff --git a/exchange/seatbid.go b/exchange/seatbid.go deleted file mode 100644 index b675127410e..00000000000 --- a/exchange/seatbid.go +++ /dev/null @@ -1,8 +0,0 @@ -package exchange - -import "encoding/json" - -// ExtSeatBid defines the contract for bidresponse.seatbid.ext -type ExtSeatBid struct { - Bidder json.RawMessage `json:"bidder,omitempty"` -} diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go index 8f4d7a094bb..7d787cf83a6 100644 --- a/openrtb_ext/bid.go +++ b/openrtb_ext/bid.go @@ -1,14 +1,12 @@ package openrtb_ext import ( - "encoding/json" "fmt" ) // ExtBid defines the contract for bidresponse.seatbid.bid[i].ext type ExtBid struct { - Prebid *ExtBidPrebid `json:"prebid,omitempty"` - Bidder json.RawMessage `json:"bidder,omitempty"` + Prebid *ExtBidPrebid `json:"prebid,omitempty"` } // ExtBidPrebid defines the contract for bidresponse.seatbid.bid[i].ext.prebid