github.com/prebid/prebid-server/v2@v2.18.0/adservertargeting/adservertargeting.go (about) 1 package adservertargeting 2 3 import ( 4 "encoding/json" 5 "net/url" 6 "strings" 7 8 "github.com/buger/jsonparser" 9 "github.com/prebid/openrtb/v20/openrtb2" 10 "github.com/prebid/prebid-server/v2/openrtb_ext" 11 ) 12 13 type DataSource string 14 15 const ( 16 SourceBidRequest DataSource = "bidrequest" 17 SourceStatic DataSource = "static" 18 SourceBidResponse DataSource = "bidresponse" 19 ) 20 21 const ( 22 bidderMacro = "{{BIDDER}}" 23 pathDelimiter = "." 24 ) 25 26 var ( 27 allowedTypes = []jsonparser.ValueType{jsonparser.String, jsonparser.Number} 28 ) 29 30 // RequestTargetingData struct to hold pre-processed ad server targeting keys and values 31 type RequestTargetingData struct { 32 SingleVal json.RawMessage 33 TargetingValueByImpId map[string][]byte 34 } 35 36 type ResponseTargetingData struct { 37 Key string 38 HasMacro bool 39 Path string 40 } 41 42 type adServerTargetingData struct { 43 RequestTargetingData map[string]RequestTargetingData 44 ResponseTargetingData []ResponseTargetingData 45 } 46 47 func Apply( 48 reqWrapper *openrtb_ext.RequestWrapper, 49 resolvedRequest json.RawMessage, 50 response *openrtb2.BidResponse, 51 queryParams url.Values, 52 bidResponseExt *openrtb_ext.ExtBidResponse, 53 truncateTargetAttribute *int) *openrtb2.BidResponse { 54 55 adServerTargeting, warnings := collect(reqWrapper, resolvedRequest, queryParams) 56 response, warnings = resolve(adServerTargeting, response, warnings, truncateTargetAttribute) 57 58 if len(warnings) > 0 { 59 bidResponseExt.Warnings[openrtb_ext.BidderReservedGeneral] = append(bidResponseExt.Warnings[openrtb_ext.BidderReservedGeneral], warnings...) 60 } 61 return response 62 } 63 64 // collect gathers targeting keys and values from request based on initial config 65 // and optimizes future key and value that should be collected from response 66 func collect( 67 reqWrapper *openrtb_ext.RequestWrapper, resolvedRequest json.RawMessage, 68 queryParams url.Values) (*adServerTargetingData, []openrtb_ext.ExtBidderMessage) { 69 70 var warnings []openrtb_ext.ExtBidderMessage 71 72 adServerTargeting, err := getAdServerTargeting(reqWrapper) 73 if err != nil { 74 warnings = append(warnings, createWarning("unable to extract adServerTargeting from request")) 75 return nil, warnings 76 } 77 adServerTargeting, validationWarnings := validateAdServerTargeting(adServerTargeting) 78 if len(validationWarnings) > 0 { 79 warnings = append(warnings, validationWarnings...) 80 } 81 82 requestTargetingData := map[string]RequestTargetingData{} 83 responseTargetingData := []ResponseTargetingData{} 84 85 impsCache := requestCache{resolvedReq: resolvedRequest} 86 87 for _, targetingObj := range adServerTargeting { 88 source := strings.ToLower(targetingObj.Source) 89 switch DataSource(source) { 90 case SourceBidRequest: 91 //causes PBS to treat 'value' as a path to pull from the request object 92 value, err := getValueFromBidRequest(&impsCache, targetingObj.Value, queryParams) 93 if err != nil { 94 warnings = append(warnings, createWarning(err.Error())) 95 } else { 96 requestTargetingData[targetingObj.Key] = value 97 } 98 case SourceStatic: 99 // causes PBS to just use the 'value' provided 100 staticValue := RequestTargetingData{SingleVal: json.RawMessage(targetingObj.Value)} 101 requestTargetingData[targetingObj.Key] = staticValue 102 case SourceBidResponse: 103 //causes PBS to treat 'value' as a path to pull from the bidder's response object, specifically seatbid[j].bid[k] 104 bidResponseTargeting := ResponseTargetingData{} 105 bidResponseTargeting.Key = targetingObj.Key 106 bidResponseTargeting.Path = targetingObj.Value 107 bidResponseTargeting.HasMacro = strings.Contains(strings.ToUpper(targetingObj.Key), bidderMacro) 108 responseTargetingData = append(responseTargetingData, bidResponseTargeting) 109 } 110 } 111 112 adServerTargetingData := &adServerTargetingData{ 113 RequestTargetingData: requestTargetingData, 114 ResponseTargetingData: responseTargetingData, 115 } 116 117 return adServerTargetingData, warnings 118 } 119 120 func resolve( 121 adServerTargetingData *adServerTargetingData, 122 response *openrtb2.BidResponse, 123 warnings []openrtb_ext.ExtBidderMessage, 124 truncateTargetAttribute *int) (*openrtb2.BidResponse, []openrtb_ext.ExtBidderMessage) { 125 126 bidCache := bidsCache{bids: make(map[string]map[string][]byte)} 127 128 for _, seat := range response.SeatBid { 129 bidderName := seat.Seat 130 for i, bid := range seat.Bid { 131 targetingData := make(map[string]string) 132 processRequestTargetingData(adServerTargetingData, targetingData, bid.ImpID) 133 respWarnings := processResponseTargetingData(adServerTargetingData, targetingData, bidderName, bid, bidCache, response, seat.Ext) 134 if len(respWarnings) > 0 { 135 warnings = append(warnings, respWarnings...) 136 } 137 seat.Bid[i].Ext = buildBidExt(targetingData, bid, warnings, truncateTargetAttribute) 138 } 139 } 140 return response, warnings 141 }