github.com/prebid/prebid-server/v2@v2.18.0/adapters/visx/visx.go (about) 1 package visx 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 8 "github.com/prebid/openrtb/v20/openrtb2" 9 "github.com/prebid/prebid-server/v2/adapters" 10 "github.com/prebid/prebid-server/v2/config" 11 "github.com/prebid/prebid-server/v2/errortypes" 12 "github.com/prebid/prebid-server/v2/openrtb_ext" 13 ) 14 15 type VisxAdapter struct { 16 endpoint string 17 } 18 19 type visxBidExtPrebidMeta struct { 20 MediaType openrtb_ext.BidType `json:"mediaType"` 21 } 22 23 type visxBidExtPrebid struct { 24 Meta visxBidExtPrebidMeta `json:"meta"` 25 } 26 27 type visxBidExt struct { 28 Prebid visxBidExtPrebid `json:"prebid"` 29 } 30 31 type visxBid struct { 32 ImpID string `json:"impid"` 33 Price float64 `json:"price"` 34 UID int `json:"auid"` 35 CrID string `json:"crid,omitempty"` 36 AdM string `json:"adm,omitempty"` 37 ADomain []string `json:"adomain,omitempty"` 38 DealID string `json:"dealid,omitempty"` 39 W uint64 `json:"w,omitempty"` 40 H uint64 `json:"h,omitempty"` 41 Ext json.RawMessage `json:"ext,omitempty"` 42 } 43 44 type visxSeatBid struct { 45 Bid []visxBid `json:"bid"` 46 Seat string `json:"seat,omitempty"` 47 } 48 49 type visxResponse struct { 50 SeatBid []visxSeatBid `json:"seatbid,omitempty"` 51 } 52 53 // MakeRequests makes the HTTP requests which should be made to fetch bids. 54 func (a *VisxAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 55 var errors = make([]error, 0) 56 57 // copy the request, because we are going to mutate it 58 requestCopy := *request 59 if len(requestCopy.Cur) == 0 { 60 requestCopy.Cur = []string{"USD"} 61 } 62 63 reqJSON, err := json.Marshal(requestCopy) 64 if err != nil { 65 errors = append(errors, err) 66 return nil, errors 67 } 68 69 headers := http.Header{} 70 headers.Add("Content-Type", "application/json;charset=utf-8") 71 72 if request.Device != nil { 73 if request.Device.IP != "" { 74 headers.Add("X-Forwarded-For", request.Device.IP) 75 } 76 77 if request.Device.IPv6 != "" { 78 headers.Add("X-Forwarded-For", request.Device.IPv6) 79 } 80 } 81 82 return []*adapters.RequestData{{ 83 Method: "POST", 84 Uri: a.endpoint, 85 Body: reqJSON, 86 Headers: headers, 87 ImpIDs: openrtb_ext.GetImpIDs(requestCopy.Imp), 88 }}, errors 89 } 90 91 // MakeBids unpacks the server's response into Bids. 92 func (a *VisxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 93 if response.StatusCode == http.StatusNoContent { 94 return nil, nil 95 } 96 97 if response.StatusCode == http.StatusBadRequest { 98 return nil, []error{&errortypes.BadInput{ 99 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 100 }} 101 } 102 103 if response.StatusCode != http.StatusOK { 104 return nil, []error{&errortypes.BadServerResponse{ 105 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 106 }} 107 } 108 109 var bidResp visxResponse 110 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 111 return nil, []error{err} 112 } 113 114 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 115 116 for _, sb := range bidResp.SeatBid { 117 for i := range sb.Bid { 118 bid := openrtb2.Bid{} 119 bid.ID = internalRequest.ID 120 bid.CrID = sb.Bid[i].CrID 121 bid.ImpID = sb.Bid[i].ImpID 122 bid.Price = sb.Bid[i].Price 123 bid.AdM = sb.Bid[i].AdM 124 bid.W = int64(sb.Bid[i].W) 125 bid.H = int64(sb.Bid[i].H) 126 bid.ADomain = sb.Bid[i].ADomain 127 bid.DealID = sb.Bid[i].DealID 128 bid.Ext = sb.Bid[i].Ext 129 130 bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp, sb.Bid[i]) 131 if err != nil { 132 return nil, []error{err} 133 } 134 135 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 136 Bid: &bid, 137 BidType: bidType, 138 }) 139 } 140 } 141 return bidResponse, nil 142 143 } 144 145 func getMediaTypeForImp(impID string, imps []openrtb2.Imp, bid visxBid) (openrtb_ext.BidType, error) { 146 for _, imp := range imps { 147 if imp.ID == impID { 148 var ext visxBidExt 149 if err := json.Unmarshal(bid.Ext, &ext); err == nil { 150 if ext.Prebid.Meta.MediaType == openrtb_ext.BidTypeBanner { 151 return openrtb_ext.BidTypeBanner, nil 152 } 153 if ext.Prebid.Meta.MediaType == openrtb_ext.BidTypeVideo { 154 return openrtb_ext.BidTypeVideo, nil 155 } 156 } 157 158 if imp.Banner != nil { 159 return openrtb_ext.BidTypeBanner, nil 160 } 161 162 if imp.Video != nil { 163 return openrtb_ext.BidTypeVideo, nil 164 } 165 166 return "", &errortypes.BadServerResponse{ 167 Message: fmt.Sprintf("Unknown impression type for ID: \"%s\"", impID), 168 } 169 } 170 } 171 172 // This shouldnt happen. Lets handle it just incase by returning an error. 173 return "", &errortypes.BadServerResponse{ 174 Message: fmt.Sprintf("Failed to find impression for ID: \"%s\"", impID), 175 } 176 } 177 178 // Builder builds a new instance of the Visx adapter for the given bidder with the given config. 179 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 180 bidder := &VisxAdapter{ 181 endpoint: config.Endpoint, 182 } 183 return bidder, nil 184 }