github.com/prebid/prebid-server@v0.275.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/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 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 }, errs 85 } 86 87 func preprocess(imp *openrtb2.Imp) error { 88 var bidderExt adapters.ExtImpBidder 89 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 90 return &errortypes.BadInput{ 91 Message: err.Error(), 92 } 93 } 94 95 var admixerExt openrtb_ext.ExtImpAdmixer 96 if err := json.Unmarshal(bidderExt.Bidder, &admixerExt); err != nil { 97 return &errortypes.BadInput{ 98 Message: "Wrong Admixer bidder ext", 99 } 100 } 101 102 //don't use regexp due to possible performance reduce 103 if len(admixerExt.ZoneId) < 32 || len(admixerExt.ZoneId) > 36 { 104 return &errortypes.BadInput{ 105 Message: "ZoneId must be UUID/GUID", 106 } 107 } 108 109 imp.TagID = admixerExt.ZoneId 110 111 if imp.BidFloor == 0 && admixerExt.CustomBidFloor > 0 { 112 imp.BidFloor = admixerExt.CustomBidFloor 113 } 114 115 imp.Ext = nil 116 117 if admixerExt.CustomParams != nil { 118 impExt := admixerImpExt{ 119 CustomParams: admixerExt.CustomParams, 120 } 121 var err error 122 if imp.Ext, err = json.Marshal(impExt); err != nil { 123 return &errortypes.BadInput{ 124 Message: err.Error(), 125 } 126 } 127 } 128 129 return nil 130 } 131 132 func (a *AdmixerAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 133 if response.StatusCode == http.StatusNoContent { 134 return nil, nil 135 } 136 137 if response.StatusCode >= http.StatusInternalServerError { 138 return nil, []error{&errortypes.BadServerResponse{ 139 Message: fmt.Sprintf("Unexpected status code: %d. Dsp server internal error", response.StatusCode), 140 }} 141 } 142 143 if response.StatusCode >= http.StatusBadRequest { 144 return nil, []error{&errortypes.BadInput{ 145 Message: fmt.Sprintf("Unexpected status code: %d. Bad request to dsp", response.StatusCode), 146 }} 147 } 148 149 if response.StatusCode != http.StatusOK { 150 return nil, []error{&errortypes.BadServerResponse{ 151 Message: fmt.Sprintf("Unexpected status code: %d", response.StatusCode), 152 }} 153 } 154 155 var bidResp openrtb2.BidResponse 156 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 157 return nil, []error{err} 158 } 159 160 //additional no content check 161 if len(bidResp.SeatBid) == 0 || len(bidResp.SeatBid[0].Bid) == 0 { 162 return nil, nil 163 } 164 165 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) 166 167 for _, sb := range bidResp.SeatBid { 168 for i := range sb.Bid { 169 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 170 Bid: &sb.Bid[i], 171 BidType: getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp), 172 }) 173 } 174 } 175 return bidResponse, nil 176 } 177 178 func getMediaTypeForImp(impID string, imps []openrtb2.Imp) openrtb_ext.BidType { 179 for _, imp := range imps { 180 if imp.ID == impID { 181 if imp.Banner != nil { 182 return openrtb_ext.BidTypeBanner 183 } else if imp.Video != nil { 184 return openrtb_ext.BidTypeVideo 185 } else if imp.Native != nil { 186 return openrtb_ext.BidTypeNative 187 } else if imp.Audio != nil { 188 return openrtb_ext.BidTypeAudio 189 } 190 } 191 } 192 return openrtb_ext.BidTypeBanner 193 }