Skip to content

Commit

Permalink
Support adapters to set bid seat prebid#2174
Browse files Browse the repository at this point in the history
  • Loading branch information
pm-nilesh-chate committed May 26, 2022
1 parent ce57f41 commit 8cd19b3
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 39 deletions.
2 changes: 2 additions & 0 deletions adapters/bidder.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,14 @@ func NewBidderResponse() *BidderResponse {
// TypedBid.BidType will become "response.seatbid[i].bid.ext.prebid.type" in the final OpenRTB response.
// TypedBid.BidVideo will become "response.seatbid[i].bid.ext.prebid.video" in the final OpenRTB response.
// TypedBid.DealPriority is optionally provided by adapters and used internally by the exchange to support deal targeted campaigns.
// TypedBid.Seat new seat under which the bid should pe placed. Default is adapter name
type TypedBid struct {
Bid *openrtb2.Bid
BidMeta *openrtb_ext.ExtBidPrebidMeta
BidType openrtb_ext.BidType
BidVideo *openrtb_ext.ExtBidPrebidVideo
DealPriority int
Seat openrtb_ext.BidderName
}

// RequestData and ResponseData exist so that prebid-server core code can implement its "debug" functionality
Expand Down
6 changes: 6 additions & 0 deletions adapters/pubmatic/pubmatic.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type PubmaticAdapter struct {
type pubmaticBidExt struct {
BidType *int `json:"BidType,omitempty"`
VideoCreativeInfo *pubmaticBidExtVideo `json:"video,omitempty"`
Marketplace string `json:"marketplace,omitempty"`
}

type pubmaticWrapperExt struct {
Expand Down Expand Up @@ -398,10 +399,15 @@ func (a *PubmaticAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa
}
bidType = getBidType(bidExt)
}
seat := ""
if bidExt.Marketplace != "" {
seat = bidExt.Marketplace
}
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: &bid,
BidType: bidType,
BidVideo: impVideo,
Seat: openrtb_ext.BidderName(seat),
})

}
Expand Down
55 changes: 44 additions & 11 deletions exchange/bidder.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type AdaptedBidder interface {
//
// Any errors will be user-facing in the API.
// Error messages should help publishers understand what might account for "bad" bids.
requestBid(ctx context.Context, bidderRequest BidderRequest, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed, headerDebugAllowed bool) (*pbsOrtbSeatBid, []error)
requestBid(ctx context.Context, bidderRequest BidderRequest, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed, headerDebugAllowed bool) ([]*pbsOrtbSeatBid, []error)
}

const ImpIdReqBody = "Stored bid response for impression id: "
Expand Down Expand Up @@ -93,6 +93,8 @@ 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
// seat defines whom these extra bids belong to.
seat string
}

// AdaptBidder converts an adapters.Bidder into an exchange.AdaptedBidder.
Expand Down Expand Up @@ -134,7 +136,7 @@ type bidderAdapterConfig struct {
DebugInfo config.DebugInfo
}

func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest BidderRequest, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed, headerDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest BidderRequest, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed, headerDebugAllowed bool) ([]*pbsOrtbSeatBid, []error) {

var reqData []*adapters.RequestData
var errs []error
Expand Down Expand Up @@ -193,10 +195,13 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde
}

