github.com/prebid/prebid-server/v2@v2.18.0/adapters/beintoo/beintoo.go (about) 1 package beintoo 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strconv" 9 10 "github.com/prebid/openrtb/v20/openrtb2" 11 "github.com/prebid/prebid-server/v2/adapters" 12 "github.com/prebid/prebid-server/v2/config" 13 "github.com/prebid/prebid-server/v2/errortypes" 14 "github.com/prebid/prebid-server/v2/openrtb_ext" 15 ) 16 17 type BeintooAdapter struct { 18 endpoint string 19 } 20 21 func (a *BeintooAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 22 var errors []error 23 24 if len(request.Imp) == 0 { 25 return nil, []error{&errortypes.BadInput{ 26 Message: fmt.Sprintf("No Imps in Bid Request"), 27 }} 28 } 29 30 if errors := preprocess(request); len(errors) > 0 { 31 return nil, append(errors, &errortypes.BadInput{ 32 Message: fmt.Sprintf("Error in preprocess of Imp, err: %s", errors), 33 }) 34 } 35 36 data, err := json.Marshal(request) 37 if err != nil { 38 return nil, []error{&errortypes.BadInput{ 39 Message: fmt.Sprintf("Error in packaging request to JSON"), 40 }} 41 } 42 43 headers := http.Header{} 44 headers.Add("Content-Type", "application/json;charset=utf-8") 45 headers.Add("Accept", "application/json") 46 47 if request.Device != nil { 48 addHeaderIfNonEmpty(headers, "User-Agent", request.Device.UA) 49 addHeaderIfNonEmpty(headers, "X-Forwarded-For", request.Device.IP) 50 addHeaderIfNonEmpty(headers, "Accept-Language", request.Device.Language) 51 if request.Device.DNT != nil { 52 addHeaderIfNonEmpty(headers, "DNT", strconv.Itoa(int(*request.Device.DNT))) 53 } 54 } 55 if request.Site != nil { 56 addHeaderIfNonEmpty(headers, "Referer", request.Site.Page) 57 } 58 59 return []*adapters.RequestData{{ 60 Method: "POST", 61 Uri: a.endpoint, 62 Body: data, 63 Headers: headers, 64 ImpIDs: openrtb_ext.GetImpIDs(request.Imp), 65 }}, errors 66 } 67 68 func unpackImpExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpBeintoo, error) { 69 var bidderExt adapters.ExtImpBidder 70 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 71 return nil, &errortypes.BadInput{ 72 Message: err.Error(), 73 } 74 } 75 76 var beintooExt openrtb_ext.ExtImpBeintoo 77 if err := json.Unmarshal(bidderExt.Bidder, &beintooExt); err != nil { 78 return nil, &errortypes.BadInput{ 79 Message: fmt.Sprintf("ignoring imp id=%s, invalid ImpExt", imp.ID), 80 } 81 } 82 83 tagIDValidation, err := strconv.ParseInt(beintooExt.TagID, 10, 64) 84 if err != nil || tagIDValidation == 0 { 85 return nil, &errortypes.BadInput{ 86 Message: fmt.Sprintf("ignoring imp id=%s, invalid tagid must be a String of numbers", imp.ID), 87 } 88 } 89 90 return &beintooExt, nil 91 } 92 93 func buildImpBanner(imp *openrtb2.Imp) error { 94 imp.Ext = nil 95 96 if imp.Banner == nil { 97 return &errortypes.BadInput{ 98 Message: fmt.Sprintf("Request needs to include a Banner object"), 99 } 100 } 101 102 bannerCopy := *imp.Banner 103 banner := &bannerCopy 104 105 if banner.W == nil && banner.H == nil { 106 if len(banner.Format) == 0 { 107 return &errortypes.BadInput{ 108 Message: fmt.Sprintf("Need at least one size to build request"), 109 } 110 } 111 format := banner.Format[0] 112 banner.Format = banner.Format[1:] 113 banner.W = &format.W 114 banner.H = &format.H 115 imp.Banner = banner 116 } 117 118 return nil 119 } 120 121 // Add Beintoo required properties to Imp object 122 func addImpProps(imp *openrtb2.Imp, secure *int8, BeintooExt *openrtb_ext.ExtImpBeintoo) { 123 imp.TagID = BeintooExt.TagID 124 imp.Secure = secure 125 126 if BeintooExt.BidFloor != "" { 127 bidFloor, err := strconv.ParseFloat(BeintooExt.BidFloor, 64) 128 if err != nil { 129 bidFloor = 0 130 } 131 132 if bidFloor > 0 { 133 imp.BidFloor = bidFloor 134 } 135 } 136 } 137 138 // Adding header fields to request header 139 func addHeaderIfNonEmpty(headers http.Header, headerName string, headerValue string) { 140 if len(headerValue) > 0 { 141 headers.Add(headerName, headerValue) 142 } 143 } 144 145 // Handle request errors and formatting to be sent to Beintoo 146 func preprocess(request *openrtb2.BidRequest) []error { 147 errors := make([]error, 0, len(request.Imp)) 148 resImps := make([]openrtb2.Imp, 0, len(request.Imp)) 149 secure := int8(0) 150 151 if request.Site != nil && request.Site.Page != "" { 152 pageURL, err := url.Parse(request.Site.Page) 153 if err == nil && pageURL.Scheme == "https" { 154 secure = int8(1) 155 } 156 } 157 158 for _, imp := range request.Imp { 159 beintooExt, err := unpackImpExt(&imp) 160 if err != nil { 161 errors = append(errors, err) 162 return errors 163 } 164 165 addImpProps(&imp, &secure, beintooExt) 166 167 if err := buildImpBanner(&imp); err != nil { 168 errors = append(errors, err) 169 return errors 170 } 171 resImps = append(resImps, imp) 172 } 173 174 request.Imp = resImps 175 176 return errors 177 } 178 179 // MakeBids make the bids for the bid response. 180 func (a *BeintooAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 181 182 if response.StatusCode == http.StatusNoContent { 183 // no bid response 184 return nil, nil 185 } 186 187 if response.StatusCode != http.StatusOK { 188 return nil, []error{&errortypes.BadServerResponse{ 189 Message: fmt.Sprintf("Invalid Status Returned: %d. Run with request.debug = 1 for more info", response.StatusCode), 190 }} 191 } 192 193 var bidResp openrtb2.BidResponse 194 195 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 196 return nil, []error{&errortypes.BadServerResponse{ 197 Message: fmt.Sprintf("Unable to unpackage bid response. Error: %s", err.Error()), 198 }} 199 } 200 201 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 202 203 for _, sb := range bidResp.SeatBid { 204 for i := range sb.Bid { 205 sb.Bid[i].ImpID = sb.Bid[i].ID 206 207 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 208 Bid: &sb.Bid[i], 209 BidType: "banner", 210 }) 211 } 212 } 213 214 return bidResponse, nil 215 216 } 217 218 // Builder builds a new instance of the Beintoo adapter for the given bidder with the given config. 219 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 220 bidder := &BeintooAdapter{ 221 endpoint: config.Endpoint, 222 } 223 return bidder, nil 224 }