github.com/prebid/prebid-server@v0.275.0/adapters/gumgum/gumgum.go (about) 1 package gumgum 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strconv" 8 "strings" 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 // GumGumAdapter implements Bidder interface. 18 type GumGumAdapter struct { 19 URI string 20 } 21 22 // MakeRequests makes the HTTP requests which should be made to fetch bids. 23 func (g *GumGumAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 24 var validImps []openrtb2.Imp 25 var siteCopy openrtb2.Site 26 if request.Site != nil { 27 siteCopy = *request.Site 28 } 29 30 numRequests := len(request.Imp) 31 errs := make([]error, 0, numRequests) 32 33 for i := 0; i < numRequests; i++ { 34 imp := request.Imp[i] 35 gumgumExt, err := preprocess(&imp) 36 if err != nil { 37 errs = append(errs, err) 38 } else { 39 if gumgumExt.Zone != "" { 40 siteCopy.ID = gumgumExt.Zone 41 } 42 43 if gumgumExt.PubID != 0 { 44 if siteCopy.Publisher != nil { 45 siteCopy.Publisher.ID = strconv.FormatFloat(gumgumExt.PubID, 'f', -1, 64) 46 } else { 47 siteCopy.Publisher = &openrtb2.Publisher{ID: strconv.FormatFloat(gumgumExt.PubID, 'f', -1, 64)} 48 } 49 } 50 51 validImps = append(validImps, imp) 52 } 53 } 54 55 if len(validImps) == 0 { 56 return nil, errs 57 } 58 59 request.Imp = validImps 60 61 if request.Site != nil { 62 request.Site = &siteCopy 63 } 64 65 reqJSON, err := json.Marshal(request) 66 if err != nil { 67 errs = append(errs, err) 68 return nil, errs 69 } 70 71 headers := http.Header{} 72 headers.Add("Content-Type", "application/json;charset=utf-8") 73 headers.Add("Accept", "application/json") 74 return []*adapters.RequestData{{ 75 Method: "POST", 76 Uri: g.URI, 77 Body: reqJSON, 78 Headers: headers, 79 }}, errs 80 } 81 82 // MakeBids unpacks the server's response into Bids. 83 func (g *GumGumAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 84 if response.StatusCode == http.StatusNoContent { 85 return nil, nil 86 } 87 88 if response.StatusCode == http.StatusBadRequest { 89 return nil, []error{&errortypes.BadInput{ 90 Message: fmt.Sprintf("Bad user input: HTTP status %d", response.StatusCode), 91 }} 92 } 93 94 if response.StatusCode != http.StatusOK { 95 return nil, []error{&errortypes.BadServerResponse{ 96 Message: fmt.Sprintf("Bad server response: HTTP status %d", response.StatusCode), 97 }} 98 } 99 var bidResp openrtb2.BidResponse 100 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 101 return nil, []error{&errortypes.BadServerResponse{ 102 Message: fmt.Sprintf("Bad server response: %d. ", err), 103 }} 104 } 105 106 var errs []error 107 bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) 108 109 for _, sb := range bidResp.SeatBid { 110 for i := range sb.Bid { 111 mediaType := getMediaTypeForImpID(sb.Bid[i].ImpID, internalRequest.Imp) 112 if mediaType == openrtb_ext.BidTypeVideo { 113 price := strconv.FormatFloat(sb.Bid[i].Price, 'f', -1, 64) 114 sb.Bid[i].AdM = strings.Replace(sb.Bid[i].AdM, "${AUCTION_PRICE}", price, -1) 115 } 116 117 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 118 Bid: &sb.Bid[i], 119 BidType: mediaType, 120 }) 121 } 122 } 123 124 return bidResponse, errs 125 } 126 127 func preprocess(imp *openrtb2.Imp) (*openrtb_ext.ExtImpGumGum, error) { 128 var bidderExt adapters.ExtImpBidder 129 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 130 err = &errortypes.BadInput{ 131 Message: err.Error(), 132 } 133 return nil, err 134 } 135 136 var gumgumExt openrtb_ext.ExtImpGumGum 137 if err := json.Unmarshal(bidderExt.Bidder, &gumgumExt); err != nil { 138 err = &errortypes.BadInput{ 139 Message: err.Error(), 140 } 141 return nil, err 142 } 143 144 if imp.Banner != nil && imp.Banner.W == nil && imp.Banner.H == nil && len(imp.Banner.Format) > 0 { 145 bannerCopy := *imp.Banner 146 format := bannerCopy.Format[0] 147 bannerCopy.W = &(format.W) 148 bannerCopy.H = &(format.H) 149 150 if gumgumExt.Slot != 0 { 151 var err error 152 bannerExt := getBiggerFormat(bannerCopy.Format, gumgumExt.Slot) 153 bannerCopy.Ext, err = json.Marshal(&bannerExt) 154 if err != nil { 155 return nil, err 156 } 157 } 158 159 imp.Banner = &bannerCopy 160 } 161 162 if imp.Video != nil { 163 err := validateVideoParams(imp.Video) 164 if err != nil { 165 return nil, err 166 } 167 168 if gumgumExt.IrisID != "" { 169 videoCopy := *imp.Video 170 videoExt := openrtb_ext.ExtImpGumGumVideo{IrisID: gumgumExt.IrisID} 171 videoCopy.Ext, err = json.Marshal(&videoExt) 172 if err != nil { 173 return nil, err 174 } 175 imp.Video = &videoCopy 176 } 177 } 178 179 return &gumgumExt, nil 180 } 181 182 func getBiggerFormat(formatList []openrtb2.Format, slot float64) openrtb_ext.ExtImpGumGumBanner { 183 maxw := int64(0) 184 maxh := int64(0) 185 greatestVal := int64(0) 186 for _, size := range formatList { 187 var biggerSide int64 188 if size.W > size.H { 189 biggerSide = size.W 190 } else { 191 biggerSide = size.H 192 } 193 194 if biggerSide > greatestVal || (biggerSide == greatestVal && size.W >= maxw && size.H >= maxh) { 195 greatestVal = biggerSide 196 maxh = size.H 197 maxw = size.W 198 } 199 } 200 201 bannerExt := openrtb_ext.ExtImpGumGumBanner{Si: slot, MaxW: float64(maxw), MaxH: float64(maxh)} 202 203 return bannerExt 204 } 205 206 func getMediaTypeForImpID(impID string, imps []openrtb2.Imp) openrtb_ext.BidType { 207 for _, imp := range imps { 208 if imp.ID == impID && imp.Banner != nil { 209 return openrtb_ext.BidTypeBanner 210 } 211 } 212 return openrtb_ext.BidTypeVideo 213 } 214 215 func validateVideoParams(video *openrtb2.Video) (err error) { 216 if video.W == 0 || video.H == 0 || video.MinDuration == 0 || video.MaxDuration == 0 || video.Placement == 0 || video.Linearity == 0 { 217 return &errortypes.BadInput{ 218 Message: "Invalid or missing video field(s)", 219 } 220 } 221 return nil 222 } 223 224 // Builder builds a new instance of the GumGum adapter for the given bidder with the given config. 225 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 226 bidder := &GumGumAdapter{ 227 URI: config.Endpoint, 228 } 229 return bidder, nil 230 }