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