github.com/prebid/prebid-server/v2@v2.18.0/adapters/triplelift_native/triplelift_native.go (about) 1 package triplelift_native 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 TripleliftNativeAdapter struct { 16 endpoint string 17 extInfo TripleliftNativeExtInfo 18 } 19 20 type TripleliftInnerExt struct { 21 Format int `json:"format"` 22 } 23 24 type TripleliftRespExt struct { 25 Triplelift TripleliftInnerExt `json:"triplelift_pb"` 26 } 27 28 type TripleliftNativeExtInfo struct { 29 // Array is used for deserialization. 30 PublisherWhitelist []string `json:"publisher_whitelist"` 31 32 // Map is used for optimized memory access and should be constructed after deserialization. 33 PublisherWhitelistMap map[string]struct{} 34 } 35 36 func getBidType(ext TripleliftRespExt) openrtb_ext.BidType { 37 return openrtb_ext.BidTypeNative 38 } 39 40 func processImp(imp *openrtb2.Imp) error { 41 // get the triplelift extension 42 var ext adapters.ExtImpBidder 43 var tlext openrtb_ext.ExtImpTriplelift 44 if err := json.Unmarshal(imp.Ext, &ext); err != nil { 45 return err 46 } 47 if err := json.Unmarshal(ext.Bidder, &tlext); err != nil { 48 return err 49 } 50 if imp.Native == nil { 51 return fmt.Errorf("no native object specified") 52 } 53 if tlext.InvCode == "" { 54 return fmt.Errorf("no inv_code specified") 55 } 56 imp.TagID = tlext.InvCode 57 // floor is optional 58 if tlext.Floor == nil { 59 return nil 60 } 61 imp.BidFloor = *tlext.Floor 62 // no error 63 return nil 64 } 65 66 // Returns the effective publisher ID 67 func effectivePubID(pub *openrtb2.Publisher) string { 68 if pub != nil { 69 if pub.Ext != nil { 70 var pubExt openrtb_ext.ExtPublisher 71 err := json.Unmarshal(pub.Ext, &pubExt) 72 if err == nil && pubExt.Prebid != nil && pubExt.Prebid.ParentAccount != nil && *pubExt.Prebid.ParentAccount != "" { 73 return *pubExt.Prebid.ParentAccount 74 } 75 } 76 if pub.ID != "" { 77 return pub.ID 78 } 79 } 80 return "unknown" 81 } 82 83 func (a *TripleliftNativeAdapter) MakeRequests(request *openrtb2.BidRequest, extra *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 84 errs := make([]error, 0, len(request.Imp)+1) 85 reqs := make([]*adapters.RequestData, 0, 1) 86 // copy the request, because we are going to mutate it 87 tlRequest := *request 88 // this will contain all the valid impressions 89 var validImps []openrtb2.Imp 90 // pre-process the imps 91 for _, imp := range tlRequest.Imp { 92 if err := processImp(&imp); err == nil { 93 validImps = append(validImps, imp) 94 } else { 95 errs = append(errs, err) 96 } 97 } 98 publisher := getPublisher(request) 99 publisherID := effectivePubID(publisher) 100 if _, exists := a.extInfo.PublisherWhitelistMap[publisherID]; !exists { 101 err := fmt.Errorf("Unsupported publisher for triplelift_native") 102 return nil, []error{err} 103 } 104 if len(validImps) == 0 { 105 err := fmt.Errorf("No valid impressions for triplelift") 106 errs = append(errs, err) 107 return nil, errs 108 } 109 tlRequest.Imp = validImps 110 reqJSON, err := json.Marshal(tlRequest) 111 if err != nil { 112 errs = append(errs, err) 113 return nil, errs 114 } 115 headers := http.Header{} 116 headers.Add("Content-Type", "application/json;charset=utf-8") 117 headers.Add("Accept", "application/json") 118 ad := a.endpoint 119 reqs = append(reqs, &adapters.RequestData{ 120 Method: "POST", 121 Uri: ad, 122 Body: reqJSON, 123 Headers: headers, 124 ImpIDs: openrtb_ext.GetImpIDs(tlRequest.Imp)}) 125 return reqs, errs 126 } 127 128 func getPublisher(request *openrtb2.BidRequest) *openrtb2.Publisher { 129 if request.App != nil { 130 return request.App.Publisher 131 } 132 return request.Site.Publisher 133 } 134 135 func getBidCount(bidResponse openrtb2.BidResponse) int { 136 c := 0 137 for _, sb := range bidResponse.SeatBid { 138 c = c + len(sb.Bid) 139 } 140 return c 141 } 142 143 func (a *TripleliftNativeAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 144 if response.StatusCode == http.StatusNoContent { 145 return nil, nil 146 } 147 148 if response.StatusCode == http.StatusBadRequest { 149 return nil, []error{&errortypes.BadInput{ 150 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 151 }} 152 } 153 154 if response.StatusCode != http.StatusOK { 155 return nil, []error{&errortypes.BadServerResponse{Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode)}} 156 } 157 var bidResp openrtb2.BidResponse 158 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 159 return nil, []error{err} 160 } 161 var errs []error 162 count := getBidCount(bidResp) 163 bidResponse := adapters.NewBidderResponseWithBidsCapacity(count) 164 165 for _, sb := range bidResp.SeatBid { 166 for i := 0; i < len(sb.Bid); i++ { 167 bid := sb.Bid[i] 168 var bidExt TripleliftRespExt 169 bidType := getBidType(bidExt) 170 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 171 Bid: &bid, 172 BidType: bidType, 173 }) 174 } 175 } 176 return bidResponse, errs 177 } 178 179 // Builder builds a new instance of the TripleliftNative adapter for the given bidder with the given config. 180 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 181 extraInfo, err := getExtraInfo(config.ExtraAdapterInfo) 182 if err != nil { 183 return nil, err 184 } 185 186 // Populate map for faster memory access 187 extraInfo.PublisherWhitelistMap = make(map[string]struct{}, len(extraInfo.PublisherWhitelist)) 188 for _, v := range extraInfo.PublisherWhitelist { 189 extraInfo.PublisherWhitelistMap[v] = struct{}{} 190 } 191 192 bidder := &TripleliftNativeAdapter{ 193 endpoint: config.Endpoint, 194 extInfo: extraInfo, 195 } 196 return bidder, nil 197 } 198 199 func getExtraInfo(v string) (TripleliftNativeExtInfo, error) { 200 if len(v) == 0 { 201 return getDefaultExtraInfo(), nil 202 } 203 204 var extraInfo TripleliftNativeExtInfo 205 if err := json.Unmarshal([]byte(v), &extraInfo); err != nil { 206 return extraInfo, fmt.Errorf("invalid extra info: %v", err) 207 } 208 209 return extraInfo, nil 210 } 211 212 func getDefaultExtraInfo() TripleliftNativeExtInfo { 213 return TripleliftNativeExtInfo{ 214 PublisherWhitelist: []string{}, 215 } 216 }