github.com/prebid/prebid-server@v0.275.0/adapters/rubicon/rubicon.go (about)

     1  package rubicon
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"net/url"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/prebid/prebid-server/adapters"
    12  	"github.com/prebid/prebid-server/config"
    13  	"github.com/prebid/prebid-server/errortypes"
    14  	"github.com/prebid/prebid-server/openrtb_ext"
    15  	"github.com/prebid/prebid-server/util/maputil"
    16  
    17  	"github.com/buger/jsonparser"
    18  	"github.com/prebid/openrtb/v19/adcom1"
    19  	"github.com/prebid/openrtb/v19/openrtb2"
    20  )
    21  
    22  const badvLimitSize = 50
    23  
    24  var bannerExtContent = []byte(`{"rp":{"mime":"text/html"}}`)
    25  
    26  type RubiconAdapter struct {
    27  	URI          string
    28  	XAPIUsername string
    29  	XAPIPassword string
    30  }
    31  
    32  type rubiconContext struct {
    33  	Data json.RawMessage `json:"data"`
    34  }
    35  
    36  type rubiconData struct {
    37  	AdServer rubiconAdServer `json:"adserver"`
    38  	PbAdSlot string          `json:"pbadslot"`
    39  }
    40  
    41  type rubiconAdServer struct {
    42  	Name   string `json:"name"`
    43  	AdSlot string `json:"adslot"`
    44  }
    45  
    46  type rubiconExtImpBidder struct {
    47  	Prebid  *openrtb_ext.ExtImpPrebid `json:"prebid"`
    48  	Bidder  openrtb_ext.ExtImpRubicon `json:"bidder"`
    49  	Gpid    string                    `json:"gpid"`
    50  	Skadn   json.RawMessage           `json:"skadn,omitempty"`
    51  	Data    json.RawMessage           `json:"data"`
    52  	Context rubiconContext            `json:"context"`
    53  }
    54  
    55  type bidRequestExt struct {
    56  	Prebid bidRequestExtPrebid `json:"prebid"`
    57  }
    58  
    59  type bidRequestExtPrebid struct {
    60  	Bidders bidRequestExtPrebidBidders `json:"bidders"`
    61  }
    62  
    63  type bidRequestExtPrebidBidders struct {
    64  	Rubicon prebidBiddersRubicon `json:"rubicon,omitempty"`
    65  }
    66  
    67  type prebidBiddersRubicon struct {
    68  	Debug prebidBiddersRubiconDebug `json:"debug,omitempty"`
    69  }
    70  
    71  type prebidBiddersRubiconDebug struct {
    72  	CpmOverride float64 `json:"cpmoverride,omitempty"`
    73  }
    74  
    75  type rubiconImpExtRPTrack struct {
    76  	Mint        string `json:"mint"`
    77  	MintVersion string `json:"mint_version"`
    78  }
    79  
    80  type rubiconImpExt struct {
    81  	RP    rubiconImpExtRP `json:"rp,omitempty"`
    82  	GPID  string          `json:"gpid,omitempty"`
    83  	Skadn json.RawMessage `json:"skadn,omitempty"`
    84  }
    85  
    86  type rubiconImpExtRP struct {
    87  	ZoneID int                  `json:"zone_id"`
    88  	Target json.RawMessage      `json:"target,omitempty"`
    89  	Track  rubiconImpExtRPTrack `json:"track"`
    90  }
    91  
    92  type rubiconUserExtRP struct {
    93  	Target json.RawMessage `json:"target,omitempty"`
    94  }
    95  
    96  type rubiconDataExt struct {
    97  	SegTax int `json:"segtax"`
    98  }
    99  
   100  type rubiconUserExt struct {
   101  	Eids        []openrtb2.EID   `json:"eids,omitempty"`
   102  	RP          rubiconUserExtRP `json:"rp"`
   103  	LiverampIdl string           `json:"liveramp_idl,omitempty"`
   104  	Data        json.RawMessage  `json:"data,omitempty"`
   105  	Consent     string           `json:"consent,omitempty"`
   106  }
   107  
   108  type rubiconSiteExtRP struct {
   109  	SiteID int             `json:"site_id"`
   110  	Target json.RawMessage `json:"target,omitempty"`
   111  }
   112  
   113  type rubiconSiteExt struct {
   114  	RP rubiconSiteExtRP `json:"rp"`
   115  }
   116  
   117  type rubiconPubExtRP struct {
   118  	AccountID int `json:"account_id"`
   119  }
   120  
   121  type rubiconPubExt struct {
   122  	RP rubiconPubExtRP `json:"rp"`
   123  }
   124  
   125  type rubiconBannerExtRP struct {
   126  	MIME string `json:"mime"`
   127  }
   128  
   129  type rubiconBannerExt struct {
   130  	RP rubiconBannerExtRP `json:"rp"`
   131  }
   132  
   133  // ***** Video Extension *****
   134  type rubiconVideoExt struct {
   135  	Skip      int               `json:"skip,omitempty"`
   136  	SkipDelay int               `json:"skipdelay,omitempty"`
   137  	VideoType string            `json:"videotype,omitempty"`
   138  	RP        rubiconVideoExtRP `json:"rp"`
   139  }
   140  
   141  type rubiconVideoExtRP struct {
   142  	SizeID int `json:"size_id,omitempty"`
   143  }
   144  
   145  type rubiconDeviceExtRP struct {
   146  	PixelRatio float64 `json:"pixelratio"`
   147  }
   148  
   149  type rubiconDeviceExt struct {
   150  	RP rubiconDeviceExtRP `json:"rp"`
   151  }
   152  
   153  type rubiconBidResponse struct {
   154  	openrtb2.BidResponse
   155  	SeatBid []rubiconSeatBid `json:"seatbid,omitempty"`
   156  }
   157  
   158  type rubiconSeatBid struct {
   159  	openrtb2.SeatBid
   160  	Buyer string       `json:"buyer,omitempty"`
   161  	Bid   []rubiconBid `json:"bid"`
   162  }
   163  
   164  type rubiconBid struct {
   165  	openrtb2.Bid
   166  	AdmNative json.RawMessage `json:"adm_native,omitempty"`
   167  }
   168  
   169  type extPrebid struct {
   170  	Prebid *openrtb_ext.ExtBidPrebid `json:"prebid,omitempty"`
   171  	Bidder json.RawMessage           `json:"bidder,omitempty"`
   172  }
   173  
   174  // defines the contract for bidrequest.user.ext.eids[i].ext
   175  type rubiconUserExtEidExt struct {
   176  	Segments []string `json:"segments,omitempty"`
   177  }
   178  
   179  type mappedRubiconUidsParam struct {
   180  	segments    []string
   181  	liverampIdl string
   182  }
   183  
   184  func resolveVideoSizeId(placement adcom1.VideoPlacementSubtype, instl int8, impId string) (sizeID int, err error) {
   185  	if placement != 0 {
   186  		if placement == 1 {
   187  			return 201, nil
   188  		}
   189  		if placement == 3 {
   190  			return 203, nil
   191  		}
   192  	}
   193  
   194  	if instl == 1 {
   195  		return 202, nil
   196  	}
   197  	return 0, &errortypes.BadInput{
   198  		Message: fmt.Sprintf("video.size_id can not be resolved in impression with id : %s", impId),
   199  	}
   200  }
   201  
   202  func appendTrackerToUrl(uri string, tracker string) (res string) {
   203  	// Append integration method. Adapter init happens once
   204  	urlObject, err := url.Parse(uri)
   205  	// No other exception throwing mechanism in this stack, so ignoring parse errors.
   206  	if err == nil {
   207  		values := urlObject.Query()
   208  		values.Add("tk_xint", tracker)
   209  		urlObject.RawQuery = values.Encode()
   210  		res = urlObject.String()
   211  	} else {
   212  		res = uri
   213  	}
   214  	return
   215  }
   216  
   217  // Builder builds a new instance of the Rubicon adapter for the given bidder with the given config.
   218  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
   219  	uri := appendTrackerToUrl(config.Endpoint, config.XAPI.Tracker)
   220  
   221  	bidder := &RubiconAdapter{
   222  		URI:          uri,
   223  		XAPIUsername: config.XAPI.Username,
   224  		XAPIPassword: config.XAPI.Password,
   225  	}
   226  	return bidder, nil
   227  }
   228  
   229  func updateRequestTo26(r *openrtb2.BidRequest) error {
   230  	if r.Regs != nil {
   231  		regsCopy := *r.Regs
   232  		r.Regs = &regsCopy
   233  	}
   234  
   235  	if r.Source != nil {
   236  		sourceCopy := *r.Source
   237  		r.Source = &sourceCopy
   238  	}
   239  
   240  	if r.User != nil {
   241  		userCopy := *r.User
   242  		r.User = &userCopy
   243  	}
   244  
   245  	requestWrapper := &openrtb_ext.RequestWrapper{BidRequest: r}
   246  
   247  	if err := openrtb_ext.ConvertUpTo26(requestWrapper); err != nil {
   248  		return err
   249  	}
   250  
   251  	return requestWrapper.RebuildRequest()
   252  }
   253  
   254  func (a *RubiconAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
   255  
   256  	err := updateRequestTo26(request)
   257  
   258  	if err != nil {
   259  		return nil, []error{err}
   260  	}
   261  
   262  	numRequests := len(request.Imp)
   263  	requestData := make([]*adapters.RequestData, 0, numRequests)
   264  	headers := http.Header{}
   265  	headers.Add("Content-Type", "application/json;charset=utf-8")
   266  	headers.Add("Accept", "application/json")
   267  	headers.Add("User-Agent", "prebid-server/1.0")
   268  
   269  	impsToExtNotGrouped, errs := createImpsToExtMap(request.Imp)
   270  	impsToExtMap := prepareImpsToExtMap(impsToExtNotGrouped)
   271  
   272  	rubiconRequest := *request
   273  	for imp, bidderExt := range impsToExtMap {
   274  		rubiconExt := bidderExt.Bidder
   275  		target, err := updateImpRpTargetWithFpdAttributes(bidderExt, rubiconExt, *imp, request.Site, request.App)
   276  		if err != nil {
   277  			errs = append(errs, err)
   278  			continue
   279  		}
   280  
   281  		siteId, err := rubiconExt.SiteId.Int64()
   282  		if err != nil {
   283  			errs = append(errs, err)
   284  			continue
   285  		}
   286  
   287  		zoneId, err := rubiconExt.ZoneId.Int64()
   288  		if err != nil {
   289  			errs = append(errs, err)
   290  			continue
   291  		}
   292  
   293  		impExt := rubiconImpExt{
   294  			RP: rubiconImpExtRP{
   295  				ZoneID: int(zoneId),
   296  				Target: target,
   297  				Track:  rubiconImpExtRPTrack{Mint: "", MintVersion: ""},
   298  			},
   299  			GPID:  bidderExt.Gpid,
   300  			Skadn: bidderExt.Skadn,
   301  		}
   302  
   303  		imp.Ext, err = json.Marshal(&impExt)
   304  		if err != nil {
   305  			errs = append(errs, err)
   306  			continue
   307  		}
   308  
   309  		secure := int8(1)
   310  		imp.Secure = &secure
   311  
   312  		resolvedBidFloor, err := resolveBidFloor(imp.BidFloor, imp.BidFloorCur, reqInfo)
   313  		if err != nil {
   314  			errs = append(errs, &errortypes.BadInput{
   315  				Message: fmt.Sprintf("Unable to convert provided bid floor currency from %s to USD",
   316  					imp.BidFloorCur),
   317  			})
   318  			continue
   319  		}
   320  
   321  		if resolvedBidFloor > 0 {
   322  			imp.BidFloorCur = "USD"
   323  			imp.BidFloor = resolvedBidFloor
   324  		}
   325  
   326  		if request.User != nil {
   327  			userCopy := *request.User
   328  			target, err := updateUserRpTargetWithFpdAttributes(rubiconExt.Visitor, userCopy)
   329  			if err != nil {
   330  				errs = append(errs, err)
   331  				continue
   332  			}
   333  
   334  			userExtRP := rubiconUserExt{RP: rubiconUserExtRP{Target: target}}
   335  			userBuyerUID := userCopy.BuyerUID
   336  
   337  			if len(userCopy.EIDs) > 0 {
   338  				userExtRP.Eids = userCopy.EIDs
   339  
   340  				if userBuyerUID == "" {
   341  					userBuyerUID = extractUserBuyerUID(userExtRP.Eids)
   342  				}
   343  
   344  				mappedRubiconUidsParam, errors := getSegments(userExtRP.Eids)
   345  				if len(errors) > 0 {
   346  					errs = append(errs, errors...)
   347  					continue
   348  				}
   349  
   350  				if err := updateUserExtWithSegments(&userExtRP, mappedRubiconUidsParam); err != nil {
   351  					errs = append(errs, err)
   352  					continue
   353  				}
   354  
   355  				userExtRP.LiverampIdl = mappedRubiconUidsParam.liverampIdl
   356  			}
   357  
   358  			if userCopy.Consent != "" {
   359  				userExtRP.Consent = userCopy.Consent
   360  				userCopy.Consent = ""
   361  			}
   362  
   363  			userCopy.Ext, err = json.Marshal(&userExtRP)
   364  			if err != nil {
   365  				errs = append(errs, err)
   366  				continue
   367  			}
   368  			userCopy.Geo = nil
   369  			userCopy.Yob = 0
   370  			userCopy.Gender = ""
   371  			userCopy.BuyerUID = userBuyerUID
   372  			userCopy.EIDs = nil
   373  
   374  			rubiconRequest.User = &userCopy
   375  		}
   376  
   377  		if request.Device != nil {
   378  			deviceCopy := *request.Device
   379  			deviceExt := rubiconDeviceExt{RP: rubiconDeviceExtRP{PixelRatio: request.Device.PxRatio}}
   380  			deviceCopy.Ext, err = json.Marshal(&deviceExt)
   381  			rubiconRequest.Device = &deviceCopy
   382  		}
   383  
   384  		isVideo := isVideo(*imp)
   385  		impType := openrtb_ext.BidTypeVideo
   386  		requestNative := make(map[string]interface{})
   387  		if isVideo {
   388  			videoCopy := *imp.Video
   389  
   390  			videoSizeId := rubiconExt.Video.VideoSizeID
   391  			if videoSizeId == 0 {
   392  				resolvedSizeId, err := resolveVideoSizeId(imp.Video.Placement, imp.Instl, imp.ID)
   393  				if err != nil {
   394  					errs = append(errs, err)
   395  					continue
   396  				}
   397  				videoSizeId = resolvedSizeId
   398  			}
   399  
   400  			// if imp.rwdd = 1, set imp.video.ext.videotype = "rewarded"
   401  			var videoType = ""
   402  			if imp.Rwdd == 1 {
   403  				videoType = "rewarded"
   404  				imp.Rwdd = 0
   405  			}
   406  			videoExt := rubiconVideoExt{Skip: rubiconExt.Video.Skip, SkipDelay: rubiconExt.Video.SkipDelay, VideoType: videoType, RP: rubiconVideoExtRP{SizeID: videoSizeId}}
   407  			videoCopy.Ext, err = json.Marshal(&videoExt)
   408  			imp.Video = &videoCopy
   409  			imp.Banner = nil
   410  			imp.Native = nil
   411  		} else if imp.Banner != nil {
   412  			bannerCopy := *imp.Banner
   413  			if len(bannerCopy.Format) < 1 && (bannerCopy.W == nil || *bannerCopy.W == 0 && bannerCopy.H == nil || *bannerCopy.H == 0) {
   414  				errs = append(errs, &errortypes.BadInput{
   415  					Message: "rubicon imps must have at least one imp.format element",
   416  				})
   417  				continue
   418  			}
   419  			bannerCopy.Ext = bannerExtContent
   420  			if err != nil {
   421  				errs = append(errs, err)
   422  				continue
   423  			}
   424  			imp.Banner = &bannerCopy
   425  			imp.Video = nil
   426  			imp.Native = nil
   427  			impType = openrtb_ext.BidTypeBanner
   428  		} else {
   429  			native, err := resolveNativeObject(imp.Native, requestNative)
   430  			if err != nil {
   431  				errs = append(errs, err)
   432  				continue
   433  			}
   434  			imp.Native = native
   435  			imp.Video = nil
   436  			impType = openrtb_ext.BidTypeNative
   437  		}
   438  
   439  		accountId, err := rubiconExt.AccountId.Int64()
   440  		if err != nil {
   441  			errs = append(errs, err)
   442  			continue
   443  		}
   444  
   445  		pubExt := rubiconPubExt{RP: rubiconPubExtRP{AccountID: int(accountId)}}
   446  
   447  		if request.Site != nil {
   448  			siteCopy := *request.Site
   449  			siteExtRP := rubiconSiteExt{RP: rubiconSiteExtRP{SiteID: int(siteId)}}
   450  			if siteCopy.Content != nil {
   451  				siteTarget := make(map[string]interface{})
   452  				updateExtWithIabAttribute(siteTarget, siteCopy.Content.Data, []int{1, 2, 5, 6})
   453  				if len(siteTarget) > 0 {
   454  					updatedSiteTarget, err := json.Marshal(siteTarget)
   455  					if err != nil {
   456  						errs = append(errs, err)
   457  						continue
   458  					}
   459  					siteExtRP.RP.Target = updatedSiteTarget
   460  				}
   461  			}
   462  
   463  			siteCopy.Ext, err = json.Marshal(&siteExtRP)
   464  			if err != nil {
   465  				errs = append(errs, err)
   466  				continue
   467  			}
   468  
   469  			siteCopy.Publisher = &openrtb2.Publisher{}
   470  			siteCopy.Publisher.Ext, err = json.Marshal(&pubExt)
   471  			rubiconRequest.Site = &siteCopy
   472  		} else {
   473  			appCopy := *request.App
   474  			appCopy.Ext, err = json.Marshal(rubiconSiteExt{RP: rubiconSiteExtRP{SiteID: int(siteId)}})
   475  			appCopy.Publisher = &openrtb2.Publisher{}
   476  			appCopy.Publisher.Ext, err = json.Marshal(&pubExt)
   477  			rubiconRequest.App = &appCopy
   478  		}
   479  
   480  		if request.Source != nil || rubiconExt.PChain != "" {
   481  			var sourceCopy openrtb2.Source
   482  			if request.Source != nil {
   483  				sourceCopy = *request.Source
   484  			} else {
   485  				sourceCopy = openrtb2.Source{}
   486  			}
   487  
   488  			if sourceCopy.SChain != nil {
   489  				var sourceCopyExt openrtb_ext.ExtSource
   490  				if sourceCopy.Ext != nil {
   491  					if err = json.Unmarshal(sourceCopy.Ext, &sourceCopyExt); err != nil {
   492  						errs = append(errs, &errortypes.BadInput{Message: err.Error()})
   493  						continue
   494  					}
   495  				} else {
   496  					sourceCopyExt = openrtb_ext.ExtSource{}
   497  				}
   498  
   499  				sourceCopyExt.SChain = sourceCopy.SChain
   500  				sourceCopy.SChain = nil
   501  
   502  				sourceCopy.Ext, err = json.Marshal(&sourceCopyExt)
   503  				if err != nil {
   504  					errs = append(errs, err)
   505  					continue
   506  				}
   507  			}
   508  
   509  			if rubiconExt.PChain != "" {
   510  				sourceCopy.PChain = rubiconExt.PChain
   511  			}
   512  
   513  			rubiconRequest.Source = &sourceCopy
   514  		}
   515  
   516  		if request.Regs != nil && (request.Regs.GDPR != nil || request.Regs.USPrivacy != "") {
   517  			regsCopy := *request.Regs
   518  
   519  			var regsCopyExt openrtb_ext.ExtRegs
   520  			if regsCopy.Ext != nil {
   521  				if err = json.Unmarshal(regsCopy.Ext, &regsCopyExt); err != nil {
   522  					errs = append(errs, &errortypes.BadInput{Message: err.Error()})
   523  					continue
   524  				}
   525  			} else {
   526  				regsCopyExt = openrtb_ext.ExtRegs{}
   527  			}
   528  
   529  			if regsCopy.GDPR != nil {
   530  				regsCopyExt.GDPR = regsCopy.GDPR
   531  			}
   532  			if regsCopy.USPrivacy != "" {
   533  				regsCopyExt.USPrivacy = regsCopy.USPrivacy
   534  			}
   535  
   536  			regsCopy.Ext, err = json.Marshal(&regsCopyExt)
   537  			if err != nil {
   538  				errs = append(errs, err)
   539  				continue
   540  			}
   541  			regsCopy.GDPR = nil
   542  			regsCopy.USPrivacy = ""
   543  
   544  			rubiconRequest.Regs = &regsCopy
   545  		}
   546  
   547  		reqBadv := request.BAdv
   548  		if reqBadv != nil {
   549  			if len(reqBadv) > badvLimitSize {
   550  				rubiconRequest.BAdv = reqBadv[:badvLimitSize]
   551  			}
   552  		}
   553  
   554  		rubiconRequest.Imp = []openrtb2.Imp{*imp}
   555  		rubiconRequest.Cur = nil
   556  		rubiconRequest.Ext = nil
   557  
   558  		reqJSON, err := json.Marshal(rubiconRequest)
   559  		if impType == openrtb_ext.BidTypeNative && len(requestNative) > 0 {
   560  			reqJSON, err = setImpNative(reqJSON, requestNative)
   561  		}
   562  
   563  		if err != nil {
   564  			errs = append(errs, err)
   565  			continue
   566  		}
   567  
   568  		reqData := &adapters.RequestData{
   569  			Method:  "POST",
   570  			Uri:     a.URI,
   571  			Body:    reqJSON,
   572  			Headers: headers,
   573  		}
   574  		reqData.SetBasicAuth(a.XAPIUsername, a.XAPIPassword)
   575  		requestData = append(requestData, reqData)
   576  	}
   577  
   578  	return requestData, errs
   579  }
   580  
   581  func createImpsToExtMap(imps []openrtb2.Imp) (map[*openrtb2.Imp]rubiconExtImpBidder, []error) {
   582  	impsToExtMap := make(map[*openrtb2.Imp]rubiconExtImpBidder)
   583  	errs := make([]error, 0)
   584  	var err error
   585  	for _, imp := range imps {
   586  		impCopy := imp
   587  		var bidderExt rubiconExtImpBidder
   588  		if err = json.Unmarshal(imp.Ext, &bidderExt); err != nil {
   589  			errs = append(errs, &errortypes.BadInput{
   590  				Message: err.Error(),
   591  			})
   592  			continue
   593  		}
   594  		impsToExtMap[&impCopy] = bidderExt
   595  	}
   596  
   597  	return impsToExtMap, errs
   598  }
   599  
   600  func prepareImpsToExtMap(impsToExtMap map[*openrtb2.Imp]rubiconExtImpBidder) map[*openrtb2.Imp]rubiconExtImpBidder {
   601  	preparedImpsToExtMap := make(map[*openrtb2.Imp]rubiconExtImpBidder)
   602  	for imp, bidderExt := range impsToExtMap {
   603  		if bidderExt.Bidder.BidOnMultiformat == false {
   604  			impCopy := imp
   605  			preparedImpsToExtMap[impCopy] = bidderExt
   606  			continue
   607  		}
   608  
   609  		splitImps := splitMultiFormatImp(imp)
   610  		for _, imp := range splitImps {
   611  			impCopy := imp
   612  			preparedImpsToExtMap[impCopy] = bidderExt
   613  		}
   614  	}
   615  
   616  	return preparedImpsToExtMap
   617  }
   618  
   619  func splitMultiFormatImp(imp *openrtb2.Imp) []*openrtb2.Imp {
   620  	splitImps := make([]*openrtb2.Imp, 0)
   621  	if imp.Banner != nil {
   622  		impCopy := *imp
   623  		impCopy.Video = nil
   624  		impCopy.Native = nil
   625  		impCopy.Audio = nil
   626  		splitImps = append(splitImps, &impCopy)
   627  	}
   628  
   629  	if imp.Video != nil {
   630  		impCopy := *imp
   631  		impCopy.Banner = nil
   632  		impCopy.Native = nil
   633  		impCopy.Audio = nil
   634  		splitImps = append(splitImps, &impCopy)
   635  	}
   636  
   637  	if imp.Native != nil {
   638  		impCopy := *imp
   639  		impCopy.Banner = nil
   640  		impCopy.Video = nil
   641  		impCopy.Audio = nil
   642  		splitImps = append(splitImps, &impCopy)
   643  	}
   644  
   645  	if imp.Audio != nil {
   646  		impCopy := *imp
   647  		impCopy.Banner = nil
   648  		impCopy.Video = nil
   649  		impCopy.Native = nil
   650  		splitImps = append(splitImps, &impCopy)
   651  	}
   652  
   653  	return splitImps
   654  }
   655  
   656  func resolveBidFloor(bidFloor float64, bidFloorCur string, reqInfo *adapters.ExtraRequestInfo) (float64, error) {
   657  	if bidFloor > 0 && bidFloorCur != "" && strings.ToUpper(bidFloorCur) != "USD" {
   658  		return reqInfo.ConvertCurrency(bidFloor, bidFloorCur, "USD")
   659  	}
   660  
   661  	return bidFloor, nil
   662  }
   663  
   664  func updateImpRpTargetWithFpdAttributes(extImp rubiconExtImpBidder, extImpRubicon openrtb_ext.ExtImpRubicon,
   665  	imp openrtb2.Imp, site *openrtb2.Site, app *openrtb2.App) (json.RawMessage, error) {
   666  
   667  	existingTarget, _, _, err := jsonparser.Get(imp.Ext, "rp", "target")
   668  	if isNotKeyPathError(err) {
   669  		return nil, err
   670  	}
   671  	target, err := rawJSONToMap(existingTarget)
   672  	if err != nil {
   673  		return nil, err
   674  	}
   675  	err = populateFirstPartyDataAttributes(extImpRubicon.Inventory, target)
   676  	if err != nil {
   677  		return nil, err
   678  	}
   679  
   680  	if site != nil {
   681  		siteExtData, _, _, err := jsonparser.Get(site.Ext, "data")
   682  		if isNotKeyPathError(err) {
   683  			return nil, err
   684  		}
   685  		err = populateFirstPartyDataAttributes(siteExtData, target)
   686  		if err != nil {
   687  			return nil, err
   688  		}
   689  		if len(site.SectionCat) > 0 {
   690  			addStringArrayAttribute(site.SectionCat, target, "sectioncat")
   691  		}
   692  		if len(site.PageCat) > 0 {
   693  			addStringArrayAttribute(site.PageCat, target, "pagecat")
   694  		}
   695  		if site.Page != "" {
   696  			addStringAttribute(site.Page, target, "page")
   697  		}
   698  		if site.Ref != "" {
   699  			addStringAttribute(site.Ref, target, "ref")
   700  		}
   701  		if site.Search != "" {
   702  			addStringAttribute(site.Search, target, "search")
   703  		}
   704  	} else {
   705  		appExtData, _, _, err := jsonparser.Get(app.Ext, "data")
   706  		if isNotKeyPathError(err) {
   707  			return nil, err
   708  		}
   709  		err = populateFirstPartyDataAttributes(appExtData, target)
   710  		if err != nil {
   711  			return nil, err
   712  		}
   713  		if len(app.SectionCat) > 0 {
   714  			addStringArrayAttribute(app.SectionCat, target, "sectioncat")
   715  		}
   716  		if len(app.PageCat) > 0 {
   717  			addStringArrayAttribute(app.PageCat, target, "pagecat")
   718  		}
   719  	}
   720  
   721  	if len(extImp.Context.Data) > 0 {
   722  		err = populateFirstPartyDataAttributes(extImp.Context.Data, target)
   723  	} else if len(extImp.Data) > 0 {
   724  		err = populateFirstPartyDataAttributes(extImp.Data, target)
   725  	}
   726  	if isNotKeyPathError(err) {
   727  		return nil, err
   728  	}
   729  
   730  	var data rubiconData
   731  	if len(extImp.Data) > 0 {
   732  		err := json.Unmarshal(extImp.Data, &data)
   733  		if err != nil {
   734  			return nil, err
   735  		}
   736  	}
   737  	var contextData rubiconData
   738  	if len(extImp.Context.Data) > 0 {
   739  		err := json.Unmarshal(extImp.Context.Data, &contextData)
   740  		if err != nil {
   741  			return nil, err
   742  		}
   743  	}
   744  
   745  	if data.PbAdSlot != "" {
   746  		target["pbadslot"] = data.PbAdSlot
   747  	} else {
   748  		dfpAdUnitCode := extractDfpAdUnitCode(data, contextData)
   749  		if dfpAdUnitCode != "" {
   750  			target["dfp_ad_unit_code"] = dfpAdUnitCode
   751  		}
   752  	}
   753  
   754  	if len(extImpRubicon.Keywords) > 0 {
   755  		addStringArrayAttribute(extImpRubicon.Keywords, target, "keywords")
   756  	}
   757  	updatedTarget, err := json.Marshal(target)
   758  	if err != nil {
   759  		return nil, err
   760  	}
   761  	return updatedTarget, nil
   762  }
   763  
   764  func extractDfpAdUnitCode(data rubiconData, contextData rubiconData) string {
   765  	if contextData.AdServer.Name == "gam" && contextData.AdServer.AdSlot != "" {
   766  		return contextData.AdServer.AdSlot
   767  	} else if data.AdServer.Name == "gam" && data.AdServer.AdSlot != "" {
   768  		return data.AdServer.AdSlot
   769  	}
   770  
   771  	return ""
   772  }
   773  
   774  func isNotKeyPathError(err error) bool {
   775  	return err != nil && err != jsonparser.KeyPathNotFoundError
   776  }
   777  
   778  func addStringAttribute(attribute string, target map[string]interface{}, attributeName string) {
   779  	target[attributeName] = [1]string{attribute}
   780  }
   781  
   782  func addStringArrayAttribute(attribute []string, target map[string]interface{}, attributeName string) {
   783  	target[attributeName] = attribute
   784  }
   785  
   786  func updateUserRpTargetWithFpdAttributes(visitor json.RawMessage, user openrtb2.User) (json.RawMessage, error) {
   787  	existingTarget, _, _, err := jsonparser.Get(user.Ext, "rp", "target")
   788  	if isNotKeyPathError(err) {
   789  		return nil, err
   790  	}
   791  	target, err := rawJSONToMap(existingTarget)
   792  	if err != nil {
   793  		return nil, err
   794  	}
   795  	err = populateFirstPartyDataAttributes(visitor, target)
   796  	if err != nil {
   797  		return nil, err
   798  	}
   799  	userExtData, _, _, err := jsonparser.Get(user.Ext, "data")
   800  	if isNotKeyPathError(err) {
   801  		return nil, err
   802  	}
   803  	err = populateFirstPartyDataAttributes(userExtData, target)
   804  	if err != nil {
   805  		return nil, err
   806  	}
   807  	updateExtWithIabAttribute(target, user.Data, []int{4})
   808  
   809  	updatedTarget, err := json.Marshal(target)
   810  	if err != nil {
   811  		return nil, err
   812  	}
   813  	return updatedTarget, nil
   814  }
   815  
   816  func updateExtWithIabAttribute(target map[string]interface{}, data []openrtb2.Data, segTaxes []int) {
   817  	var segmentIdsToCopy = getSegmentIdsToCopy(data, segTaxes)
   818  	if len(segmentIdsToCopy) == 0 {
   819  		return
   820  	}
   821  
   822  	target["iab"] = segmentIdsToCopy
   823  }
   824  
   825  func populateFirstPartyDataAttributes(source json.RawMessage, target map[string]interface{}) error {
   826  	sourceAsMap, err := rawJSONToMap(source)
   827  	if err != nil {
   828  		return err
   829  	}
   830  
   831  	for key, val := range sourceAsMap {
   832  		switch typedValue := val.(type) {
   833  		case string:
   834  			target[key] = [1]string{typedValue}
   835  		case float64:
   836  			if typedValue == float64(int(typedValue)) {
   837  				target[key] = [1]string{strconv.Itoa(int(typedValue))}
   838  			}
   839  		case bool:
   840  			target[key] = [1]string{strconv.FormatBool(typedValue)}
   841  		case []interface{}:
   842  			if isStringArray(typedValue) {
   843  				target[key] = typedValue
   844  			}
   845  			if isBoolArray(typedValue) {
   846  				target[key] = convertToStringArray(typedValue)
   847  			}
   848  		}
   849  	}
   850  	return nil
   851  }
   852  
   853  func isStringArray(array []interface{}) bool {
   854  	for _, val := range array {
   855  		if _, ok := val.(string); !ok {
   856  			return false
   857  		}
   858  	}
   859  
   860  	return true
   861  }
   862  
   863  func isBoolArray(array []interface{}) bool {
   864  	for _, val := range array {
   865  		if _, ok := val.(bool); !ok {
   866  			return false
   867  		}
   868  	}
   869  
   870  	return true
   871  }
   872  
   873  func convertToStringArray(arr []interface{}) []string {
   874  	var stringArray []string
   875  	for _, val := range arr {
   876  		if boolVal, ok := val.(bool); ok {
   877  			stringArray = append(stringArray, strconv.FormatBool(boolVal))
   878  		}
   879  	}
   880  
   881  	return stringArray
   882  }
   883  
   884  func rawJSONToMap(message json.RawMessage) (map[string]interface{}, error) {
   885  	if message == nil {
   886  		return make(map[string]interface{}), nil
   887  	}
   888  
   889  	return mapFromRawJSON(message)
   890  }
   891  
   892  func mapFromRawJSON(message json.RawMessage) (map[string]interface{}, error) {
   893  	targetAsMap := make(map[string]interface{})
   894  	err := json.Unmarshal(message, &targetAsMap)
   895  	if err != nil {
   896  		return nil, err
   897  	}
   898  	return targetAsMap, nil
   899  }
   900  
   901  func getSegmentIdsToCopy(data []openrtb2.Data, segTaxValues []int) []string {
   902  	var segmentIdsToCopy = make([]string, 0, len(data))
   903  
   904  	for _, dataRecord := range data {
   905  		if dataRecord.Ext != nil {
   906  			var dataExtObject rubiconDataExt
   907  			err := json.Unmarshal(dataRecord.Ext, &dataExtObject)
   908  			if err != nil {
   909  				continue
   910  			}
   911  			if contains(segTaxValues, dataExtObject.SegTax) {
   912  				for _, segment := range dataRecord.Segment {
   913  					segmentIdsToCopy = append(segmentIdsToCopy, segment.ID)
   914  				}
   915  			}
   916  		}
   917  	}
   918  	return segmentIdsToCopy
   919  }
   920  
   921  func contains(s []int, e int) bool {
   922  	for _, a := range s {
   923  		if a == e {
   924  			return true
   925  		}
   926  	}
   927  	return false
   928  }
   929  
   930  func extractUserBuyerUID(eids []openrtb2.EID) string {
   931  	for _, eid := range eids {
   932  		if eid.Source != "rubiconproject.com" {
   933  			continue
   934  		}
   935  
   936  		for _, uid := range eid.UIDs {
   937  			return uid.ID
   938  		}
   939  	}
   940  
   941  	return ""
   942  }
   943  
   944  func getSegments(eids []openrtb2.EID) (mappedRubiconUidsParam, []error) {
   945  	rubiconUidsParam := mappedRubiconUidsParam{
   946  		segments: make([]string, 0),
   947  	}
   948  	errs := make([]error, 0)
   949  
   950  	for _, eid := range eids {
   951  		switch eid.Source {
   952  		case "liveintent.com":
   953  			uids := eid.UIDs
   954  			if len(uids) > 0 {
   955  				if eid.Ext != nil {
   956  					var eidExt rubiconUserExtEidExt
   957  					if err := json.Unmarshal(eid.Ext, &eidExt); err != nil {
   958  						errs = append(errs, &errortypes.BadInput{
   959  							Message: err.Error(),
   960  						})
   961  						continue
   962  					}
   963  					rubiconUidsParam.segments = eidExt.Segments
   964  				}
   965  			}
   966  		case "liveramp.com":
   967  			uids := eid.UIDs
   968  			if len(uids) > 0 {
   969  				uidId := uids[0].ID
   970  				if uidId != "" && rubiconUidsParam.liverampIdl == "" {
   971  					rubiconUidsParam.liverampIdl = uidId
   972  				}
   973  			}
   974  		}
   975  	}
   976  
   977  	return rubiconUidsParam, errs
   978  }
   979  
   980  func updateUserExtWithSegments(userExtRP *rubiconUserExt, rubiconUidsParam mappedRubiconUidsParam) error {
   981  	if len(rubiconUidsParam.segments) > 0 {
   982  
   983  		if rubiconUidsParam.segments != nil {
   984  			userExtRPTarget := make(map[string]interface{})
   985  
   986  			if userExtRP.RP.Target != nil {
   987  				if err := json.Unmarshal(userExtRP.RP.Target, &userExtRPTarget); err != nil {
   988  					return &errortypes.BadInput{Message: err.Error()}
   989  				}
   990  			}
   991  
   992  			userExtRPTarget["LIseg"] = rubiconUidsParam.segments
   993  
   994  			if target, err := json.Marshal(&userExtRPTarget); err != nil {
   995  				return &errortypes.BadInput{Message: err.Error()}
   996  			} else {
   997  				userExtRP.RP.Target = target
   998  			}
   999  		}
  1000  	}
  1001  	return nil
  1002  }
  1003  
  1004  func isVideo(imp openrtb2.Imp) bool {
  1005  	video := imp.Video
  1006  	if video != nil {
  1007  		// Do any other media types exist? Or check required video fields.
  1008  		return imp.Banner == nil || isFullyPopulatedVideo(video)
  1009  	}
  1010  	return false
  1011  }
  1012  
  1013  func isFullyPopulatedVideo(video *openrtb2.Video) bool {
  1014  	// These are just recommended video fields for XAPI
  1015  	return video.MIMEs != nil && video.Protocols != nil && video.MaxDuration != 0 && video.Linearity != 0 && video.API != nil
  1016  }
  1017  
  1018  func resolveNativeObject(native *openrtb2.Native, target map[string]interface{}) (*openrtb2.Native, error) {
  1019  	if native == nil {
  1020  		return nil, fmt.Errorf("Native object is not present for request")
  1021  	}
  1022  	ver := native.Ver
  1023  	if ver == "1.0" || ver == "1.1" {
  1024  		return native, nil
  1025  	}
  1026  
  1027  	err := json.Unmarshal([]byte(native.Request), &target)
  1028  	if err != nil {
  1029  		return nil, err
  1030  	}
  1031  
  1032  	if _, ok := target["eventtrackers"].([]interface{}); !ok {
  1033  		return nil, fmt.Errorf("Eventtrackers are not present or not of array type")
  1034  	}
  1035  
  1036  	context := target["context"]
  1037  	if context != nil {
  1038  		if _, ok := context.(float64); !ok {
  1039  			return nil, fmt.Errorf("Context is not of int type")
  1040  		}
  1041  	}
  1042  
  1043  	if _, ok := target["plcmttype"].(float64); !ok {
  1044  		return nil, fmt.Errorf("Plcmttype is not present or not of int type")
  1045  	}
  1046  
  1047  	return native, nil
  1048  }
  1049  
  1050  func setImpNative(jsonData []byte, requestNative map[string]interface{}) ([]byte, error) {
  1051  	var jsonMap map[string]interface{}
  1052  	if err := json.Unmarshal(jsonData, &jsonMap); err != nil {
  1053  		return jsonData, err
  1054  	}
  1055  
  1056  	var impMap map[string]interface{}
  1057  	if impSlice, ok := maputil.ReadEmbeddedSlice(jsonMap, "imp"); !ok {
  1058  		return jsonData, fmt.Errorf("unable to find imp in json data")
  1059  	} else if len(impSlice) == 0 {
  1060  		return jsonData, fmt.Errorf("unable to find imp[0] in json data")
  1061  	} else if impMap, ok = impSlice[0].(map[string]interface{}); !ok {
  1062  		return jsonData, fmt.Errorf("unexpected type for imp[0] found in json data")
  1063  	}
  1064  
  1065  	nativeMap, ok := maputil.ReadEmbeddedMap(impMap, "native")
  1066  	if !ok {
  1067  		return jsonData, fmt.Errorf("unable to find imp[0].native in json data")
  1068  	}
  1069  
  1070  	nativeMap["request_native"] = requestNative
  1071  
  1072  	if jsonReEncoded, err := json.Marshal(jsonMap); err == nil {
  1073  		return jsonReEncoded, nil
  1074  	} else {
  1075  		return nil, fmt.Errorf("unable to encode json data (%v)", err)
  1076  	}
  1077  }
  1078  
  1079  func (a *RubiconAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
  1080  	if response.StatusCode == http.StatusNoContent {
  1081  		return nil, nil
  1082  	}
  1083  
  1084  	if response.StatusCode == http.StatusBadRequest {
  1085  		return nil, []error{&errortypes.BadInput{
  1086  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
  1087  		}}
  1088  	}
  1089  
  1090  	if response.StatusCode != http.StatusOK {
  1091  		return nil, []error{&errortypes.BadServerResponse{
  1092  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
  1093  		}}
  1094  	}
  1095  
  1096  	var bidResp rubiconBidResponse
  1097  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
  1098  		return nil, []error{&errortypes.BadServerResponse{
  1099  			Message: err.Error(),
  1100  		}}
  1101  	}
  1102  
  1103  	var bidReq openrtb2.BidRequest
  1104  	if err := json.Unmarshal(externalRequest.Body, &bidReq); err != nil {
  1105  		return nil, []error{err}
  1106  	}
  1107  
  1108  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)
  1109  
  1110  	bidType := openrtb_ext.BidTypeNative
  1111  
  1112  	isVideo := isVideo(bidReq.Imp[0])
  1113  	if isVideo {
  1114  		bidType = openrtb_ext.BidTypeVideo
  1115  	} else if bidReq.Imp[0].Banner != nil {
  1116  		bidType = openrtb_ext.BidTypeBanner
  1117  	}
  1118  
  1119  	impToCpmOverride := mapImpIdToCpmOverride(internalRequest.Imp)
  1120  	cmpOverride := cmpOverrideFromBidRequest(internalRequest)
  1121  
  1122  	for _, sb := range bidResp.SeatBid {
  1123  		buyer, err := strconv.Atoi(sb.Buyer)
  1124  		if err != nil {
  1125  			buyer = 0
  1126  		}
  1127  		for i := 0; i < len(sb.Bid); i++ {
  1128  			bid := sb.Bid[i]
  1129  
  1130  			updatedBidExt := updateBidExtWithMetaNetworkId(bid, buyer)
  1131  			if updatedBidExt != nil {
  1132  				bid.Ext = updatedBidExt
  1133  			}
  1134  			bidCmpOverride, ok := impToCpmOverride[bid.ImpID]
  1135  			if !ok || bidCmpOverride == 0 {
  1136  				bidCmpOverride = cmpOverride
  1137  			}
  1138  
  1139  			if bidCmpOverride > 0 {
  1140  				bid.Price = bidCmpOverride
  1141  			}
  1142  
  1143  			if bid.Price != 0 {
  1144  				// Since Rubicon XAPI returns only one bid per response
  1145  				// copy response.bidid to openrtb_response.seatbid.bid.bidid
  1146  				if bid.ID == "0" {
  1147  					bid.ID = bidResp.BidID
  1148  				}
  1149  
  1150  				resolvedAdm := resolveAdm(bid)
  1151  				if len(resolvedAdm) > 0 {
  1152  					bid.AdM = resolvedAdm
  1153  				}
  1154  
  1155  				var ortbBid openrtb2.Bid // `targetStruct` can be anything of your choice
  1156  
  1157  				rubiconBidAsBytes, _ := json.Marshal(bid)
  1158  				if len(rubiconBidAsBytes) > 0 {
  1159  					err = json.Unmarshal(rubiconBidAsBytes, &ortbBid)
  1160  					if err != nil {
  1161  						return nil, []error{err}
  1162  					}
  1163  				}
  1164  
  1165  				bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
  1166  					Bid:     &ortbBid,
  1167  					BidType: bidType,
  1168  				})
  1169  			}
  1170  		}
  1171  	}
  1172  	if bidResp.Cur != "" {
  1173  		bidResponse.Currency = bidResp.Cur
  1174  	}
  1175  
  1176  	return bidResponse, nil
  1177  }
  1178  
  1179  func mapImpIdToCpmOverride(imps []openrtb2.Imp) map[string]float64 {
  1180  	impIdToCmpOverride := make(map[string]float64)
  1181  	for _, imp := range imps {
  1182  		var bidderExt adapters.ExtImpBidder
  1183  		if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
  1184  			continue
  1185  		}
  1186  
  1187  		var rubiconExt openrtb_ext.ExtImpRubicon
  1188  		if err := json.Unmarshal(bidderExt.Bidder, &rubiconExt); err != nil {
  1189  			continue
  1190  		}
  1191  
  1192  		impIdToCmpOverride[imp.ID] = rubiconExt.Debug.CpmOverride
  1193  	}
  1194  	return impIdToCmpOverride
  1195  }
  1196  
  1197  func resolveAdm(bid rubiconBid) string {
  1198  	var bidAdm = bid.AdM
  1199  	if len(bidAdm) > 0 {
  1200  		return bidAdm
  1201  	}
  1202  
  1203  	admObject := bid.AdmNative
  1204  	admObjectAsBytes, err := json.Marshal(&admObject)
  1205  	if err != nil {
  1206  		return ""
  1207  	}
  1208  
  1209  	return string(admObjectAsBytes)
  1210  }
  1211  
  1212  func cmpOverrideFromBidRequest(bidRequest *openrtb2.BidRequest) float64 {
  1213  	var bidRequestExt bidRequestExt
  1214  	if err := json.Unmarshal(bidRequest.Ext, &bidRequestExt); err != nil {
  1215  		return 0
  1216  	}
  1217  
  1218  	return bidRequestExt.Prebid.Bidders.Rubicon.Debug.CpmOverride
  1219  }
  1220  
  1221  func updateBidExtWithMetaNetworkId(bid rubiconBid, buyer int) json.RawMessage {
  1222  	if buyer <= 0 {
  1223  		return nil
  1224  	}
  1225  	var bidExt *extPrebid
  1226  	if bid.Ext != nil {
  1227  		if err := json.Unmarshal(bid.Ext, &bidExt); err != nil {
  1228  			return nil
  1229  		}
  1230  	}
  1231  
  1232  	if bidExt != nil {
  1233  		if bidExt.Prebid != nil {
  1234  			if bidExt.Prebid.Meta != nil {
  1235  				bidExt.Prebid.Meta.NetworkID = buyer
  1236  			} else {
  1237  				bidExt.Prebid.Meta = &openrtb_ext.ExtBidPrebidMeta{NetworkID: buyer}
  1238  			}
  1239  		} else {
  1240  			bidExt.Prebid = &openrtb_ext.ExtBidPrebid{Meta: &openrtb_ext.ExtBidPrebidMeta{NetworkID: buyer}}
  1241  		}
  1242  	} else {
  1243  		bidExt = &extPrebid{Prebid: &openrtb_ext.ExtBidPrebid{Meta: &openrtb_ext.ExtBidPrebidMeta{NetworkID: buyer}}}
  1244  	}
  1245  
  1246  	marshalledExt, err := json.Marshal(&bidExt)
  1247  	if err == nil {
  1248  		return marshalledExt
  1249  	}
  1250  	return nil
  1251  }