github.com/prebid/prebid-server@v0.275.0/adapters/openx/openx.go (about) 1 package openx 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 8 "github.com/prebid/openrtb/v19/openrtb2" 9 "github.com/prebid/prebid-server/adapters" 10 "github.com/prebid/prebid-server/config" 11 "github.com/prebid/prebid-server/errortypes" 12 "github.com/prebid/prebid-server/openrtb_ext" 13 ) 14 15 const hbconfig = "hb_pbs_1.0.0" 16 17 type OpenxAdapter struct { 18 bidderName string 19 endpoint string 20 } 21 22 type openxImpExt map[string]json.RawMessage 23 24 type openxReqExt struct { 25 DelDomain string `json:"delDomain,omitempty"` 26 Platform string `json:"platform,omitempty"` 27 BidderConfig string `json:"bc"` 28 } 29 30 type openxRespExt struct { 31 FledgeAuctionConfigs map[string]json.RawMessage `json:"fledge_auction_configs,omitempty"` 32 } 33 34 func (a *OpenxAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 35 var errs []error 36 var bannerImps []openrtb2.Imp 37 var videoImps []openrtb2.Imp 38 39 for _, imp := range request.Imp { 40 // OpenX doesn't allow multi-type imp. Banner takes priority over video. 41 if imp.Banner != nil { 42 bannerImps = append(bannerImps, imp) 43 } else if imp.Video != nil { 44 videoImps = append(videoImps, imp) 45 } 46 } 47 48 var adapterRequests []*adapters.RequestData 49 // Make a copy as we don't want to change the original request 50 reqCopy := *request 51 52 reqCopy.Imp = bannerImps 53 adapterReq, errors := a.makeRequest(&reqCopy) 54 if adapterReq != nil { 55 adapterRequests = append(adapterRequests, adapterReq) 56 } 57 errs = append(errs, errors...) 58 59 // OpenX only supports single imp video request 60 for _, videoImp := range videoImps { 61 reqCopy.Imp = []openrtb2.Imp{videoImp} 62 adapterReq, errors := a.makeRequest(&reqCopy) 63 if adapterReq != nil { 64 adapterRequests = append(adapterRequests, adapterReq) 65 } 66 errs = append(errs, errors...) 67 } 68 69 return adapterRequests, errs 70 } 71 72 func (a *OpenxAdapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) { 73 var errs []error 74 var validImps []openrtb2.Imp 75 reqExt := openxReqExt{BidderConfig: hbconfig} 76 77 for _, imp := range request.Imp { 78 if err := preprocess(&imp, &reqExt); err != nil { 79 errs = append(errs, err) 80 continue 81 } 82 validImps = append(validImps, imp) 83 } 84 85 // If all the imps were malformed, don't bother making a server call with no impressions. 86 if len(validImps) == 0 { 87 return nil, errs 88 } 89 90 request.Imp = validImps 91 92 var err error 93 request.Ext, err = json.Marshal(reqExt) 94 if err != nil { 95 errs = append(errs, err) 96 return nil, errs 97 } 98 99 reqJSON, err := json.Marshal(request) 100 if err != nil { 101 errs = append(errs, err) 102 return nil, errs 103 } 104 105 headers := http.Header{} 106 headers.Add("Content-Type", "application/json;charset=utf-8") 107 headers.Add("Accept", "application/json") 108 return &adapters.RequestData{ 109 Method: "POST", 110 Uri: a.endpoint, 111 Body: reqJSON, 112 Headers: headers, 113 }, errs 114 } 115 116 // Mutate the imp to get it ready to send to openx. 117 func preprocess(imp *openrtb2.Imp, reqExt *openxReqExt) error { 118 var bidderExt adapters.ExtImpBidder 119 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 120 return &errortypes.BadInput{ 121 Message: err.Error(), 122 } 123 } 124 125 var openxExt openrtb_ext.ExtImpOpenx 126 if err := json.Unmarshal(bidderExt.Bidder, &openxExt); err != nil { 127 return &errortypes.BadInput{ 128 Message: err.Error(), 129 } 130 } 131 132 reqExt.DelDomain = openxExt.DelDomain 133 reqExt.Platform = openxExt.Platform 134 135 imp.TagID = openxExt.Unit 136 if imp.BidFloor == 0 && openxExt.CustomFloor > 0 { 137 imp.BidFloor = openxExt.CustomFloor 138 } 139 140 // outgoing imp.ext should be same as incoming imp.ext minus prebid and bidder 141 impExt := openxImpExt{} 142 if err := json.Unmarshal(imp.Ext, &impExt); err != nil { 143 return &errortypes.BadInput{ 144 Message: err.Error(), 145 } 146 } 147 delete(impExt, openrtb_ext.PrebidExtKey) 148 delete(impExt, openrtb_ext.PrebidExtBidderKey) 149 150 if openxExt.CustomParams != nil { 151 var err error 152 if impExt["customParams"], err = json.Marshal(openxExt.CustomParams); err != nil { 153 return &errortypes.BadInput{ 154 Message: err.Error(), 155 } 156 } 157 } 158 159 if len(impExt) > 0 { 160 var err error 161 if imp.Ext, err = json.Marshal(impExt); err != nil { 162 return &errortypes.BadInput{ 163 Message: err.Error(), 164 } 165 } 166 } else { 167 imp.Ext = nil 168 } 169 170 if imp.Video != nil { 171 videoCopy := *imp.Video 172 if bidderExt.Prebid != nil && bidderExt.Prebid.IsRewardedInventory != nil && *bidderExt.Prebid.IsRewardedInventory == 1 { 173 videoCopy.Ext = json.RawMessage(`{"rewarded":1}`) 174 } else { 175 videoCopy.Ext = nil 176 } 177 imp.Video = &videoCopy 178 } 179 180 return nil 181 } 182 183 func (a *OpenxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 184 if response.StatusCode == http.StatusNoContent { 185 return nil, nil 186 } 187 188 if response.StatusCode == http.StatusBadRequest { 189 return nil, []error{&errortypes.BadInput{ 190 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 191 }} 192 } 193 194 if response.StatusCode != http.StatusOK { 195 return nil, []error{&errortypes.BadServerResponse{ 196 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 197 }} 198 } 199 200 var bidResp openrtb2.BidResponse 201 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 202 return nil, []error{err} 203 } 204 205 bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) 206 207 // overrride default currency 208 if bidResp.Cur != "" { 209 bidResponse.Currency = bidResp.Cur 210 } 211 212 if bidResp.Ext != nil { 213 var bidRespExt openxRespExt 214 if err := json.Unmarshal(bidResp.Ext, &bidRespExt); err == nil && bidRespExt.FledgeAuctionConfigs != nil { 215 bidResponse.FledgeAuctionConfigs = make([]*openrtb_ext.FledgeAuctionConfig, 0, len(bidRespExt.FledgeAuctionConfigs)) 216 for impId, config := range bidRespExt.FledgeAuctionConfigs { 217 fledgeAuctionConfig := &openrtb_ext.FledgeAuctionConfig{ 218 ImpId: impId, 219 Bidder: a.bidderName, 220 Config: config, 221 } 222 bidResponse.FledgeAuctionConfigs = append(bidResponse.FledgeAuctionConfigs, fledgeAuctionConfig) 223 } 224 } 225 } 226 227 for _, sb := range bidResp.SeatBid { 228 for i := range sb.Bid { 229 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 230 Bid: &sb.Bid[i], 231 BidType: getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp), 232 }) 233 } 234 } 235 return bidResponse, nil 236 } 237 238 // getMediaTypeForImp figures out which media type this bid is for. 239 // 240 // OpenX doesn't support multi-type impressions. 241 // If both banner and video exist, take banner as we do not want in-banner video. 242 func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType { 243 mediaType := openrtb_ext.BidTypeBanner 244 for _, imp := range imps { 245 if imp.ID == impId { 246 if imp.Banner == nil && imp.Video != nil { 247 mediaType = openrtb_ext.BidTypeVideo 248 } 249 return mediaType 250 } 251 } 252 return mediaType 253 } 254 255 // Builder builds a new instance of the Openx adapter for the given bidder with the given config. 256 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 257 bidder := &OpenxAdapter{ 258 endpoint: config.Endpoint, 259 bidderName: string(bidderName), 260 } 261 return bidder, nil 262 }