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