github.com/prebid/prebid-server/v2@v2.18.0/adservertargeting/respdataprocessor.go (about) 1 package adservertargeting 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7 8 "github.com/pkg/errors" 9 "github.com/prebid/openrtb/v20/openrtb2" 10 "github.com/prebid/prebid-server/v2/openrtb_ext" 11 "github.com/prebid/prebid-server/v2/util/jsonutil" 12 jsonpatch "gopkg.in/evanphx/json-patch.v4" 13 ) 14 15 const MaxKeyLength = 20 16 17 func processRequestTargetingData(adServerTargetingData *adServerTargetingData, targetingData map[string]string, bidImpId string) { 18 for key, val := range adServerTargetingData.RequestTargetingData { 19 if len(val.SingleVal) > 0 { 20 targetingData[key] = string(val.SingleVal) 21 } else if len(val.TargetingValueByImpId) > 0 { 22 targetingData[key] = string(val.TargetingValueByImpId[bidImpId]) 23 } 24 } 25 } 26 27 func processResponseTargetingData( 28 adServerTargetingData *adServerTargetingData, 29 targetingData map[string]string, 30 bidderName string, 31 bid openrtb2.Bid, 32 bidsHolder bidsCache, 33 response *openrtb2.BidResponse, 34 seatExt json.RawMessage) []openrtb_ext.ExtBidderMessage { 35 36 var warnings []openrtb_ext.ExtBidderMessage 37 38 for _, respTargetingData := range adServerTargetingData.ResponseTargetingData { 39 key := resolveKey(respTargetingData, bidderName) 40 path := respTargetingData.Path 41 42 var value string 43 var err error 44 pathSplit := strings.Split(path, pathDelimiter) 45 46 switch pathSplit[0] { 47 48 case "seatbid": 49 switch pathSplit[1] { 50 case "bid": 51 value, err = getValueFromSeatBidBid(path, bidsHolder, bidderName, bid) 52 case "ext": 53 value, err = getValueFromExt(path, "seatbid.ext.", seatExt) 54 } 55 case "ext": 56 value, err = getValueFromExt(path, "ext.", response.Ext) 57 default: 58 value, err = getValueFromResp(path, response) 59 } 60 61 if err != nil { 62 message := fmt.Sprintf("%s for bidder: %s, bid id: %s", err.Error(), bidderName, bid.ID) 63 warnings = append(warnings, createWarning(message)) 64 } else { 65 targetingData[key] = value 66 } 67 } 68 return warnings 69 } 70 71 func buildBidExt(targetingData map[string]string, 72 bid openrtb2.Bid, 73 warnings []openrtb_ext.ExtBidderMessage, 74 truncateTargetAttribute *int) json.RawMessage { 75 76 targetingDataTruncated := truncateTargetingKeys(targetingData, truncateTargetAttribute) 77 78 bidExtTargetingData := openrtb_ext.ExtBid{ 79 Prebid: &openrtb_ext.ExtBidPrebid{ 80 Targeting: targetingDataTruncated, 81 }, 82 } 83 bidExtTargeting, err := jsonutil.Marshal(bidExtTargetingData) 84 if err != nil { 85 warnings = append(warnings, createWarning(err.Error())) //nolint: ineffassign,staticcheck 86 return nil 87 } 88 89 newExt, err := jsonpatch.MergePatch(bid.Ext, bidExtTargeting) 90 if err != nil { 91 warnings = append(warnings, createWarning(err.Error())) //nolint: ineffassign,staticcheck 92 return nil 93 } 94 return newExt 95 } 96 97 func resolveKey(respTargetingData ResponseTargetingData, bidderName string) string { 98 key := respTargetingData.Key 99 if respTargetingData.HasMacro { 100 key = strings.Replace(respTargetingData.Key, bidderMacro, bidderName, -1) 101 } 102 return key 103 } 104 105 func truncateTargetingKeys(targetingData map[string]string, truncateTargetAttribute *int) map[string]string { 106 maxLength := MaxKeyLength 107 if truncateTargetAttribute != nil { 108 maxLength = *truncateTargetAttribute 109 if maxLength <= 0 { 110 maxLength = MaxKeyLength 111 } 112 } 113 114 targetingDataTruncated := make(map[string]string) 115 for key, value := range targetingData { 116 newKey := openrtb_ext.TargetingKey(key).TruncateKey(maxLength) 117 targetingDataTruncated[newKey] = value 118 } 119 return targetingDataTruncated 120 } 121 122 func getValueFromSeatBidBid(path string, bidsHolder bidsCache, bidderName string, bid openrtb2.Bid) (string, error) { 123 bidBytes, err := bidsHolder.GetBid(bidderName, bid.ID, bid) 124 if err != nil { 125 return "", err 126 } 127 128 bidSplit := strings.Split(path, "seatbid.bid.") 129 value, err := splitAndGet(bidSplit[1], bidBytes, pathDelimiter) 130 if err != nil { 131 return "", err 132 133 } 134 return value, nil 135 } 136 137 func getValueFromExt(path, separator string, respExt json.RawMessage) (string, error) { 138 //path points to resp.ext, means path starts with ext 139 extSplit := strings.Split(path, separator) 140 value, err := splitAndGet(extSplit[1], respExt, pathDelimiter) 141 if err != nil { 142 return "", err 143 } 144 145 return value, nil 146 } 147 148 // getValueFromResp optimizes retrieval of paths for a bid response (not a seat or ext) 149 func getValueFromResp(path string, response *openrtb2.BidResponse) (string, error) { 150 value, err := getRespData(response, path) 151 if err != nil { 152 return "", err 153 } 154 return value, nil 155 } 156 157 func getRespData(bidderResp *openrtb2.BidResponse, field string) (string, error) { 158 // this code should be modified if there are changes in OpenRtb format 159 // it's up to date with OpenRTB 2.5 and 2.6 160 switch field { 161 case "id": 162 return bidderResp.ID, nil 163 case "bidid": 164 return bidderResp.BidID, nil 165 case "cur": 166 return bidderResp.Cur, nil 167 case "customdata": 168 return bidderResp.CustomData, nil 169 case "nbr": 170 return fmt.Sprint(bidderResp.NBR.Val()), nil 171 172 default: 173 return "", errors.Errorf("key not found for field in bid response: %s", field) 174 } 175 176 }