github.com/prebid/prebid-server@v0.275.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/v19/openrtb2" 9 "github.com/prebid/prebid-server/adapters" 10 "github.com/prebid/prebid-server/config" 11 "github.com/prebid/prebid-server/errortypes" 12 "github.com/prebid/prebid-server/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 }}, errors 88 } 89 90 // MakeBids unpacks the server's response into Bids. 91 func (a *VisxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 92 if response.StatusCode == http.StatusNoContent { 93 return nil, nil 94 } 95 96 if response.StatusCode == http.StatusBadRequest { 97 return nil, []error{&errortypes.BadInput{ 98 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 99 }} 100 } 101 102 if response.StatusCode != http.StatusOK { 103 return nil, []error{&errortypes.BadServerResponse{ 104 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 105 }} 106 } 107 108 var bidResp visxResponse 109 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 110 return nil, []error{err} 111 } 112 113 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 114 115 for _, sb := range bidResp.SeatBid { 116 for i := range sb.Bid { 117 bid := openrtb2.Bid{} 118 bid.ID = internalRequest.ID 119 bid.CrID = sb.Bid[i].CrID 120 bid.ImpID = sb.Bid[i].ImpID 121 bid.Price = sb.Bid[i].Price 122 bid.AdM = sb.Bid[i].AdM 123 bid.W = int64(sb.Bid[i].W) 124 bid.H = int64(sb.Bid[i].H) 125 bid.ADomain = sb.Bid[i].ADomain 126 bid.DealID = sb.Bid[i].DealID 127 bid.Ext = sb.Bid[i].Ext 128 129 bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp, sb.Bid[i]) 130 if err != nil { 131 return nil, []error{err} 132 } 133 134 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 135 Bid: &bid, 136 BidType: bidType, 137 }) 138 } 139 } 140 return bidResponse, nil 141 142 } 143 144 func getMediaTypeForImp(impID string, imps []openrtb2.Imp, bid visxBid) (openrtb_ext.BidType, error) { 145 for _, imp := range imps { 146 if imp.ID == impID { 147 var ext visxBidExt 148 if err := json.Unmarshal(bid.Ext, &ext); err == nil { 149 if ext.Prebid.Meta.MediaType == openrtb_ext.BidTypeBanner { 150 return openrtb_ext.BidTypeBanner, nil 151 } 152 if ext.Prebid.Meta.MediaType == openrtb_ext.BidTypeVideo { 153 return openrtb_ext.BidTypeVideo, nil 154 } 155 } 156 157 if imp.Banner != nil { 158 return openrtb_ext.BidTypeBanner, nil 159 } 160 161 if imp.Video != nil { 162 return openrtb_ext.BidTypeVideo, nil 163 } 164 165 return "", &errortypes.BadServerResponse{ 166 Message: fmt.Sprintf("Unknown impression type for ID: \"%s\"", impID), 167 } 168 } 169 } 170 171 // This shouldnt happen. Lets handle it just incase by returning an error. 172 return "", &errortypes.BadServerResponse{ 173 Message: fmt.Sprintf("Failed to find impression for ID: \"%s\"", impID), 174 } 175 } 176 177 // Builder builds a new instance of the Visx adapter for the given bidder with the given config. 178 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 179 bidder := &VisxAdapter{ 180 endpoint: config.Endpoint, 181 } 182 return bidder, nil 183 }