github.com/prebid/prebid-server@v0.275.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/v19/openrtb2" 11 12 "github.com/prebid/prebid-server/adapters" 13 "github.com/prebid/prebid-server/config" 14 "github.com/prebid/prebid-server/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 } 69 70 return []*adapters.RequestData{requestData}, nil 71 } 72 73 func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { 74 switch responseData.StatusCode { 75 case http.StatusNoContent: 76 return nil, nil 77 case http.StatusBadRequest: 78 return nil, []error{errors.New("bad request from publisher")} 79 case http.StatusOK: 80 break 81 default: 82 return nil, []error{fmt.Errorf("unexpected response status code: %v", responseData.StatusCode)} 83 } 84 85 var response openrtb2.BidResponse 86 if err := json.Unmarshal(responseData.Body, &response); err != nil { 87 return nil, []error{fmt.Errorf("bid response unmarshal: %v", err)} 88 } 89 90 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) 91 bidResponse.Currency = response.Cur 92 for _, seatBid := range response.SeatBid { 93 for i := range seatBid.Bid { 94 b := &adapters.TypedBid{ 95 Bid: &seatBid.Bid[i], 96 BidType: openrtb_ext.BidTypeVideo, 97 } 98 bidResponse.Bids = append(bidResponse.Bids, b) 99 } 100 } 101 102 return bidResponse, nil 103 } 104 105 func prepareHeaders(request *openrtb2.BidRequest) (headers http.Header, err error) { 106 bidderImpExt, err := getBidderExt(request.Imp[0]) 107 if err != nil { 108 return nil, fmt.Errorf("get bidder ext: %v", err) 109 } 110 111 return http.Header{ 112 headerKeyContentType: {contentTypeApplicationJSON}, 113 headerKeyAuthorization: {headerValueAuthorizationBearer + bidderImpExt.PublisherID}, 114 }, nil 115 } 116 117 func getBidderExt(imp openrtb2.Imp) (bidderImpExt openrtb_ext.ImpExtBidstack, err error) { 118 var impExt adapters.ExtImpBidder 119 if err = json.Unmarshal(imp.Ext, &impExt); err != nil { 120 return bidderImpExt, fmt.Errorf("imp ext: %v", err) 121 } 122 if err = json.Unmarshal(impExt.Bidder, &bidderImpExt); err != nil { 123 return bidderImpExt, fmt.Errorf("bidder ext: %v", err) 124 } 125 return bidderImpExt, nil 126 }