github.com/prebid/prebid-server/v2@v2.18.0/adapters/adquery/adquery.go (about) 1 package adquery 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "regexp" 8 "strconv" 9 "strings" 10 11 "github.com/prebid/openrtb/v20/openrtb2" 12 "github.com/prebid/prebid-server/v2/adapters" 13 "github.com/prebid/prebid-server/v2/config" 14 "github.com/prebid/prebid-server/v2/errortypes" 15 "github.com/prebid/prebid-server/v2/openrtb_ext" 16 ) 17 18 const ( 19 defaultCurrency string = "PLN" 20 bidderName string = "adquery" 21 prebidVersion string = "server" 22 ) 23 24 type adapter struct { 25 endpoint string 26 } 27 28 // Builder builds a new instance of the Adquery adapter for the given bidder with the given config. 29 func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { 30 bidder := &adapter{ 31 endpoint: config.Endpoint, 32 } 33 return bidder, nil 34 } 35 36 func (a *adapter) MakeRequests(request *openrtb2.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 37 headers := buildHeaders(request) 38 39 var result []*adapters.RequestData 40 var errs []error 41 for _, imp := range request.Imp { 42 ext, err := parseExt(imp.Ext) 43 if err != nil { 44 errs = append(errs, &errortypes.BadInput{Message: err.Error()}) 45 continue 46 } 47 48 requestJSON, err := json.Marshal(buildRequest(request, &imp, ext)) 49 if err != nil { 50 return nil, append(errs, err) 51 } 52 53 data := &adapters.RequestData{ 54 Method: "POST", 55 Uri: a.endpoint, 56 Body: requestJSON, 57 Headers: headers, 58 ImpIDs: []string{imp.ID}, 59 } 60 result = append(result, data) 61 } 62 63 return result, errs 64 } 65 66 func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { 67 if adapters.IsResponseStatusCodeNoContent(responseData) { 68 return nil, nil 69 } 70 71 err := adapters.CheckResponseStatusCodeForErrors(responseData) 72 if err != nil { 73 return nil, []error{err} 74 } 75 76 respData, price, width, height, errs := parseResponseJson(responseData.Body) 77 if len(errs) > 0 { 78 return nil, errs 79 } 80 81 if respData == nil { 82 return adapters.NewBidderResponse(), nil 83 } 84 85 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 86 if respData.Currency != "" { 87 bidResponse.Currency = respData.Currency 88 } else { 89 bidResponse.Currency = defaultCurrency 90 } 91 92 var bidReqIdRegex = regexp.MustCompile(`^` + request.ID) 93 impId := bidReqIdRegex.ReplaceAllLiteralString(respData.ReqID, "") 94 95 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 96 Bid: &openrtb2.Bid{ 97 // There's much more possible fields to be added here, see OpenRTB docs for reference (type: Bid) 98 ID: respData.ReqID, 99 ImpID: impId, 100 Price: price, 101 AdM: fmt.Sprintf("<script src=\"%s\"></script>%s", respData.AdQLib, respData.Tag), 102 ADomain: respData.ADomains, 103 CrID: fmt.Sprintf("%d", respData.CrID), 104 W: width, 105 H: height, 106 }, 107 BidType: respData.MediaType.Name, 108 }) 109 110 return bidResponse, nil 111 } 112 113 func buildHeaders(bidReq *openrtb2.BidRequest) http.Header { 114 headers := http.Header{} 115 116 headers.Add("Content-Type", "application/json;charset=utf-8") 117 headers.Add("Accept", "application/json") 118 headers.Add("X-Openrtb-Version", "2.5") 119 120 if bidReq.Device != nil && len(bidReq.Device.IP) > 0 { 121 headers.Add("X-Forwarded-For", bidReq.Device.IP) 122 } 123 124 return headers 125 } 126 127 func buildRequest(bidReq *openrtb2.BidRequest, imp *openrtb2.Imp, ext *openrtb_ext.ImpExtAdQuery) *BidderRequest { 128 userId := "" 129 if bidReq.User != nil { 130 userId = bidReq.User.ID 131 } 132 133 bidderRequest := &BidderRequest{ 134 V: prebidVersion, 135 PlacementCode: ext.PlacementID, 136 AuctionId: "", 137 BidType: ext.Type, 138 AdUnitCode: imp.TagID, 139 BidQid: userId, 140 BidId: fmt.Sprintf("%s%s", bidReq.ID, imp.ID), 141 Bidder: bidderName, 142 BidderRequestId: bidReq.ID, 143 BidRequestsCount: 1, 144 BidderRequestsCount: 1, 145 Sizes: getImpSizes(imp), 146 } 147 148 if bidReq.Device != nil { 149 bidderRequest.BidIp = bidReq.Device.IP 150 bidderRequest.BidIpv6 = bidReq.Device.IPv6 151 bidderRequest.BidUa = bidReq.Device.UA 152 } 153 154 if bidReq.Site != nil { 155 bidderRequest.BidPageUrl = bidReq.Site.Page 156 } 157 158 return bidderRequest 159 } 160 161 func parseExt(ext json.RawMessage) (*openrtb_ext.ImpExtAdQuery, error) { 162 var bext adapters.ExtImpBidder 163 err := json.Unmarshal(ext, &bext) 164 if err != nil { 165 return nil, err 166 } 167 168 var adsExt openrtb_ext.ImpExtAdQuery 169 err = json.Unmarshal(bext.Bidder, &adsExt) 170 if err != nil { 171 return nil, err 172 } 173 174 // not validating, because it should have been done earlier by the server 175 return &adsExt, nil 176 } 177 178 func parseResponseJson(respBody []byte) (*ResponseData, float64, int64, int64, []error) { 179 var response ResponseAdQuery 180 if err := json.Unmarshal(respBody, &response); err != nil { 181 return nil, 0, 0, 0, []error{err} 182 } 183 184 if response.Data == nil { 185 return nil, 0, 0, 0, nil 186 } 187 188 var errs []error 189 price, err := strconv.ParseFloat(response.Data.CPM, 64) 190 if err != nil { 191 errs = append(errs, err) 192 } 193 width, err := strconv.ParseInt(response.Data.MediaType.Width, 10, 64) 194 if err != nil { 195 errs = append(errs, err) 196 } 197 height, err := strconv.ParseInt(response.Data.MediaType.Height, 10, 64) 198 if err != nil { 199 errs = append(errs, err) 200 } 201 202 if response.Data.MediaType.Name != openrtb_ext.BidTypeBanner { 203 return nil, 0, 0, 0, []error{fmt.Errorf("unsupported MediaType: %s", response.Data.MediaType.Name)} 204 } 205 206 if len(errs) > 0 { 207 return nil, 0, 0, 0, errs 208 } 209 return response.Data, price, width, height, nil 210 } 211 212 func getImpSizes(imp *openrtb2.Imp) string { 213 if imp.Banner == nil { 214 return "" 215 } 216 217 if len(imp.Banner.Format) > 0 { 218 sizes := make([]string, len(imp.Banner.Format)) 219 for i, format := range imp.Banner.Format { 220 sizes[i] = strconv.FormatInt(format.W, 10) + "x" + strconv.FormatInt(format.H, 10) 221 } 222 223 return strings.Join(sizes, ",") 224 } 225 226 if imp.Banner.W != nil && imp.Banner.H != nil { 227 return strconv.FormatInt(*imp.Banner.W, 10) + "x" + strconv.FormatInt(*imp.Banner.H, 10) 228 } 229 230 return "" 231 }