github.com/prebid/prebid-server@v0.275.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/v19/openrtb2" 12 "github.com/prebid/prebid-server/adapters" 13 "github.com/prebid/prebid-server/config" 14 "github.com/prebid/prebid-server/errortypes" 15 "github.com/prebid/prebid-server/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 := http.Header{} 38 headers.Add("Content-Type", "application/json;charset=utf-8") 39 headers.Add("Accept", "application/json") 40 headers.Add("x-openrtb-version", "2.5") 41 42 var result []*adapters.RequestData 43 var errs []error 44 for _, imp := range request.Imp { 45 ext, err := parseExt(imp.Ext) 46 if err != nil { 47 errs = append(errs, &errortypes.BadInput{err.Error()}) 48 continue 49 } 50 51 requestJSON, err := json.Marshal(buildRequest(request, &imp, ext)) 52 if err != nil { 53 return nil, append(errs, err) 54 } 55 56 data := &adapters.RequestData{ 57 Method: "POST", 58 Uri: a.endpoint, 59 Body: requestJSON, 60 Headers: headers, 61 } 62 result = append(result, data) 63 } 64 65 return result, errs 66 } 67 68 func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { 69 if adapters.IsResponseStatusCodeNoContent(responseData) { 70 return nil, nil 71 } 72 73 err := adapters.CheckResponseStatusCodeForErrors(responseData) 74 if err != nil { 75 return nil, []error{err} 76 } 77 78 respData, price, width, height, errs := parseResponseJson(responseData.Body) 79 if len(errs) > 0 { 80 return nil, errs 81 } 82 83 if respData == nil { 84 return adapters.NewBidderResponse(), nil 85 } 86 87 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 88 if respData.Currency != "" { 89 bidResponse.Currency = respData.Currency 90 } else { 91 bidResponse.Currency = defaultCurrency 92 } 93 94 var bidReqIdRegex = regexp.MustCompile(`^` + request.ID) 95 impId := bidReqIdRegex.ReplaceAllLiteralString(respData.ReqID, "") 96 97 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 98 Bid: &openrtb2.Bid{ 99 // There's much more possible fields to be added here, see OpenRTB docs for reference (type: Bid) 100 ID: respData.ReqID, 101 ImpID: impId, 102 Price: price, 103 AdM: fmt.Sprintf("<script src=\"%s\"></script>%s", respData.AdQLib, respData.Tag), 104 ADomain: respData.ADomains, 105 CrID: fmt.Sprintf("%d", respData.CrID), 106 W: width, 107 H: height, 108 }, 109 BidType: respData.MediaType.Name, 110 }) 111 112 return bidResponse, nil 113 } 114 115 func buildRequest(bidReq *openrtb2.BidRequest, imp *openrtb2.Imp, ext *openrtb_ext.ImpExtAdQuery) *BidderRequest { 116 userId := "" 117 if bidReq.User != nil { 118 userId = bidReq.User.ID 119 } 120 121 return &BidderRequest{ 122 V: prebidVersion, 123 PlacementCode: ext.PlacementID, 124 AuctionId: "", 125 BidType: ext.Type, 126 AdUnitCode: imp.TagID, 127 BidQid: userId, 128 BidId: fmt.Sprintf("%s%s", bidReq.ID, imp.ID), 129 Bidder: bidderName, 130 BidderRequestId: bidReq.ID, 131 BidRequestsCount: 1, 132 BidderRequestsCount: 1, 133 Sizes: getImpSizes(imp), 134 } 135 } 136 137 func parseExt(ext json.RawMessage) (*openrtb_ext.ImpExtAdQuery, error) { 138 var bext adapters.ExtImpBidder 139 err := json.Unmarshal(ext, &bext) 140 if err != nil { 141 return nil, err 142 } 143 144 var adsExt openrtb_ext.ImpExtAdQuery 145 err = json.Unmarshal(bext.Bidder, &adsExt) 146 if err != nil { 147 return nil, err 148 } 149 150 // not validating, because it should have been done earlier by the server 151 return &adsExt, nil 152 } 153 154 func parseResponseJson(respBody []byte) (*ResponseData, float64, int64, int64, []error) { 155 var response ResponseAdQuery 156 if err := json.Unmarshal(respBody, &response); err != nil { 157 return nil, 0, 0, 0, []error{err} 158 } 159 160 if response.Data == nil { 161 return nil, 0, 0, 0, nil 162 } 163 164 var errs []error 165 price, err := strconv.ParseFloat(response.Data.CPM, 64) 166 if err != nil { 167 errs = append(errs, err) 168 } 169 width, err := strconv.ParseInt(response.Data.MediaType.Width, 10, 64) 170 if err != nil { 171 errs = append(errs, err) 172 } 173 height, err := strconv.ParseInt(response.Data.MediaType.Height, 10, 64) 174 if err != nil { 175 errs = append(errs, err) 176 } 177 178 if response.Data.MediaType.Name != openrtb_ext.BidTypeBanner { 179 return nil, 0, 0, 0, []error{fmt.Errorf("unsupported MediaType: %s", response.Data.MediaType.Name)} 180 } 181 182 if len(errs) > 0 { 183 return nil, 0, 0, 0, errs 184 } 185 return response.Data, price, width, height, nil 186 } 187 188 func getImpSizes(imp *openrtb2.Imp) string { 189 if imp.Banner == nil { 190 return "" 191 } 192 193 if len(imp.Banner.Format) > 0 { 194 sizes := make([]string, len(imp.Banner.Format)) 195 for i, format := range imp.Banner.Format { 196 sizes[i] = strconv.FormatInt(format.W, 10) + "x" + strconv.FormatInt(format.H, 10) 197 } 198 199 return strings.Join(sizes, ",") 200 } 201 202 if imp.Banner.W != nil && imp.Banner.H != nil { 203 return strconv.FormatInt(*imp.Banner.W, 10) + "x" + strconv.FormatInt(*imp.Banner.H, 10) 204 } 205 206 return "" 207 }