github.com/prebid/prebid-server@v0.275.0/adapters/orbidder/orbidder.go (about) 1 package orbidder 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strings" 8 9 "github.com/prebid/openrtb/v19/openrtb2" 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 ) 16 17 type OrbidderAdapter struct { 18 endpoint string 19 } 20 21 // MakeRequests makes the HTTP requests which should be made to fetch bids from orbidder. 22 func (rcv *OrbidderAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 23 validImps, errs := getValidImpressions(request, reqInfo) 24 if len(validImps) == 0 { 25 return nil, errs 26 } 27 28 request.Imp = validImps 29 30 requestBodyJSON, err := json.Marshal(request) 31 if err != nil { 32 errs = append(errs, err) 33 return nil, errs 34 } 35 36 headers := http.Header{} 37 headers.Add("Content-Type", "application/json;charset=utf-8") 38 headers.Add("Accept", "application/json") 39 40 return []*adapters.RequestData{{ 41 Method: http.MethodPost, 42 Uri: rcv.endpoint, 43 Body: requestBodyJSON, 44 Headers: headers, 45 }}, errs 46 } 47 48 // getValidImpressions validate imps and check for bid floor currency. Convert to EUR if necessary 49 func getValidImpressions(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]openrtb2.Imp, []error) { 50 var errs []error 51 var validImps []openrtb2.Imp 52 53 for _, imp := range request.Imp { 54 if err := preprocessBidFloorCurrency(&imp, reqInfo); err != nil { 55 errs = append(errs, err) 56 continue 57 } 58 59 if err := preprocessExtensions(&imp); err != nil { 60 errs = append(errs, err) 61 continue 62 } 63 validImps = append(validImps, imp) 64 } 65 return validImps, errs 66 } 67 68 func preprocessExtensions(imp *openrtb2.Imp) error { 69 var bidderExt adapters.ExtImpBidder 70 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 71 return &errortypes.BadInput{ 72 Message: err.Error(), 73 } 74 } 75 76 var orbidderExt openrtb_ext.ExtImpOrbidder 77 if err := json.Unmarshal(bidderExt.Bidder, &orbidderExt); err != nil { 78 return &errortypes.BadInput{ 79 Message: "Wrong orbidder bidder ext: " + err.Error(), 80 } 81 } 82 83 return nil 84 } 85 86 func preprocessBidFloorCurrency(imp *openrtb2.Imp, reqInfo *adapters.ExtraRequestInfo) error { 87 // we expect every currency related data to be EUR 88 if imp.BidFloor > 0 && strings.ToUpper(imp.BidFloorCur) != "EUR" && imp.BidFloorCur != "" { 89 if convertedValue, err := reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "EUR"); err != nil { 90 return err 91 } else { 92 imp.BidFloor = convertedValue 93 } 94 } 95 imp.BidFloorCur = "EUR" 96 return nil 97 } 98 99 // MakeBids unpacks server response into Bids. 100 func (rcv OrbidderAdapter) MakeBids(_ *openrtb2.BidRequest, _ *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 101 if response.StatusCode == http.StatusNoContent { 102 return nil, nil 103 } 104 105 if response.StatusCode >= http.StatusInternalServerError { 106 return nil, []error{&errortypes.BadServerResponse{ 107 Message: fmt.Sprintf("Unexpected status code: %d. Dsp server internal error.", response.StatusCode), 108 }} 109 } 110 111 if response.StatusCode >= http.StatusBadRequest { 112 return nil, []error{&errortypes.BadInput{ 113 Message: fmt.Sprintf("Unexpected status code: %d. Bad request to dsp.", response.StatusCode), 114 }} 115 } 116 117 if response.StatusCode != http.StatusOK { 118 return nil, []error{&errortypes.BadServerResponse{ 119 Message: fmt.Sprintf("Unexpected status code: %d. Bad response from dsp.", response.StatusCode), 120 }} 121 } 122 123 var bidResp openrtb2.BidResponse 124 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 125 return nil, []error{err} 126 } 127 128 var bidErrs []error 129 bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) 130 for _, seatBid := range bidResp.SeatBid { 131 for i := range seatBid.Bid { 132 // later we have to add the bid as a pointer, 133 // because of this we need a variable that only exists at this loop iteration. 134 // otherwise there will be issues with multibid and pointer behavior. 135 bid := seatBid.Bid[i] 136 bidType, err := getBidType(bid) 137 if err != nil { 138 // could not determinate media type, append an error and continue with the next bid. 139 bidErrs = append(bidErrs, err) 140 continue 141 } 142 143 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 144 Bid: &bid, 145 BidType: bidType, 146 }) 147 } 148 } 149 if bidResp.Cur != "" { 150 bidResponse.Currency = bidResp.Cur 151 } 152 153 return bidResponse, bidErrs 154 } 155 156 func getBidType(bid openrtb2.Bid) (openrtb_ext.BidType, error) { 157 158 // determinate media type by bid response field mtype 159 switch bid.MType { 160 case openrtb2.MarkupBanner: 161 return openrtb_ext.BidTypeBanner, nil 162 case openrtb2.MarkupVideo: 163 return openrtb_ext.BidTypeVideo, nil 164 case openrtb2.MarkupAudio: 165 return openrtb_ext.BidTypeAudio, nil 166 case openrtb2.MarkupNative: 167 return openrtb_ext.BidTypeNative, nil 168 } 169 170 return "", &errortypes.BadInput{ 171 Message: fmt.Sprintf("Could not define media type for impression: %s", bid.ImpID), 172 } 173 } 174 175 // Builder builds a new instance of the Orbidder adapter for the given bidder with the given config. 176 func Builder(_ openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 177 bidder := &OrbidderAdapter{ 178 endpoint: config.Endpoint, 179 } 180 return bidder, nil 181 }