github.com/prebid/prebid-server@v0.275.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/v19/openrtb2" 11 "github.com/prebid/prebid-server/adapters" 12 "github.com/prebid/prebid-server/config" 13 "github.com/prebid/prebid-server/errortypes" 14 "github.com/prebid/prebid-server/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); errors != nil && 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 }}, errors 65 } 66 67 func unpackImpExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpBeintoo, error) { 68 var bidderExt adapters.ExtImpBidder 69 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 70 return nil, &errortypes.BadInput{ 71 Message: err.Error(), 72 } 73 } 74 75 var beintooExt openrtb_ext.ExtImpBeintoo 76 if err := json.Unmarshal(bidderExt.Bidder, &beintooExt); err != nil { 77 return nil, &errortypes.BadInput{ 78 Message: fmt.Sprintf("ignoring imp id=%s, invalid ImpExt", imp.ID), 79 } 80 } 81 82 tagIDValidation, err := strconv.ParseInt(beintooExt.TagID, 10, 64) 83 if err != nil || tagIDValidation == 0 { 84 return nil, &errortypes.BadInput{ 85 Message: fmt.Sprintf("ignoring imp id=%s, invalid tagid must be a String of numbers", imp.ID), 86 } 87 } 88 89 return &beintooExt, nil 90 } 91 92 func buildImpBanner(imp *openrtb2.Imp) error { 93 imp.Ext = nil 94 95 if imp.Banner == nil { 96 return &errortypes.BadInput{ 97 Message: fmt.Sprintf("Request needs to include a Banner object"), 98 } 99 } 100 101 bannerCopy := *imp.Banner 102 banner := &bannerCopy 103 104 if banner.W == nil && banner.H == nil { 105 if len(banner.Format) == 0 { 106 return &errortypes.BadInput{ 107 Message: fmt.Sprintf("Need at least one size to build request"), 108 } 109 } 110 format := banner.Format[0] 111 banner.Format = banner.Format[1:] 112 banner.W = &format.W 113 banner.H = &format.H 114 imp.Banner = banner 115 } 116 117 return nil 118 } 119 120 // Add Beintoo required properties to Imp object 121 func addImpProps(imp *openrtb2.Imp, secure *int8, BeintooExt *openrtb_ext.ExtImpBeintoo) { 122 imp.TagID = BeintooExt.TagID 123 imp.Secure = secure 124 125 if BeintooExt.BidFloor != "" { 126 bidFloor, err := strconv.ParseFloat(BeintooExt.BidFloor, 64) 127 if err != nil { 128 bidFloor = 0 129 } 130 131 if bidFloor > 0 { 132 imp.BidFloor = bidFloor 133 } 134 } 135 136 return 137 } 138 139 // Adding header fields to request header 140 func addHeaderIfNonEmpty(headers http.Header, headerName string, headerValue string) { 141 if len(headerValue) > 0 { 142 headers.Add(headerName, headerValue) 143 } 144 } 145 146 // Handle request errors and formatting to be sent to Beintoo 147 func preprocess(request *openrtb2.BidRequest) []error { 148 errors := make([]error, 0, len(request.Imp)) 149 resImps := make([]openrtb2.Imp, 0, len(request.Imp)) 150 secure := int8(0) 151 152 if request.Site != nil && request.Site.Page != "" { 153 pageURL, err := url.Parse(request.Site.Page) 154 if err == nil && pageURL.Scheme == "https" { 155 secure = int8(1) 156 } 157 } 158 159 for _, imp := range request.Imp { 160 beintooExt, err := unpackImpExt(&imp) 161 if err != nil { 162 errors = append(errors, err) 163 return errors 164 } 165 166 addImpProps(&imp, &secure, beintooExt) 167 168 if err := buildImpBanner(&imp); err != nil { 169 errors = append(errors, err) 170 return errors 171 } 172 resImps = append(resImps, imp) 173 } 174 175 request.Imp = resImps 176 177 return errors 178 } 179 180 // MakeBids make the bids for the bid response. 181 func (a *BeintooAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 182 183 if response.StatusCode == http.StatusNoContent { 184 // no bid response 185 return nil, nil 186 } 187 188 if response.StatusCode != http.StatusOK { 189 return nil, []error{&errortypes.BadServerResponse{ 190 Message: fmt.Sprintf("Invalid Status Returned: %d. Run with request.debug = 1 for more info", response.StatusCode), 191 }} 192 } 193 194 var bidResp openrtb2.BidResponse 195 196 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 197 return nil, []error{&errortypes.BadServerResponse{ 198 Message: fmt.Sprintf("Unable to unpackage bid response. Error: %s", err.Error()), 199 }} 200 } 201 202 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 203 204 for _, sb := range bidResp.SeatBid { 205 for i := range sb.Bid { 206 sb.Bid[i].ImpID = sb.Bid[i].ID 207 208 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 209 Bid: &sb.Bid[i], 210 BidType: "banner", 211 }) 212 } 213 } 214 215 return bidResponse, nil 216 217 } 218 219 // Builder builds a new instance of the Beintoo adapter for the given bidder with the given config. 220 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 221 bidder := &BeintooAdapter{ 222 endpoint: config.Endpoint, 223 } 224 return bidder, nil 225 }