github.com/prebid/prebid-server@v0.275.0/adapters/zeroclickfraud/zeroclickfraud.go (about) 1 package zeroclickfraud 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strconv" 8 "text/template" 9 10 "github.com/prebid/openrtb/v19/openrtb2" 11 "github.com/prebid/prebid-server/adapters" 12 "github.com/prebid/prebid-server/config" 13 "github.com/prebid/prebid-server/errortypes" 14 "github.com/prebid/prebid-server/macros" 15 "github.com/prebid/prebid-server/openrtb_ext" 16 ) 17 18 type ZeroClickFraudAdapter struct { 19 EndpointTemplate *template.Template 20 } 21 22 func (a *ZeroClickFraudAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 23 24 errs := make([]error, 0, len(request.Imp)) 25 headers := http.Header{ 26 "Content-Type": {"application/json"}, 27 "Accept": {"application/json"}, 28 } 29 30 // Pull the host and source ID info from the bidder params. 31 reqImps, err := splitImpressions(request.Imp) 32 33 if err != nil { 34 errs = append(errs, err) 35 } 36 37 requests := []*adapters.RequestData{} 38 39 for reqExt, reqImp := range reqImps { 40 request.Imp = reqImp 41 reqJson, err := json.Marshal(request) 42 43 if err != nil { 44 errs = append(errs, err) 45 continue 46 } 47 48 urlParams := macros.EndpointTemplateParams{Host: reqExt.Host, SourceId: strconv.Itoa(reqExt.SourceId)} 49 url, err := macros.ResolveMacros(a.EndpointTemplate, urlParams) 50 51 if err != nil { 52 errs = append(errs, err) 53 continue 54 } 55 56 request := adapters.RequestData{ 57 Method: "POST", 58 Uri: url, 59 Body: reqJson, 60 Headers: headers} 61 62 requests = append(requests, &request) 63 } 64 65 return requests, errs 66 } 67 68 /* 69 internal original request in OpenRTB, external = result of us having converted it (what comes out of MakeRequests) 70 */ 71 func (a *ZeroClickFraudAdapter) MakeBids( 72 internalRequest *openrtb2.BidRequest, 73 externalRequest *adapters.RequestData, 74 response *adapters.ResponseData, 75 ) (*adapters.BidderResponse, []error) { 76 77 if response.StatusCode == http.StatusNoContent { 78 return nil, nil 79 } 80 81 if response.StatusCode == http.StatusBadRequest { 82 return nil, []error{&errortypes.BadInput{ 83 Message: fmt.Sprintf("ERR, bad input %d", response.StatusCode), 84 }} 85 } else if response.StatusCode != http.StatusOK { 86 return nil, []error{&errortypes.BadServerResponse{ 87 Message: fmt.Sprintf("ERR, response with status %d", response.StatusCode), 88 }} 89 } 90 91 var bidResp openrtb2.BidResponse 92 93 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 94 return nil, []error{err} 95 } 96 97 bidResponse := adapters.NewBidderResponse() 98 bidResponse.Currency = bidResp.Cur 99 100 for _, seatBid := range bidResp.SeatBid { 101 for i := 0; i < len(seatBid.Bid); i++ { 102 bid := seatBid.Bid[i] 103 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 104 Bid: &bid, 105 BidType: getMediaType(bid.ImpID, internalRequest.Imp), 106 }) 107 } 108 } 109 110 return bidResponse, nil 111 } 112 113 func splitImpressions(imps []openrtb2.Imp) (map[openrtb_ext.ExtImpZeroClickFraud][]openrtb2.Imp, error) { 114 115 var m = make(map[openrtb_ext.ExtImpZeroClickFraud][]openrtb2.Imp) 116 117 for _, imp := range imps { 118 bidderParams, err := getBidderParams(&imp) 119 if err != nil { 120 return nil, err 121 } 122 123 m[*bidderParams] = append(m[*bidderParams], imp) 124 } 125 126 return m, nil 127 } 128 129 func getBidderParams(imp *openrtb2.Imp) (*openrtb_ext.ExtImpZeroClickFraud, error) { 130 var bidderExt adapters.ExtImpBidder 131 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 132 return nil, &errortypes.BadInput{ 133 Message: fmt.Sprintf("Missing bidder ext: %s", err.Error()), 134 } 135 } 136 var zeroclickfraudExt openrtb_ext.ExtImpZeroClickFraud 137 if err := json.Unmarshal(bidderExt.Bidder, &zeroclickfraudExt); err != nil { 138 return nil, &errortypes.BadInput{ 139 Message: fmt.Sprintf("Cannot Resolve host or sourceId: %s", err.Error()), 140 } 141 } 142 143 if zeroclickfraudExt.SourceId < 1 { 144 return nil, &errortypes.BadInput{ 145 Message: "Invalid/Missing SourceId", 146 } 147 } 148 149 if len(zeroclickfraudExt.Host) < 1 { 150 return nil, &errortypes.BadInput{ 151 Message: "Invalid/Missing Host", 152 } 153 } 154 155 return &zeroclickfraudExt, nil 156 } 157 158 func getMediaType(impID string, imps []openrtb2.Imp) openrtb_ext.BidType { 159 160 bidType := openrtb_ext.BidTypeBanner 161 162 for _, imp := range imps { 163 if imp.ID == impID { 164 if imp.Video != nil { 165 bidType = openrtb_ext.BidTypeVideo 166 break 167 } else if imp.Native != nil { 168 bidType = openrtb_ext.BidTypeNative 169 break 170 } else { 171 bidType = openrtb_ext.BidTypeBanner 172 break 173 } 174 } 175 } 176 177 return bidType 178 } 179 180 // Builder builds a new instance of the AppNexus adapter for the given bidder with the given config. 181 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 182 template, err := template.New("endpointTemplate").Parse(config.Endpoint) 183 if err != nil { 184 return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) 185 } 186 187 bidder := &ZeroClickFraudAdapter{ 188 EndpointTemplate: template, 189 } 190 return bidder, nil 191 }