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

     1  package rtbhouse
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  
     9  	"github.com/prebid/openrtb/v19/openrtb2"
    10  	"github.com/prebid/prebid-server/adapters"
    11  	"github.com/prebid/prebid-server/config"
    12  	"github.com/prebid/prebid-server/errortypes"
    13  	"github.com/prebid/prebid-server/openrtb_ext"
    14  )
    15  
    16  const (
    17  	BidderCurrency string = "USD"
    18  )
    19  
    20  // RTBHouseAdapter implements the Bidder interface.
    21  type RTBHouseAdapter struct {
    22  	endpoint string
    23  }
    24  
    25  // Builder builds a new instance of the RTBHouse adapter for the given bidder with the given config.
    26  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    27  	bidder := &RTBHouseAdapter{
    28  		endpoint: config.Endpoint,
    29  	}
    30  	return bidder, nil
    31  }
    32  
    33  // MakeRequests prepares the HTTP requests which should be made to fetch bids.
    34  func (adapter *RTBHouseAdapter) MakeRequests(
    35  	openRTBRequest *openrtb2.BidRequest,
    36  	reqInfo *adapters.ExtraRequestInfo,
    37  ) (
    38  	requestsToBidder []*adapters.RequestData,
    39  	errs []error,
    40  ) {
    41  
    42  	reqCopy := *openRTBRequest
    43  	reqCopy.Imp = []openrtb2.Imp{}
    44  	for _, imp := range openRTBRequest.Imp {
    45  		var bidFloorCur = imp.BidFloorCur
    46  		var bidFloor = imp.BidFloor
    47  		if bidFloorCur == "" && bidFloor == 0 {
    48  			rtbhouseExt, err := getImpressionExt(imp)
    49  			if err != nil {
    50  				return nil, []error{err}
    51  			}
    52  			if rtbhouseExt.BidFloor > 0 && len(reqCopy.Cur) > 0 {
    53  				bidFloorCur = reqCopy.Cur[0]
    54  				bidFloor = rtbhouseExt.BidFloor
    55  			}
    56  		}
    57  
    58  		// Check if imp comes with bid floor amount defined in a foreign currency
    59  		if bidFloor > 0 && bidFloorCur != "" && strings.ToUpper(bidFloorCur) != BidderCurrency {
    60  			// Convert to US dollars
    61  			convertedValue, err := reqInfo.ConvertCurrency(bidFloor, bidFloorCur, BidderCurrency)
    62  			if err != nil {
    63  				return nil, []error{err}
    64  			}
    65  
    66  			bidFloorCur = BidderCurrency
    67  			bidFloor = convertedValue
    68  		}
    69  
    70  		if bidFloor > 0 && bidFloorCur == BidderCurrency {
    71  			// Update after conversion. All imp elements inside request.Imp are shallow copies
    72  			// therefore, their non-pointer values are not shared memory and are safe to modify.
    73  			imp.BidFloorCur = bidFloorCur
    74  			imp.BidFloor = bidFloor
    75  		}
    76  
    77  		// Set the CUR of bid to BIDDER_CURRENCY after converting all floors
    78  		reqCopy.Cur = []string{BidderCurrency}
    79  		reqCopy.Imp = append(reqCopy.Imp, imp)
    80  	}
    81  
    82  	openRTBRequestJSON, err := json.Marshal(reqCopy)
    83  	if err != nil {
    84  		errs = append(errs, err)
    85  		return nil, errs
    86  	}
    87  
    88  	headers := http.Header{}
    89  	headers.Add("Content-Type", "application/json;charset=utf-8")
    90  	requestToBidder := &adapters.RequestData{
    91  		Method:  "POST",
    92  		Uri:     adapter.endpoint,
    93  		Body:    openRTBRequestJSON,
    94  		Headers: headers,
    95  	}
    96  	requestsToBidder = append(requestsToBidder, requestToBidder)
    97  
    98  	return requestsToBidder, errs
    99  }
   100  
   101  func getImpressionExt(imp openrtb2.Imp) (*openrtb_ext.ExtImpRTBHouse, error) {
   102  	var bidderExt adapters.ExtImpBidder
   103  	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
   104  		return nil, &errortypes.BadInput{
   105  			Message: "Bidder extension not provided or can't be unmarshalled",
   106  		}
   107  	}
   108  
   109  	var rtbhouseExt openrtb_ext.ExtImpRTBHouse
   110  	if err := json.Unmarshal(bidderExt.Bidder, &rtbhouseExt); err != nil {
   111  		return nil, &errortypes.BadInput{
   112  			Message: "Error while unmarshaling bidder extension",
   113  		}
   114  	}
   115  
   116  	return &rtbhouseExt, nil
   117  }
   118  
   119  const unexpectedStatusCodeFormat = "" +
   120  	"Unexpected status code: %d. Run with request.debug = 1 for more info"
   121  
   122  // MakeBids unpacks the server's response into Bids.
   123  func (adapter *RTBHouseAdapter) MakeBids(
   124  	openRTBRequest *openrtb2.BidRequest,
   125  	requestToBidder *adapters.RequestData,
   126  	bidderRawResponse *adapters.ResponseData,
   127  ) (
   128  	bidderResponse *adapters.BidderResponse,
   129  	errs []error,
   130  ) {
   131  	switch bidderRawResponse.StatusCode {
   132  	case http.StatusOK:
   133  		break
   134  	case http.StatusNoContent:
   135  		return nil, nil
   136  	case http.StatusBadRequest:
   137  		err := &errortypes.BadInput{
   138  			Message: fmt.Sprintf(unexpectedStatusCodeFormat, bidderRawResponse.StatusCode),
   139  		}
   140  		return nil, []error{err}
   141  	default:
   142  		err := &errortypes.BadServerResponse{
   143  			Message: fmt.Sprintf(unexpectedStatusCodeFormat, bidderRawResponse.StatusCode),
   144  		}
   145  		return nil, []error{err}
   146  	}
   147  
   148  	var openRTBBidderResponse openrtb2.BidResponse
   149  	if err := json.Unmarshal(bidderRawResponse.Body, &openRTBBidderResponse); err != nil {
   150  		return nil, []error{err}
   151  	}
   152  
   153  	bidsCapacity := len(openRTBBidderResponse.SeatBid[0].Bid)
   154  	bidderResponse = adapters.NewBidderResponseWithBidsCapacity(bidsCapacity)
   155  	var typedBid *adapters.TypedBid
   156  	for _, seatBid := range openRTBBidderResponse.SeatBid {
   157  		for _, bid := range seatBid.Bid {
   158  			bid := bid // pin! -> https://github.com/kyoh86/scopelint#whats-this
   159  			typedBid = &adapters.TypedBid{Bid: &bid, BidType: "banner"}
   160  			bidderResponse.Bids = append(bidderResponse.Bids, typedBid)
   161  		}
   162  	}
   163  
   164  	bidderResponse.Currency = BidderCurrency
   165  
   166  	return bidderResponse, nil
   167  
   168  }