github.com/prebid/prebid-server/v2@v2.18.0/adapters/undertone/undertone.go (about) 1 package undertone 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strconv" 8 9 "github.com/prebid/openrtb/v20/openrtb2" 10 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 const adapterId = 4 18 const adapterVersion = "1.0.0" 19 20 type adapter struct { 21 endpoint string 22 } 23 24 type undertoneParams struct { 25 Id int `json:"id"` 26 Version string `json:"version"` 27 } 28 29 type impExt struct { 30 Bidder *openrtb_ext.ExtImpUndertone `json:"bidder,omitempty"` 31 Gpid string `json:"gpid,omitempty"` 32 } 33 34 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 35 bidder := &adapter{ 36 endpoint: config.Endpoint, 37 } 38 return bidder, nil 39 } 40 41 func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 42 imps, publisherId, errs := getImpsAndPublisherId(request) 43 if len(imps) == 0 { 44 return nil, errs 45 } 46 47 reqCopy := *request 48 reqCopy.Imp = imps 49 50 populateSiteApp(&reqCopy, publisherId, request.Site, request.App) 51 e := populateBidReqExt(&reqCopy) 52 if e != nil { 53 errs = append(errs, e) 54 } 55 56 requestJSON, err := json.Marshal(reqCopy) 57 if err != nil { 58 return nil, append(errs, err) 59 } 60 61 requestData := &adapters.RequestData{ 62 Method: "POST", 63 Uri: a.endpoint, 64 Body: requestJSON, 65 ImpIDs: openrtb_ext.GetImpIDs(reqCopy.Imp), 66 } 67 68 return []*adapters.RequestData{requestData}, errs 69 } 70 71 func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { 72 if responseData.StatusCode == http.StatusNoContent { 73 return nil, nil 74 } 75 76 if responseData.StatusCode == http.StatusBadRequest { 77 err := &errortypes.BadInput{ 78 Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.", 79 } 80 return nil, []error{err} 81 } 82 83 if responseData.StatusCode != http.StatusOK { 84 err := &errortypes.BadServerResponse{ 85 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode), 86 } 87 return nil, []error{err} 88 } 89 90 var response openrtb2.BidResponse 91 if err := json.Unmarshal(responseData.Body, &response); err != nil { 92 return nil, []error{err} 93 } 94 95 impIdBidTypeMap := map[string]openrtb_ext.BidType{} 96 for _, imp := range request.Imp { 97 if imp.Banner != nil { 98 impIdBidTypeMap[imp.ID] = openrtb_ext.BidTypeBanner 99 } else if imp.Video != nil { 100 impIdBidTypeMap[imp.ID] = openrtb_ext.BidTypeVideo 101 } 102 } 103 104 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) 105 bidResponse.Currency = response.Cur 106 for _, seatBid := range response.SeatBid { 107 for i, bid := range seatBid.Bid { 108 bidType, ok := impIdBidTypeMap[bid.ImpID] 109 if !ok { 110 continue 111 } 112 113 b := &adapters.TypedBid{ 114 Bid: &seatBid.Bid[i], 115 BidType: bidType, 116 } 117 bidResponse.Bids = append(bidResponse.Bids, b) 118 } 119 } 120 return bidResponse, nil 121 } 122 123 func populateBidReqExt(bidRequest *openrtb2.BidRequest) error { 124 undertoneParams := &undertoneParams{ 125 Id: adapterId, 126 Version: adapterVersion, 127 } 128 129 undertoneParamsJSON, err := json.Marshal(undertoneParams) 130 if err != nil { 131 return err 132 } 133 134 bidRequest.Ext = undertoneParamsJSON 135 return nil 136 } 137 138 func populateSiteApp(bidRequest *openrtb2.BidRequest, publisherId int, site *openrtb2.Site, app *openrtb2.App) { 139 pubId := strconv.Itoa(publisherId) 140 if site != nil { 141 siteCopy := *site 142 var publisher openrtb2.Publisher 143 if siteCopy.Publisher != nil { 144 publisher = *siteCopy.Publisher 145 } 146 publisher.ID = pubId 147 bidRequest.Site = &siteCopy 148 bidRequest.Site.Publisher = &publisher 149 } else if app != nil { 150 appCopy := *app 151 var publisher openrtb2.Publisher 152 if appCopy.Publisher != nil { 153 publisher = *appCopy.Publisher 154 } 155 publisher.ID = pubId 156 bidRequest.App = &appCopy 157 bidRequest.App.Publisher = &publisher 158 } 159 } 160 161 func getImpsAndPublisherId(bidRequest *openrtb2.BidRequest) ([]openrtb2.Imp, int, []error) { 162 var errs []error 163 var publisherId int 164 var validImps []openrtb2.Imp 165 166 for _, imp := range bidRequest.Imp { 167 var ext impExt 168 if err := json.Unmarshal(imp.Ext, &ext); err != nil { 169 errs = append(errs, getInvalidImpErr(imp.ID, err)) 170 continue 171 } 172 173 if publisherId == 0 { 174 publisherId = ext.Bidder.PublisherID 175 } 176 177 imp.TagID = strconv.Itoa(ext.Bidder.PlacementID) 178 imp.Ext = nil 179 180 if ext.Gpid != "" { 181 ext.Bidder = nil 182 impExtJson, err := json.Marshal(&ext) 183 if err != nil { 184 errs = append(errs, getInvalidImpErr(imp.ID, err)) 185 continue 186 } 187 imp.Ext = impExtJson 188 } 189 190 validImps = append(validImps, imp) 191 } 192 193 return validImps, publisherId, errs 194 } 195 196 func getInvalidImpErr(impId string, err error) *errortypes.BadInput { 197 return &errortypes.BadInput{ 198 Message: "Invalid impid=" + impId + ": " + err.Error(), 199 } 200 }