github.com/prebid/prebid-server@v0.275.0/adapters/rtbhouse/rtbhouse.go (about) 1 package rtbhouse 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strings" 8 9 "github.com/prebid/openrtb/v19/openrtb2" 10 "github.com/prebid/prebid-server/adapters" 11 "github.com/prebid/prebid-server/config" 12 "github.com/prebid/prebid-server/errortypes" 13 "github.com/prebid/prebid-server/openrtb_ext" 14 ) 15 16 const ( 17 BidderCurrency string = "USD" 18 ) 19 20 // RTBHouseAdapter implements the Bidder interface. 21 type RTBHouseAdapter struct { 22 endpoint string 23 } 24 25 // Builder builds a new instance of the RTBHouse adapter for the given bidder with the given config. 26 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 27 bidder := &RTBHouseAdapter{ 28 endpoint: config.Endpoint, 29 } 30 return bidder, nil 31 } 32 33 // MakeRequests prepares the HTTP requests which should be made to fetch bids. 34 func (adapter *RTBHouseAdapter) MakeRequests( 35 openRTBRequest *openrtb2.BidRequest, 36 reqInfo *adapters.ExtraRequestInfo, 37 ) ( 38 requestsToBidder []*adapters.RequestData, 39 errs []error, 40 ) { 41 42 reqCopy := *openRTBRequest 43 reqCopy.Imp = []openrtb2.Imp{} 44 for _, imp := range openRTBRequest.Imp { 45 var bidFloorCur = imp.BidFloorCur 46 var bidFloor = imp.BidFloor 47 if bidFloorCur == "" && bidFloor == 0 { 48 rtbhouseExt, err := getImpressionExt(imp) 49 if err != nil { 50 return nil, []error{err} 51 } 52 if rtbhouseExt.BidFloor > 0 && len(reqCopy.Cur) > 0 { 53 bidFloorCur = reqCopy.Cur[0] 54 bidFloor = rtbhouseExt.BidFloor 55 } 56 } 57 58 // Check if imp comes with bid floor amount defined in a foreign currency 59 if bidFloor > 0 && bidFloorCur != "" && strings.ToUpper(bidFloorCur) != BidderCurrency { 60 // Convert to US dollars 61 convertedValue, err := reqInfo.ConvertCurrency(bidFloor, bidFloorCur, BidderCurrency) 62 if err != nil { 63 return nil, []error{err} 64 } 65 66 bidFloorCur = BidderCurrency 67 bidFloor = convertedValue 68 } 69 70 if bidFloor > 0 && bidFloorCur == BidderCurrency { 71 // Update after conversion. All imp elements inside request.Imp are shallow copies 72 // therefore, their non-pointer values are not shared memory and are safe to modify. 73 imp.BidFloorCur = bidFloorCur 74 imp.BidFloor = bidFloor 75 } 76 77 // Set the CUR of bid to BIDDER_CURRENCY after converting all floors 78 reqCopy.Cur = []string{BidderCurrency} 79 reqCopy.Imp = append(reqCopy.Imp, imp) 80 } 81 82 openRTBRequestJSON, err := json.Marshal(reqCopy) 83 if err != nil { 84 errs = append(errs, err) 85 return nil, errs 86 } 87 88 headers := http.Header{} 89 headers.Add("Content-Type", "application/json;charset=utf-8") 90 requestToBidder := &adapters.RequestData{ 91 Method: "POST", 92 Uri: adapter.endpoint, 93 Body: openRTBRequestJSON, 94 Headers: headers, 95 } 96 requestsToBidder = append(requestsToBidder, requestToBidder) 97 98 return requestsToBidder, errs 99 } 100 101 func getImpressionExt(imp openrtb2.Imp) (*openrtb_ext.ExtImpRTBHouse, error) { 102 var bidderExt adapters.ExtImpBidder 103 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 104 return nil, &errortypes.BadInput{ 105 Message: "Bidder extension not provided or can't be unmarshalled", 106 } 107 } 108 109 var rtbhouseExt openrtb_ext.ExtImpRTBHouse 110 if err := json.Unmarshal(bidderExt.Bidder, &rtbhouseExt); err != nil { 111 return nil, &errortypes.BadInput{ 112 Message: "Error while unmarshaling bidder extension", 113 } 114 } 115 116 return &rtbhouseExt, nil 117 } 118 119 const unexpectedStatusCodeFormat = "" + 120 "Unexpected status code: %d. Run with request.debug = 1 for more info" 121 122 // MakeBids unpacks the server's response into Bids. 123 func (adapter *RTBHouseAdapter) MakeBids( 124 openRTBRequest *openrtb2.BidRequest, 125 requestToBidder *adapters.RequestData, 126 bidderRawResponse *adapters.ResponseData, 127 ) ( 128 bidderResponse *adapters.BidderResponse, 129 errs []error, 130 ) { 131 switch bidderRawResponse.StatusCode { 132 case http.StatusOK: 133 break 134 case http.StatusNoContent: 135 return nil, nil 136 case http.StatusBadRequest: 137 err := &errortypes.BadInput{ 138 Message: fmt.Sprintf(unexpectedStatusCodeFormat, bidderRawResponse.StatusCode), 139 } 140 return nil, []error{err} 141 default: 142 err := &errortypes.BadServerResponse{ 143 Message: fmt.Sprintf(unexpectedStatusCodeFormat, bidderRawResponse.StatusCode), 144 } 145 return nil, []error{err} 146 } 147 148 var openRTBBidderResponse openrtb2.BidResponse 149 if err := json.Unmarshal(bidderRawResponse.Body, &openRTBBidderResponse); err != nil { 150 return nil, []error{err} 151 } 152 153 bidsCapacity := len(openRTBBidderResponse.SeatBid[0].Bid) 154 bidderResponse = adapters.NewBidderResponseWithBidsCapacity(bidsCapacity) 155 var typedBid *adapters.TypedBid 156 for _, seatBid := range openRTBBidderResponse.SeatBid { 157 for _, bid := range seatBid.Bid { 158 bid := bid // pin! -> https://github.com/kyoh86/scopelint#whats-this 159 typedBid = &adapters.TypedBid{Bid: &bid, BidType: "banner"} 160 bidderResponse.Bids = append(bidderResponse.Bids, typedBid) 161 } 162 } 163 164 bidderResponse.Currency = BidderCurrency 165 166 return bidderResponse, nil 167 168 }