github.com/prebid/prebid-server/v2@v2.18.0/adapters/bidstack/bidstack.go (about) 1 package bidstack 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "net/http" 8 "strings" 9 10 "github.com/prebid/openrtb/v20/openrtb2" 11 12 "github.com/prebid/prebid-server/v2/adapters" 13 "github.com/prebid/prebid-server/v2/config" 14 "github.com/prebid/prebid-server/v2/openrtb_ext" 15 ) 16 17 const ( 18 currencyUSDISO4217 = "USD" 19 20 headerKeyAuthorization = "Authorization" 21 headerValueAuthorizationBearer = "Bearer " 22 headerKeyContentType = "Content-Type" 23 24 contentTypeApplicationJSON = "application/json" 25 ) 26 27 type adapter struct { 28 endpoint string 29 } 30 31 // Builder builds a new instance of the Bidstack adapter for the given bidder with the given config. 32 func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { 33 bidder := &adapter{ 34 endpoint: config.Endpoint, 35 } 36 37 return bidder, nil 38 } 39 40 func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 41 headers, err := prepareHeaders(request) 42 if err != nil { 43 return nil, []error{fmt.Errorf("headers prepare: %v", err)} 44 } 45 46 for i := range request.Imp { 47 imp := &request.Imp[i] 48 if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != currencyUSDISO4217 { 49 convertedValue, err := reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, currencyUSDISO4217) 50 if err != nil { 51 return nil, []error{err} 52 } 53 imp.BidFloorCur = currencyUSDISO4217 54 imp.BidFloor = convertedValue 55 } 56 } 57 58 requestJSON, err := json.Marshal(request) 59 if err != nil { 60 return nil, []error{fmt.Errorf("bid request marshal: %v", err)} 61 } 62 63 requestData := &adapters.RequestData{ 64 Method: "POST", 65 Uri: a.endpoint, 66 Headers: headers, 67 Body: requestJSON, 68 ImpIDs: openrtb_ext.GetImpIDs(request.Imp), 69 } 70 71 return []*adapters.RequestData{requestData}, nil 72 } 73 74 func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { 75 switch responseData.StatusCode { 76 case http.StatusNoContent: 77 return nil, nil 78 case http.StatusBadRequest: 79 return nil, []error{errors.New("bad request from publisher")} 80 case http.StatusOK: 81 break 82 default: 83 return nil, []error{fmt.Errorf("unexpected response status code: %v", responseData.StatusCode)} 84 } 85 86 var response openrtb2.BidResponse 87 if err := json.Unmarshal(responseData.Body, &response); err != nil { 88 return nil, []error{fmt.Errorf("bid response unmarshal: %v", err)} 89 } 90 91 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) 92 bidResponse.Currency = response.Cur 93 for _, seatBid := range response.SeatBid { 94 for i := range seatBid.Bid { 95 b := &adapters.TypedBid{ 96 Bid: &seatBid.Bid[i], 97 BidType: openrtb_ext.BidTypeVideo, 98 } 99 bidResponse.Bids = append(bidResponse.Bids, b) 100 } 101 } 102 103 return bidResponse, nil 104 } 105 106 func prepareHeaders(request *openrtb2.BidRequest) (headers http.Header, err error) { 107 bidderImpExt, err := getBidderExt(request.Imp[0]) 108 if err != nil { 109 return nil, fmt.Errorf("get bidder ext: %v", err) 110 } 111 112 return http.Header{ 113 headerKeyContentType: {contentTypeApplicationJSON}, 114 headerKeyAuthorization: {headerValueAuthorizationBearer + bidderImpExt.PublisherID}, 115 }, nil 116 } 117 118 func getBidderExt(imp openrtb2.Imp) (bidderImpExt openrtb_ext.ImpExtBidstack, err error) { 119 var impExt adapters.ExtImpBidder 120 if err = json.Unmarshal(imp.Ext, &impExt); err != nil { 121 return bidderImpExt, fmt.Errorf("imp ext: %v", err) 122 } 123 if err = json.Unmarshal(impExt.Bidder, &bidderImpExt); err != nil { 124 return bidderImpExt, fmt.Errorf("bidder ext: %v", err) 125 } 126 return bidderImpExt, nil 127 }