github.com/prebid/prebid-server@v0.275.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/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 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 return reqs, errs 125 } 126 127 func getPublisher(request *openrtb2.BidRequest) *openrtb2.Publisher { 128 if request.App != nil { 129 return request.App.Publisher 130 } 131 return request.Site.Publisher 132 } 133 134 func getBidCount(bidResponse openrtb2.BidResponse) int { 135 c := 0 136 for _, sb := range bidResponse.SeatBid { 137 c = c + len(sb.Bid) 138 } 139 return c 140 } 141 142 func (a *TripleliftNativeAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 143 if response.StatusCode == http.StatusNoContent { 144 return nil, nil 145 } 146 147 if response.StatusCode == http.StatusBadRequest { 148 return nil, []error{&errortypes.BadInput{ 149 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 150 }} 151 } 152 153 if response.StatusCode != http.StatusOK { 154 return nil, []error{&errortypes.BadServerResponse{Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode)}} 155 } 156 var bidResp openrtb2.BidResponse 157 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 158 return nil, []error{err} 159 } 160 var errs []error 161 count := getBidCount(bidResp) 162 bidResponse := adapters.NewBidderResponseWithBidsCapacity(count) 163 164 for _, sb := range bidResp.SeatBid { 165 for i := 0; i < len(sb.Bid); i++ { 166 bid := sb.Bid[i] 167 var bidExt TripleliftRespExt 168 bidType := getBidType(bidExt) 169 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 170 Bid: &bid, 171 BidType: bidType, 172 }) 173 } 174 } 175 return bidResponse, errs 176 } 177 178 // Builder builds a new instance of the TripleliftNative adapter for the given bidder with the given config. 179 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 180 extraInfo, err := getExtraInfo(config.ExtraAdapterInfo) 181 if err != nil { 182 return nil, err 183 } 184 185 // Populate map for faster memory access 186 extraInfo.PublisherWhitelistMap = make(map[string]struct{}, len(extraInfo.PublisherWhitelist)) 187 for _, v := range extraInfo.PublisherWhitelist { 188 extraInfo.PublisherWhitelistMap[v] = struct{}{} 189 } 190 191 bidder := &TripleliftNativeAdapter{ 192 endpoint: config.Endpoint, 193 extInfo: extraInfo, 194 } 195 return bidder, nil 196 } 197 198 func getExtraInfo(v string) (TripleliftNativeExtInfo, error) { 199 if len(v) == 0 { 200 return getDefaultExtraInfo(), nil 201 } 202 203 var extraInfo TripleliftNativeExtInfo 204 if err := json.Unmarshal([]byte(v), &extraInfo); err != nil { 205 return extraInfo, fmt.Errorf("invalid extra info: %v", err) 206 } 207 208 return extraInfo, nil 209 } 210 211 func getDefaultExtraInfo() TripleliftNativeExtInfo { 212 return TripleliftNativeExtInfo{ 213 PublisherWhitelist: []string{}, 214 } 215 }