github.com/prebid/prebid-server/v2@v2.18.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/v20/openrtb2" 11 "github.com/prebid/prebid-server/v2/adapters" 12 "github.com/prebid/prebid-server/v2/config" 13 "github.com/prebid/prebid-server/v2/errortypes" 14 "github.com/prebid/prebid-server/v2/macros" 15 "github.com/prebid/prebid-server/v2/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 ImpIDs: openrtb_ext.GetImpIDs(request.Imp), 62 } 63 64 requests = append(requests, &request) 65 } 66 67 return requests, errs 68 } 69 70 /* 71 internal original request in OpenRTB, external = result of us having converted it (what comes out of MakeRequests) 72 */ 73 func (a *ZeroClickFraudAdapter) MakeBids( 74 internalRequest *openrtb2.BidRequest, 75 externalRequest *adapters.RequestData, 76 response *adapters.ResponseData, 77 ) (*adapters.BidderResponse, []error) { 78 79 if response.StatusCode == http.StatusNoContent { 80 return nil, nil 81 } 82 83 if response.StatusCode == http.StatusBadRequest { 84 return nil, []error{&errortypes.BadInput{ 85 Message: fmt.Sprintf("ERR, bad input %d", response.StatusCode), 86 }} 87 } else if response.StatusCode != http.StatusOK { 88 return nil, []error{&errortypes.BadServerResponse{ 89 Message: fmt.Sprintf("ERR, response with status %d", response.StatusCode), 90 }} 91 } 92 93 var bidResp openrtb2.BidResponse 94 95 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 96 return nil, []error{err} 97 } 98 99 bidResponse := adapters.NewBidderResponse() 100 bidResponse.Currency = bidResp.Cur 101 102 for _, seatBid := range bidResp.SeatBid { 103 for i := 0; i < len(seatBid.Bid); i++ { 104 bid := seatBid.Bid[i] 105 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 106 Bid: &bid, 107 BidType: getMediaType(bid.ImpID, internalRequest.Imp), 108 }) 109 } 110 } 111 112 return bidResponse, nil 113 } 114 115 func splitImpressions(imps []openrtb2.Imp) (map[openrtb_ext.ExtImpZeroClickFraud][]openrtb2.Imp, error) { 116 117 var m = make(map[openrtb_ext.ExtImpZeroClickFraud][]openrtb2.Imp) 118 119 for _, imp := range imps { 120 bidderParams, err := getBidderParams(&imp) 121 if err != nil { 122 return nil, err 123 } 124 125 m[*bidderParams] = append(m[*bidderParams], imp) 126 } 127 128 return m, nil 129 } 130 131 func getBidderParams(imp *openrtb2.Imp) (*openrtb_ext.ExtImpZeroClickFraud, error) { 132 var bidderExt adapters.ExtImpBidder 133 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 134 return nil, &errortypes.BadInput{ 135 Message: fmt.Sprintf("Missing bidder ext: %s", err.Error()), 136 } 137 } 138 var zeroclickfraudExt openrtb_ext.ExtImpZeroClickFraud 139 if err := json.Unmarshal(bidderExt.Bidder, &zeroclickfraudExt); err != nil { 140 return nil, &errortypes.BadInput{ 141 Message: fmt.Sprintf("Cannot Resolve host or sourceId: %s", err.Error()), 142 } 143 } 144 145 if zeroclickfraudExt.SourceId < 1 { 146 return nil, &errortypes.BadInput{ 147 Message: "Invalid/Missing SourceId", 148 } 149 } 150 151 if len(zeroclickfraudExt.Host) < 1 { 152 return nil, &errortypes.BadInput{ 153 Message: "Invalid/Missing Host", 154 } 155 } 156 157 return &zeroclickfraudExt, nil 158 } 159 160 func getMediaType(impID string, imps []openrtb2.Imp) openrtb_ext.BidType { 161 162 bidType := openrtb_ext.BidTypeBanner 163 164 for _, imp := range imps { 165 if imp.ID == impID { 166 if imp.Video != nil { 167 bidType = openrtb_ext.BidTypeVideo 168 break 169 } else if imp.Native != nil { 170 bidType = openrtb_ext.BidTypeNative 171 break 172 } else { 173 bidType = openrtb_ext.BidTypeBanner 174 break 175 } 176 } 177 } 178 179 return bidType 180 } 181 182 // Builder builds a new instance of the AppNexus adapter for the given bidder with the given config. 183 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 184 template, err := template.New("endpointTemplate").Parse(config.Endpoint) 185 if err != nil { 186 return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) 187 } 188 189 bidder := &ZeroClickFraudAdapter{ 190 EndpointTemplate: template, 191 } 192 return bidder, nil 193 }