github.com/prebid/prebid-server@v0.275.0/adapters/orbidder/orbidder.go (about)

     1  package orbidder
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  
     9  	"github.com/prebid/openrtb/v19/openrtb2"
    10  
    11  	"github.com/prebid/prebid-server/adapters"
    12  	"github.com/prebid/prebid-server/config"
    13  	"github.com/prebid/prebid-server/errortypes"
    14  	"github.com/prebid/prebid-server/openrtb_ext"
    15  )
    16  
    17  type OrbidderAdapter struct {
    18  	endpoint string
    19  }
    20  
    21  // MakeRequests makes the HTTP requests which should be made to fetch bids from orbidder.
    22  func (rcv *OrbidderAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    23  	validImps, errs := getValidImpressions(request, reqInfo)
    24  	if len(validImps) == 0 {
    25  		return nil, errs
    26  	}
    27  
    28  	request.Imp = validImps
    29  
    30  	requestBodyJSON, err := json.Marshal(request)
    31  	if err != nil {
    32  		errs = append(errs, err)
    33  		return nil, errs
    34  	}
    35  
    36  	headers := http.Header{}
    37  	headers.Add("Content-Type", "application/json;charset=utf-8")
    38  	headers.Add("Accept", "application/json")
    39  
    40  	return []*adapters.RequestData{{
    41  		Method:  http.MethodPost,
    42  		Uri:     rcv.endpoint,
    43  		Body:    requestBodyJSON,
    44  		Headers: headers,
    45  	}}, errs
    46  }
    47  
    48  // getValidImpressions validate imps and check for bid floor currency. Convert to EUR if necessary
    49  func getValidImpressions(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]openrtb2.Imp, []error) {
    50  	var errs []error
    51  	var validImps []openrtb2.Imp
    52  
    53  	for _, imp := range request.Imp {
    54  		if err := preprocessBidFloorCurrency(&imp, reqInfo); err != nil {
    55  			errs = append(errs, err)
    56  			continue
    57  		}
    58  
    59  		if err := preprocessExtensions(&imp); err != nil {
    60  			errs = append(errs, err)
    61  			continue
    62  		}
    63  		validImps = append(validImps, imp)
    64  	}
    65  	return validImps, errs
    66  }
    67  
    68  func preprocessExtensions(imp *openrtb2.Imp) error {
    69  	var bidderExt adapters.ExtImpBidder
    70  	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
    71  		return &errortypes.BadInput{
    72  			Message: err.Error(),
    73  		}
    74  	}
    75  
    76  	var orbidderExt openrtb_ext.ExtImpOrbidder
    77  	if err := json.Unmarshal(bidderExt.Bidder, &orbidderExt); err != nil {
    78  		return &errortypes.BadInput{
    79  			Message: "Wrong orbidder bidder ext: " + err.Error(),
    80  		}
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  func preprocessBidFloorCurrency(imp *openrtb2.Imp, reqInfo *adapters.ExtraRequestInfo) error {
    87  	// we expect every currency related data to be EUR
    88  	if imp.BidFloor > 0 && strings.ToUpper(imp.BidFloorCur) != "EUR" && imp.BidFloorCur != "" {
    89  		if convertedValue, err := reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "EUR"); err != nil {
    90  			return err
    91  		} else {
    92  			imp.BidFloor = convertedValue
    93  		}
    94  	}
    95  	imp.BidFloorCur = "EUR"
    96  	return nil
    97  }
    98  
    99  // MakeBids unpacks server response into Bids.
   100  func (rcv OrbidderAdapter) MakeBids(_ *openrtb2.BidRequest, _ *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   101  	if response.StatusCode == http.StatusNoContent {
   102  		return nil, nil
   103  	}
   104  
   105  	if response.StatusCode >= http.StatusInternalServerError {
   106  		return nil, []error{&errortypes.BadServerResponse{
   107  			Message: fmt.Sprintf("Unexpected status code: %d. Dsp server internal error.", response.StatusCode),
   108  		}}
   109  	}
   110  
   111  	if response.StatusCode >= http.StatusBadRequest {
   112  		return nil, []error{&errortypes.BadInput{
   113  			Message: fmt.Sprintf("Unexpected status code: %d. Bad request to dsp.", response.StatusCode),
   114  		}}
   115  	}
   116  
   117  	if response.StatusCode != http.StatusOK {
   118  		return nil, []error{&errortypes.BadServerResponse{
   119  			Message: fmt.Sprintf("Unexpected status code: %d. Bad response from dsp.", response.StatusCode),
   120  		}}
   121  	}
   122  
   123  	var bidResp openrtb2.BidResponse
   124  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   125  		return nil, []error{err}
   126  	}
   127  
   128  	var bidErrs []error
   129  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)
   130  	for _, seatBid := range bidResp.SeatBid {
   131  		for i := range seatBid.Bid {
   132  			// later we have to add the bid as a pointer,
   133  			// because of this we need a variable that only exists at this loop iteration.
   134  			// otherwise there will be issues with multibid and pointer behavior.
   135  			bid := seatBid.Bid[i]
   136  			bidType, err := getBidType(bid)
   137  			if err != nil {
   138  				// could not determinate media type, append an error and continue with the next bid.
   139  				bidErrs = append(bidErrs, err)
   140  				continue
   141  			}
   142  
   143  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   144  				Bid:     &bid,
   145  				BidType: bidType,
   146  			})
   147  		}
   148  	}
   149  	if bidResp.Cur != "" {
   150  		bidResponse.Currency = bidResp.Cur
   151  	}
   152  
   153  	return bidResponse, bidErrs
   154  }
   155  
   156  func getBidType(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
   157  
   158  	// determinate media type by bid response field mtype
   159  	switch bid.MType {
   160  	case openrtb2.MarkupBanner:
   161  		return openrtb_ext.BidTypeBanner, nil
   162  	case openrtb2.MarkupVideo:
   163  		return openrtb_ext.BidTypeVideo, nil
   164  	case openrtb2.MarkupAudio:
   165  		return openrtb_ext.BidTypeAudio, nil
   166  	case openrtb2.MarkupNative:
   167  		return openrtb_ext.BidTypeNative, nil
   168  	}
   169  
   170  	return "", &errortypes.BadInput{
   171  		Message: fmt.Sprintf("Could not define media type for impression: %s", bid.ImpID),
   172  	}
   173  }
   174  
   175  // Builder builds a new instance of the Orbidder adapter for the given bidder with the given config.
   176  func Builder(_ openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
   177  	bidder := &OrbidderAdapter{
   178  		endpoint: config.Endpoint,
   179  	}
   180  	return bidder, nil
   181  }