defaultCurrency := "USD"
seatBid := &pbsOrtbSeatBid{
bids: make([]*pbsOrtbBid, 0, dataLen),
currency: defaultCurrency,
httpCalls: make([]*openrtb_ext.ExtHttpCall, 0, dataLen),
seatBidMap := map[openrtb_ext.BidderName]*pbsOrtbSeatBid{
bidderRequest.BidderName: {
bids: make([]*pbsOrtbBid, 0, dataLen),
currency: defaultCurrency,
httpCalls: make([]*openrtb_ext.ExtHttpCall, 0, dataLen),
seat: string(bidderRequest.BidderName),
},
}

// If the bidder made multiple requests, we still want them to enter as many bids as possible...
Expand All @@ -209,11 +214,11 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde
// - account debug is allowed
// - bidder debug is allowed
if headerDebugAllowed {
seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
seatBidMap[bidderRequest.BidderName].httpCalls = append(seatBidMap[bidderRequest.BidderName].httpCalls, makeExt(httpInfo))
} else {
if accountDebugAllowed {
if bidder.config.DebugInfo.Allow {
seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
seatBidMap[bidderRequest.BidderName].httpCalls = append(seatBidMap[bidderRequest.BidderName].httpCalls, makeExt(httpInfo))
} else {
debugDisabledWarning := errortypes.Warning{
WarningCode: errortypes.BidderLevelDebugDisabledWarningCode,
Expand Down Expand Up @@ -244,7 +249,7 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde
var err error
for _, bidReqCur := range bidderRequest.BidRequest.Cur {
if conversionRate, err = conversions.GetRate(bidResponse.Currency, bidReqCur); err == nil {
seatBid.currency = bidReqCur
seatBidMap[bidderRequest.BidderName].currency = bidReqCur
break
}
}
Expand Down Expand Up @@ -288,7 +293,29 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde
originalBidCpm = bidResponse.Bids[i].Bid.Price
bidResponse.Bids[i].Bid.Price = bidResponse.Bids[i].Bid.Price * bidAdjustment * conversionRate
}
seatBid.bids = append(seatBid.bids, &pbsOrtbBid{

if bidResponse.Bids[i].BidMeta == nil {
bidResponse.Bids[i].BidMeta = &openrtb_ext.ExtBidPrebidMeta{}
}
bidResponse.Bids[i].BidMeta.AdapterCode = bidderRequest.BidderName.String()

bidderName := bidderRequest.BidderName
if bidResponse.Bids[i].Seat != "" {
bidderName = bidResponse.Bids[i].Seat

if _, ok := seatBidMap[bidderName]; !ok {
// Initalize seatBidMap entry as this is first extra bid with seat bidderName
// seatBidMap[bidderName] =
seatBidMap[bidderName] = &pbsOrtbSeatBid{
bids: make([]*pbsOrtbBid, 0, dataLen),
currency: defaultCurrency,
// Do we need to fill httpCalls for this?. Can we refer one from adaptercode for debugging?
httpCalls: seatBidMap[bidderRequest.BidderName].httpCalls,
seat: bidderName.String(),
}
}
}
seatBidMap[bidderName].bids = append(seatBidMap[bidderName].bids, &pbsOrtbBid{
bid: bidResponse.Bids[i].Bid,
bidMeta: bidResponse.Bids[i].BidMeta,
bidType: bidResponse.Bids[i].BidType,
Expand All @@ -307,7 +334,13 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde
errs = append(errs, httpInfo.err)
}
}
return seatBid, errs

seatBids := make([]*pbsOrtbSeatBid, 0, len(seatBidMap))
for _, seatBid := range seatBidMap {
seatBids = append(seatBids, seatBid)
}

return seatBids, errs
}

func addNativeTypes(bid *openrtb2.Bid, request *openrtb2.BidRequest) (*nativeResponse.Response, []error) {
Expand Down
12 changes: 7 additions & 5 deletions exchange/bidder_validate_bids.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ type validatedBidder struct {
bidder AdaptedBidder
}

func (v *validatedBidder) requestBid(ctx context.Context, bidderRequest BidderRequest, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed, headerDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
seatBid, errs := v.bidder.requestBid(ctx, bidderRequest, bidAdjustment, conversions, reqInfo, accountDebugAllowed, headerDebugAllowed)
if validationErrors := removeInvalidBids(bidderRequest.BidRequest, seatBid); len(validationErrors) > 0 {
errs = append(errs, validationErrors...)
func (v *validatedBidder) requestBid(ctx context.Context, bidderRequest BidderRequest, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed, headerDebugAllowed bool) ([]*pbsOrtbSeatBid, []error) {
seatBids, errs := v.bidder.requestBid(ctx, bidderRequest, bidAdjustment, conversions, reqInfo, accountDebugAllowed, headerDebugAllowed)
for _, seatBid := range seatBids {
if validationErrors := removeInvalidBids(bidderRequest.BidRequest, seatBid); len(validationErrors) > 0 {
errs = append(errs, validationErrors...)
}
}
return seatBid, errs
return seatBids, errs
}

// validateBids will run some validation checks on the returned bids and excise any invalid bids
Expand Down
54 changes: 31 additions & 23 deletions exchange/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ type seatResponseExtra struct {
}

type bidResponseWrapper struct {
adapterBids *pbsOrtbSeatBid
adapterExtra *seatResponseExtra
bidder openrtb_ext.BidderName
adapterSeatBids []*pbsOrtbSeatBid
adapterExtra *seatResponseExtra
bidder openrtb_ext.BidderName
}

type BidIDGenerator interface {
Expand Down Expand Up @@ -517,31 +517,33 @@ func (e *exchange) getAllBids(
reqInfo.PbsEntryPoint = bidderRequest.BidderLabels.RType
reqInfo.GlobalPrivacyControlHeader = globalPrivacyControlHeader

bids, err := e.adapterMap[bidderRequest.BidderCoreName].requestBid(ctx, bidderRequest, adjustmentFactor, conversions, &reqInfo, accountDebugAllowed, headerDebugAllowed)
seatBids, err := e.adapterMap[bidderRequest.BidderCoreName].requestBid(ctx, bidderRequest, adjustmentFactor, conversions, &reqInfo, accountDebugAllowed, headerDebugAllowed)

// Add in time reporting
elapsed := time.Since(start)
brw.adapterBids = bids
brw.adapterSeatBids = seatBids
// Structure to record extra tracking data generated during bidding
ae := new(seatResponseExtra)
ae.ResponseTimeMillis = int(elapsed / time.Millisecond)
if bids != nil {
ae.HttpCalls = bids.httpCalls
if len(seatBids) != 0 {
ae.HttpCalls = seatBids[0].httpCalls
}

// Timing statistics
e.me.RecordAdapterTime(bidderRequest.BidderLabels, time.Since(start))
bidderRequest.BidderLabels.AdapterBids = bidsToMetric(brw.adapterBids)
bidderRequest.BidderLabels.AdapterBids = bidsToMetric(brw.adapterSeatBids)
bidderRequest.BidderLabels.AdapterErrors = errorsToMetric(err)
// Append any bid validation errors to the error list
ae.Errors = errsToBidderErrors(err)
ae.Warnings = errsToBidderWarnings(err)
brw.adapterExtra = ae
if bids != nil {
for _, bid := range bids.bids {
var cpm = float64(bid.bid.Price * 1000)
e.me.RecordAdapterPrice(bidderRequest.BidderLabels, cpm)
e.me.RecordAdapterBidReceived(bidderRequest.BidderLabels, bid.bidType, bid.bid.AdM != "")
for _, seatBid := range seatBids {
if seatBid != nil {
for _, bid := range seatBid.bids {
var cpm = float64(bid.bid.Price * 1000)
e.me.RecordAdapterPrice(bidderRequest.BidderLabels, cpm)
e.me.RecordAdapterBidReceived(bidderRequest.BidderLabels, bid.bidType, bid.bid.AdM != "")
}
}
}
chBids <- brw
Expand All @@ -553,8 +555,13 @@ func (e *exchange) getAllBids(
brw := <-chBids

//if bidder returned no bids back - remove bidder from further processing
if brw.adapterBids != nil && len(brw.adapterBids.bids) != 0 {
adapterBids[brw.bidder] = brw.adapterBids
for _, seatBid := range brw.adapterSeatBids {
if seatBid != nil && len(seatBid.bids) != 0 {
// Do we need to address duplicate names here?. What if different BidderNames have exta bids with same seat value.
// Ex. 'pubmatic' adapter has extra bids under new 'groupm' seat and 'appnexus' also comes with additional bids under seat 'groupm'
// Above example might be more relatable with 'allowUnknownBidderCodes'
adapterBids[openrtb_ext.BidderName(seatBid.seat)] = seatBid
}
}
//but we need to add all bidders data to adapterExtra to have metrics and other metadata
adapterExtra[brw.bidder] = brw.adapterExtra
Expand Down Expand Up @@ -598,8 +605,9 @@ func (e *exchange) recoverSafely(bidderRequests []BidderRequest,
}
}

func bidsToMetric(bids *pbsOrtbSeatBid) metrics.AdapterBid {
if bids == nil || len(bids.bids) == 0 {
func bidsToMetric(seatBids []*pbsOrtbSeatBid) metrics.AdapterBid {

if len(seatBids) == 0 || seatBids[0] == nil || len(seatBids[0].bids) == 0 {
return metrics.AdapterBidNone
}
return metrics.AdapterBidPresent
Expand Down Expand Up @@ -654,7 +662,7 @@ func errsToBidderWarnings(errs []error) []openrtb_ext.ExtBidderMessage {
}

// This piece takes all the bids supplied by the adapters and crafts an openRTB response to send back to the requester
func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, bidRequest *openrtb2.BidRequest, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, bidResponseExt *openrtb_ext.ExtBidResponse, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, errList []error) (*openrtb2.BidResponse, error) {
func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterSeatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, bidRequest *openrtb2.BidRequest, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, bidResponseExt *openrtb_ext.ExtBidResponse, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, errList []error) (*openrtb2.BidResponse, error) {
bidResponse := new(openrtb2.BidResponse)
var err error

Expand All @@ -667,12 +675,12 @@ func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_
// Create the SeatBids. We use a zero sized slice so that we can append non-zero seat bids, and not include seatBid
// objects for seatBids without any bids. Preallocate the max possible size to avoid reallocating the array as we go.
seatBids := make([]openrtb2.SeatBid, 0, len(liveAdapters))
for _, a := range liveAdapters {
for a, adapterSeatBids := range adapterSeatBids {
//while processing every single bib, do we need to handle categories here?
if adapterBids[a] != nil && len(adapterBids[a].bids) > 0 {
sb := e.makeSeatBid(adapterBids[a], a, adapterExtra, auc, returnCreative, impExtInfoMap)
if adapterSeatBids != nil && len(adapterSeatBids.bids) > 0 {
sb := e.makeSeatBid(adapterSeatBids, a, adapterExtra, auc, returnCreative, impExtInfoMap)
seatBids = append(seatBids, *sb)
bidResponse.Cur = adapterBids[a].currency
bidResponse.Cur = adapterSeatBids.currency
}
}

Expand Down Expand Up @@ -1180,7 +1188,7 @@ func buildStoredAuctionResponse(storedAuctionResponses map[string]json.RawMessag
} else {
//create new seat bid and add it to live adapters
liveAdapters = append(liveAdapters, bidderName)
newSeatBid := pbsOrtbSeatBid{bidsToAdd, "", nil}
newSeatBid := pbsOrtbSeatBid{bidsToAdd, "", nil, ""}
adapterBids[bidderName] = &newSeatBid

}
Expand Down
1 change: 1 addition & 0 deletions openrtb_ext/bid.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type ExtBidPrebidMeta struct {
NetworkName string `json:"networkName,omitempty"`
PrimaryCategoryID string `json:"primaryCatId,omitempty"`
SecondaryCategoryIDs []string `json:"secondaryCatIds,omitempty"`
AdapterCode string `json:"adaptercode"`
}

// ExtBidPrebidVideo defines the contract for bidresponse.seatbid.bid[i].ext.prebid.video
Expand Down

0 comments on commit 8cd19b3

Please sign in to comment.