github.com/prebid/prebid-server/v2@v2.18.0/adapters/sharethrough/sharethrough.go (about)

     1  package sharethrough
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  
     9  	"github.com/prebid/openrtb/v20/openrtb2"
    10  	"github.com/prebid/prebid-server/v2/adapters"
    11  	"github.com/prebid/prebid-server/v2/config"
    12  	"github.com/prebid/prebid-server/v2/errortypes"
    13  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    14  	"github.com/prebid/prebid-server/v2/version"
    15  )
    16  
    17  var adapterVersion = "10.0"
    18  
    19  type adapter struct {
    20  	endpoint string
    21  }
    22  
    23  // Builder builds a new instance of the Sharethrough adapter for the given bidder with the given config.
    24  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    25  	bidder := &adapter{
    26  		endpoint: config.Endpoint,
    27  	}
    28  	return bidder, nil
    29  }
    30  
    31  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    32  	var requests []*adapters.RequestData
    33  	var errors []error
    34  
    35  	headers := http.Header{}
    36  	headers.Add("Content-Type", "application/json;charset=utf-8")
    37  	headers.Add("Accept", "application/json")
    38  
    39  	modifiableSource := openrtb2.Source{}
    40  	if request.Source != nil {
    41  		modifiableSource = *request.Source
    42  	}
    43  	var sourceExt map[string]interface{}
    44  	if err := json.Unmarshal(modifiableSource.Ext, &sourceExt); err == nil {
    45  		sourceExt["str"] = adapterVersion
    46  		sourceExt["version"] = version.Ver
    47  	} else {
    48  		sourceExt = map[string]interface{}{"str": adapterVersion, "version": version.Ver}
    49  	}
    50  
    51  	var err error
    52  	if modifiableSource.Ext, err = json.Marshal(sourceExt); err != nil {
    53  		errors = append(errors, err)
    54  	}
    55  	request.Source = &modifiableSource
    56  
    57  	requestCopy := *request
    58  	for _, imp := range request.Imp {
    59  		// Extract Sharethrough Params
    60  		var strImpExt adapters.ExtImpBidder
    61  		if err := json.Unmarshal(imp.Ext, &strImpExt); err != nil {
    62  			errors = append(errors, err)
    63  			continue
    64  		}
    65  		var strImpParams openrtb_ext.ExtImpSharethrough
    66  		if err := json.Unmarshal(strImpExt.Bidder, &strImpParams); err != nil {
    67  			errors = append(errors, err)
    68  			continue
    69  		}
    70  
    71  		// Convert Floor into USD
    72  		if imp.BidFloor > 0 && imp.BidFloorCur != "" && !strings.EqualFold(imp.BidFloorCur, "USD") {
    73  			convertedValue, err := reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD")
    74  			if err != nil {
    75  				return nil, []error{err}
    76  			}
    77  			imp.BidFloorCur = "USD"
    78  			imp.BidFloor = convertedValue
    79  		}
    80  
    81  		// Relocate Custom Params
    82  		imp.TagID = strImpParams.Pkey
    83  		requestCopy.BCat = append(requestCopy.BCat, strImpParams.BCat...)
    84  		requestCopy.BAdv = append(requestCopy.BAdv, strImpParams.BAdv...)
    85  
    86  		impressionsByMediaType, err := splitImpressionsByMediaType(&imp)
    87  		if err != nil {
    88  			errors = append(errors, err)
    89  			continue
    90  		}
    91  
    92  		for _, impression := range impressionsByMediaType {
    93  			requestCopy.Imp = []openrtb2.Imp{impression}
    94  
    95  			requestJSON, err := json.Marshal(requestCopy)
    96  			if err != nil {
    97  				errors = append(errors, err)
    98  				continue
    99  			}
   100  
   101  			requestData := &adapters.RequestData{
   102  				Method:  "POST",
   103  				Uri:     a.endpoint,
   104  				Body:    requestJSON,
   105  				Headers: headers,
   106  				ImpIDs:  openrtb_ext.GetImpIDs(requestCopy.Imp),
   107  			}
   108  			requests = append(requests, requestData)
   109  		}
   110  	}
   111  
   112  	return requests, errors
   113  }
   114  
   115  func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   116  	if response.StatusCode == http.StatusNoContent {
   117  		return nil, nil
   118  	}
   119  
   120  	if response.StatusCode == http.StatusBadRequest {
   121  		return nil, []error{&errortypes.BadInput{
   122  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
   123  		}}
   124  	}
   125  
   126  	if response.StatusCode != http.StatusOK {
   127  		return nil, []error{fmt.Errorf("unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode)}
   128  	}
   129  
   130  	var bidReq openrtb2.BidRequest
   131  	if err := json.Unmarshal(requestData.Body, &bidReq); err != nil {
   132  		return nil, []error{err}
   133  	}
   134  
   135  	var bidResp openrtb2.BidResponse
   136  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   137  		return nil, []error{err}
   138  	}
   139  
   140  	bidderResponse := adapters.NewBidderResponse()
   141  	bidderResponse.Currency = "USD"
   142  	var errors []error
   143  
   144  	for _, seatBid := range bidResp.SeatBid {
   145  		for i := range seatBid.Bid {
   146  			bid := &seatBid.Bid[i]
   147  			bidType, err := getMediaTypeForBid(*bid)
   148  			if err != nil {
   149  				errors = append(errors, err)
   150  			}
   151  
   152  			bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
   153  				BidType: bidType,
   154  				Bid:     bid,
   155  			})
   156  		}
   157  	}
   158  
   159  	return bidderResponse, errors
   160  }
   161  
   162  func splitImpressionsByMediaType(impression *openrtb2.Imp) ([]openrtb2.Imp, error) {
   163  	if impression.Banner == nil && impression.Video == nil && impression.Native == nil {
   164  		return nil, &errortypes.BadInput{Message: "Invalid MediaType. Sharethrough only supports Banner, Video and Native."}
   165  	}
   166  
   167  	if impression.Audio != nil {
   168  		impression.Audio = nil
   169  	}
   170  
   171  	impressions := make([]openrtb2.Imp, 0, 3)
   172  
   173  	if impression.Banner != nil {
   174  		impCopy := *impression
   175  		impCopy.Video = nil
   176  		impCopy.Native = nil
   177  		impressions = append(impressions, impCopy)
   178  	}
   179  
   180  	if impression.Video != nil {
   181  		impCopy := *impression
   182  		impCopy.Banner = nil
   183  		impCopy.Native = nil
   184  		impressions = append(impressions, impCopy)
   185  	}
   186  
   187  	if impression.Native != nil {
   188  		impression.Banner = nil
   189  		impression.Video = nil
   190  		impressions = append(impressions, *impression)
   191  	}
   192  
   193  	return impressions, nil
   194  }
   195  
   196  func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
   197  
   198  	if bid.Ext != nil {
   199  		var bidExt openrtb_ext.ExtBid
   200  		err := json.Unmarshal(bid.Ext, &bidExt)
   201  		if err == nil && bidExt.Prebid != nil {
   202  			return openrtb_ext.ParseBidType(string(bidExt.Prebid.Type))
   203  		}
   204  	}
   205  
   206  	return "", &errortypes.BadServerResponse{
   207  		Message: fmt.Sprintf("Failed to parse bid mediatype for impression \"%s\"", bid.ImpID),
   208  	}
   209  }