github.com/prebid/prebid-server/v2@v2.18.0/adapters/admixer/admixer.go (about) 1 package admixer 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 AdmixerAdapter struct { 16 endpoint string 17 } 18 19 // Builder builds a new instance of the Admixer adapter for the given bidder with the given config. 20 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 21 bidder := &AdmixerAdapter{ 22 endpoint: config.Endpoint, 23 } 24 return bidder, nil 25 } 26 27 type admixerImpExt struct { 28 CustomParams map[string]interface{} `json:"customParams"` 29 } 30 31 func (a *AdmixerAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) (requests []*adapters.RequestData, errors []error) { 32 rq, errs := a.makeRequest(request) 33 34 if len(errs) > 0 { 35 errors = append(errors, errs...) 36 return 37 } 38 39 if rq != nil { 40 requests = append(requests, rq) 41 } 42 43 return 44 } 45 46 func (a *AdmixerAdapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) { 47 var errs []error 48 var validImps []openrtb2.Imp 49 50 if len(request.Imp) == 0 { 51 return nil, []error{&errortypes.BadInput{ 52 Message: "No impressions in request", 53 }} 54 } 55 56 for _, imp := range request.Imp { 57 if err := preprocess(&imp); err != nil { 58 errs = append(errs, err) 59 continue 60 } 61 validImps = append(validImps, imp) 62 } 63 64 if len(validImps) == 0 { 65 return nil, errs 66 } 67 68 request.Imp = validImps 69 70 reqJSON, err := json.Marshal(request) 71 if err != nil { 72 errs = append(errs, err) 73 return nil, errs 74 } 75 76 headers := http.Header{} 77 headers.Add("Content-Type", "application/json;charset=utf-8") 78 headers.Add("Accept", "application/json") 79 return &adapters.RequestData{ 80 Method: "POST", 81 Uri: a.endpoint, 82 Body: reqJSON, 83 Headers: headers, 84 ImpIDs: openrtb_ext.GetImpIDs(request.Imp), 85 }, errs 86 } 87 88 func preprocess(imp *openrtb2.Imp) error { 89 var bidderExt adapters.ExtImpBidder 90 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 91 return &errortypes.BadInput{ 92 Message: err.Error(), 93 } 94 } 95 96 var admixerExt openrtb_ext.ExtImpAdmixer 97 if err := json.Unmarshal(bidderExt.Bidder, &admixerExt); err != nil { 98 return &errortypes.BadInput{ 99 Message: "Wrong Admixer bidder ext", 100 } 101 } 102 103 //don't use regexp due to possible performance reduce 104 if len(admixerExt.ZoneId) < 32 || len(admixerExt.ZoneId) > 36 { 105 return &errortypes.BadInput{ 106 Message: "ZoneId must be UUID/GUID", 107 } 108 } 109 110 imp.TagID = admixerExt.ZoneId 111 112 if imp.BidFloor == 0 && admixerExt.CustomBidFloor > 0 { 113 imp.BidFloor = admixerExt.CustomBidFloor 114 } 115 116 imp.Ext = nil 117 118 if admixerExt.CustomParams != nil { 119 impExt := admixerImpExt{ 120 CustomParams: admixerExt.CustomParams, 121 } 122 var err error 123 if imp.Ext, err = json.Marshal(impExt); err != nil { 124 return &errortypes.BadInput{ 125 Message: err.Error(), 126 } 127 } 128 } 129 130 return nil 131 } 132 133 func (a *AdmixerAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 134 if response.StatusCode == http.StatusNoContent { 135 return nil, nil 136 } 137 138 if response.StatusCode >= http.StatusInternalServerError { 139 return nil, []error{&errortypes.BadServerResponse{ 140 Message: fmt.Sprintf("Unexpected status code: %d. Dsp server internal error", response.StatusCode), 141 }} 142 } 143 144 if response.StatusCode >= http.StatusBadRequest { 145 return nil, []error{&errortypes.BadInput{ 146 Message: fmt.Sprintf("Unexpected status code: %d. Bad request to dsp", response.StatusCode), 147 }} 148 } 149 150 if response.StatusCode != http.StatusOK { 151 return nil, []error{&errortypes.BadServerResponse{ 152 Message: fmt.Sprintf("Unexpected status code: %d", response.StatusCode), 153 }} 154 } 155 156 var bidResp openrtb2.BidResponse 157 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 158 return nil, []error{err} 159 } 160 161 //additional no content check 162 if len(bidResp.SeatBid) == 0 || len(bidResp.SeatBid[0].Bid) == 0 { 163 return nil, nil 164 } 165 166 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) 167 168 for _, sb := range bidResp.SeatBid { 169 for i := range sb.Bid { 170 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 171 Bid: &sb.Bid[i], 172 BidType: getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp), 173 }) 174 } 175 } 176 return bidResponse, nil 177 } 178 179 func getMediaTypeForImp(impID string, imps []openrtb2.Imp) openrtb_ext.BidType { 180 for _, imp := range imps { 181 if imp.ID == impID { 182 if imp.Banner != nil { 183 return openrtb_ext.BidTypeBanner 184 } else if imp.Video != nil { 185 return openrtb_ext.BidTypeVideo 186 } else if imp.Native != nil { 187 return openrtb_ext.BidTypeNative 188 } else if imp.Audio != nil { 189 return openrtb_ext.BidTypeAudio 190 } 191 } 192 } 193 return openrtb_ext.BidTypeBanner 194 }