github.com/prebid/prebid-server@v0.275.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/v19/openrtb2" 9 "github.com/prebid/prebid-server/adapters" 10 "github.com/prebid/prebid-server/config" 11 "github.com/prebid/prebid-server/errortypes" 12 "github.com/prebid/prebid-server/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 }) 78 } 79 80 return reqs, errors 81 } 82 83 func (a *AdtargetAdapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.RequestData, httpRes *adapters.ResponseData) (*adapters.BidderResponse, []error) { 84 85 if httpRes.StatusCode == http.StatusNoContent { 86 return nil, nil 87 } 88 if httpRes.StatusCode == http.StatusBadRequest { 89 return nil, []error{&errortypes.BadInput{ 90 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", httpRes.StatusCode), 91 }} 92 } 93 var bidResp openrtb2.BidResponse 94 if err := json.Unmarshal(httpRes.Body, &bidResp); err != nil { 95 return nil, []error{&errortypes.BadServerResponse{ 96 Message: fmt.Sprintf("error while decoding response, err: %s", err), 97 }} 98 } 99 100 bidResponse := adapters.NewBidderResponse() 101 var errors []error 102 103 var impOK bool 104 for _, sb := range bidResp.SeatBid { 105 for i := 0; i < len(sb.Bid); i++ { 106 107 bid := sb.Bid[i] 108 109 impOK = false 110 mediaType := openrtb_ext.BidTypeBanner 111 for _, imp := range bidReq.Imp { 112 if imp.ID == bid.ImpID { 113 114 impOK = true 115 116 if imp.Video != nil { 117 mediaType = openrtb_ext.BidTypeVideo 118 break 119 } 120 } 121 } 122 123 if !impOK { 124 errors = append(errors, &errortypes.BadServerResponse{ 125 Message: fmt.Sprintf("ignoring bid id=%s, request doesn't contain any impression with id=%s", bid.ID, bid.ImpID), 126 }) 127 continue 128 } 129 130 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 131 Bid: &bid, 132 BidType: mediaType, 133 }) 134 } 135 } 136 137 return bidResponse, errors 138 } 139 140 func validateImpressionAndSetExt(imp *openrtb2.Imp) (int, error) { 141 142 if imp.Banner == nil && imp.Video == nil { 143 return 0, &errortypes.BadInput{ 144 Message: fmt.Sprintf("ignoring imp id=%s, Adtarget supports only Video and Banner", imp.ID), 145 } 146 } 147 148 if 0 == len(imp.Ext) { 149 return 0, &errortypes.BadInput{ 150 Message: fmt.Sprintf("ignoring imp id=%s, extImpBidder is empty", imp.ID), 151 } 152 } 153 154 var bidderExt adapters.ExtImpBidder 155 156 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 157 return 0, &errortypes.BadInput{ 158 Message: fmt.Sprintf("ignoring imp id=%s, error while decoding extImpBidder, err: %s", imp.ID, err), 159 } 160 } 161 162 impExt := openrtb_ext.ExtImpAdtarget{} 163 err := json.Unmarshal(bidderExt.Bidder, &impExt) 164 if err != nil { 165 return 0, &errortypes.BadInput{ 166 Message: fmt.Sprintf("ignoring imp id=%s, error while decoding impExt, err: %s", imp.ID, err), 167 } 168 } 169 170 // common extension for all impressions 171 var impExtBuffer []byte 172 173 impExtBuffer, err = json.Marshal(&adtargetImpExt{ 174 Adtarget: impExt, 175 }) 176 177 if impExt.BidFloor > 0 { 178 imp.BidFloor = impExt.BidFloor 179 } 180 181 imp.Ext = impExtBuffer 182 183 return impExt.SourceId, nil 184 } 185 186 // Builder builds a new instance of the Adtarget adapter for the given bidder with the given config. 187 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 188 bidder := &AdtargetAdapter{ 189 endpoint: config.Endpoint, 190 } 191 return bidder, nil 192 }