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  }