github.com/prebid/prebid-server/v2@v2.18.0/adapters/adtarget/adtarget.go (about) 1 package adtarget 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 8 "github.com/prebid/openrtb/v20/openrtb2" 9 "github.com/prebid/prebid-server/v2/adapters" 10 "github.com/prebid/prebid-server/v2/config" 11 "github.com/prebid/prebid-server/v2/errortypes" 12 "github.com/prebid/prebid-server/v2/openrtb_ext" 13 ) 14 15 type AdtargetAdapter struct { 16 endpoint string 17 } 18 19 type adtargetImpExt struct { 20 Adtarget openrtb_ext.ExtImpAdtarget `json:"adtarget"` 21 } 22 23 func (a *AdtargetAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 24 25 totalImps := len(request.Imp) 26 errors := make([]error, 0, totalImps) 27 imp2source := make(map[int][]int) 28 29 for i := 0; i < totalImps; i++ { 30 31 sourceId, err := validateImpressionAndSetExt(&request.Imp[i]) 32 33 if err != nil { 34 errors = append(errors, err) 35 continue 36 } 37 38 if _, ok := imp2source[sourceId]; !ok { 39 imp2source[sourceId] = make([]int, 0, totalImps-i) 40 } 41 42 imp2source[sourceId] = append(imp2source[sourceId], i) 43 44 } 45 46 totalReqs := len(imp2source) 47 if 0 == totalReqs { 48 return nil, errors 49 } 50 51 headers := http.Header{} 52 headers.Add("Content-Type", "application/json;charset=utf-8") 53 headers.Add("Accept", "application/json") 54 55 reqs := make([]*adapters.RequestData, 0, totalReqs) 56 57 imps := request.Imp 58 request.Imp = make([]openrtb2.Imp, 0, len(imps)) 59 for sourceId, impIndexes := range imp2source { 60 request.Imp = request.Imp[:0] 61 62 for i := 0; i < len(impIndexes); i++ { 63 request.Imp = append(request.Imp, imps[impIndexes[i]]) 64 } 65 66 body, err := json.Marshal(request) 67 if err != nil { 68 errors = append(errors, fmt.Errorf("error while encoding bidRequest, err: %s", err)) 69 return nil, errors 70 } 71 72 reqs = append(reqs, &adapters.RequestData{ 73 Method: "POST", 74 Uri: a.endpoint + fmt.Sprintf("?aid=%d", sourceId), 75 Body: body, 76 Headers: headers, 77 ImpIDs: openrtb_ext.GetImpIDs(request.Imp), 78 }) 79 } 80 81 return reqs, errors 82 } 83 84 func (a *AdtargetAdapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.RequestData, httpRes *adapters.ResponseData) (*adapters.BidderResponse, []error) { 85 86 if httpRes.StatusCode == http.StatusNoContent { 87 return nil, nil 88 } 89 if httpRes.StatusCode == http.StatusBadRequest { 90 return nil, []error{&errortypes.BadInput{ 91 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", httpRes.StatusCode), 92 }} 93 } 94 var bidResp openrtb2.BidResponse 95 if err := json.Unmarshal(httpRes.Body, &bidResp); err != nil { 96 return nil, []error{&errortypes.BadServerResponse{ 97 Message: fmt.Sprintf("error while decoding response, err: %s", err), 98 }} 99 } 100 101 bidResponse := adapters.NewBidderResponse() 102 var errors []error 103 104 var impOK bool 105 for _, sb := range bidResp.SeatBid { 106 for i := 0; i < len(sb.Bid); i++ { 107 108 bid := sb.Bid[i] 109 110 impOK = false 111 mediaType := openrtb_ext.BidTypeBanner 112 for _, imp := range bidReq.Imp { 113 if imp.ID == bid.ImpID { 114 115 impOK = true 116 117 if imp.Video != nil { 118 mediaType = openrtb_ext.BidTypeVideo 119 break 120 } 121 } 122 } 123 124 if !impOK { 125 errors = append(errors, &errortypes.BadServerResponse{ 126 Message: fmt.Sprintf("ignoring bid id=%s, request doesn't contain any impression with id=%s", bid.ID, bid.ImpID), 127 }) 128 continue 129 } 130 131 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 132 Bid: &bid, 133 BidType: mediaType, 134 }) 135 } 136 } 137 138 return bidResponse, errors 139 } 140 141 func validateImpressionAndSetExt(imp *openrtb2.Imp) (int, error) { 142 143 if imp.Banner == nil && imp.Video == nil { 144 return 0, &errortypes.BadInput{ 145 Message: fmt.Sprintf("ignoring imp id=%s, Adtarget supports only Video and Banner", imp.ID), 146 } 147 } 148 149 if 0 == len(imp.Ext) { 150 return 0, &errortypes.BadInput{ 151 Message: fmt.Sprintf("ignoring imp id=%s, extImpBidder is empty", imp.ID), 152 } 153 } 154 155 var bidderExt adapters.ExtImpBidder 156 157 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 158 return 0, &errortypes.BadInput{ 159 Message: fmt.Sprintf("ignoring imp id=%s, error while decoding extImpBidder, err: %s", imp.ID, err), 160 } 161 } 162 163 impExt := openrtb_ext.ExtImpAdtarget{} 164 err := json.Unmarshal(bidderExt.Bidder, &impExt) 165 if err != nil { 166 return 0, &errortypes.BadInput{ 167 Message: fmt.Sprintf("ignoring imp id=%s, error while decoding impExt, err: %s", imp.ID, err), 168 } 169 } 170 171 // common extension for all impressions 172 var impExtBuffer []byte 173 174 impExtBuffer, err = json.Marshal(&adtargetImpExt{ 175 Adtarget: impExt, 176 }) 177 178 if impExt.BidFloor > 0 { 179 imp.BidFloor = impExt.BidFloor 180 } 181 182 imp.Ext = impExtBuffer 183 184 return impExt.SourceId, nil 185 } 186 187 // Builder builds a new instance of the Adtarget adapter for the given bidder with the given config. 188 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 189 bidder := &AdtargetAdapter{ 190 endpoint: config.Endpoint, 191 } 192 return bidder, nil 193 }