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

     1  package adview
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  	"text/template"
     9  
    10  	"github.com/prebid/openrtb/v20/openrtb2"
    11  	"github.com/prebid/prebid-server/v2/adapters"
    12  	"github.com/prebid/prebid-server/v2/config"
    13  	"github.com/prebid/prebid-server/v2/errortypes"
    14  	"github.com/prebid/prebid-server/v2/macros"
    15  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    16  )
    17  
    18  type adapter struct {
    19  	endpoint *template.Template
    20  }
    21  
    22  type adviewBidExt struct {
    23  	BidType int `json:"formattype,omitempty"`
    24  }
    25  
    26  // Builder builds a new instance of the adview adapter for the given bidder with the given config.
    27  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    28  	endpointTemplate, err := template.New("endpointTemplate").Parse(config.Endpoint)
    29  	if err != nil {
    30  		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
    31  	}
    32  
    33  	bidder := &adapter{
    34  		endpoint: endpointTemplate,
    35  	}
    36  	return bidder, nil
    37  }
    38  
    39  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    40  
    41  	var requests []*adapters.RequestData
    42  	var errors []error
    43  
    44  	//must copy the original request.
    45  	requestCopy := *request
    46  	for _, imp := range request.Imp {
    47  		var bidderExt adapters.ExtImpBidder
    48  		if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
    49  			errors = append(errors, &errortypes.BadInput{
    50  				Message: fmt.Sprintf("invalid imp.ext, %s", err.Error()),
    51  			})
    52  			continue
    53  		}
    54  		//use adview
    55  		var advImpExt openrtb_ext.ExtImpAdView
    56  		if err := json.Unmarshal(bidderExt.Bidder, &advImpExt); err != nil {
    57  			errors = append(errors, &errortypes.BadInput{
    58  				Message: fmt.Sprintf("invalid bidderExt.Bidder, %s", err.Error()),
    59  			})
    60  			continue
    61  		}
    62  
    63  		imp.TagID = advImpExt.MasterTagID //tagid means posid
    64  		//for adview bid request
    65  		if imp.Banner != nil {
    66  			if len(imp.Banner.Format) != 0 {
    67  				bannerCopy := *imp.Banner
    68  				bannerCopy.H = &imp.Banner.Format[0].H
    69  				bannerCopy.W = &imp.Banner.Format[0].W
    70  				imp.Banner = &bannerCopy
    71  			}
    72  		}
    73  
    74  		// Check if imp comes with bid floor amount defined in a foreign currency
    75  		if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" {
    76  			// Convert to US dollars
    77  			convertedValue, err := requestInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD")
    78  			if err != nil {
    79  				errors = append(errors, err)
    80  				continue
    81  			}
    82  			// Update after conversion. All imp elements inside request.Imp are shallow copies
    83  			// therefore, their non-pointer values are not shared memory and are safe to modify.
    84  			imp.BidFloorCur = "USD"
    85  			imp.BidFloor = convertedValue
    86  		}
    87  
    88  		// Set the CUR of bid to USD after converting all floors
    89  		requestCopy.Cur = []string{"USD"}
    90  		requestCopy.Imp = []openrtb2.Imp{imp}
    91  
    92  		url, err := a.buildEndpointURL(&advImpExt)
    93  		if err != nil {
    94  			errors = append(errors, err)
    95  			continue
    96  		}
    97  
    98  		reqJSON, err := json.Marshal(requestCopy) //request
    99  		if err != nil {
   100  			errors = append(errors, err)
   101  			continue
   102  		}
   103  
   104  		requestData := &adapters.RequestData{
   105  			Method: http.MethodPost,
   106  			Uri:    url,
   107  			Body:   reqJSON,
   108  			ImpIDs: openrtb_ext.GetImpIDs(requestCopy.Imp),
   109  		}
   110  		requests = append(requests, requestData)
   111  	}
   112  	return requests, errors
   113  }
   114  
   115  func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   116  	if responseData.StatusCode == http.StatusNoContent {
   117  		return nil, nil
   118  	}
   119  
   120  	if responseData.StatusCode == http.StatusBadRequest {
   121  		err := &errortypes.BadInput{
   122  			Message: "Unexpected status code: 400. Bad request from publisher.",
   123  		}
   124  		return nil, []error{err}
   125  	}
   126  
   127  	if responseData.StatusCode != http.StatusOK {
   128  		err := &errortypes.BadServerResponse{
   129  			Message: fmt.Sprintf("Unexpected status code: %d.", responseData.StatusCode),
   130  		}
   131  		return nil, []error{err}
   132  	}
   133  
   134  	var response openrtb2.BidResponse
   135  	if err := json.Unmarshal(responseData.Body, &response); err != nil {
   136  		return nil, []error{err}
   137  	}
   138  
   139  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
   140  	//we just support USD for resp
   141  	bidResponse.Currency = "USD"
   142  
   143  	var errors []error
   144  	for _, seatBid := range response.SeatBid {
   145  		for i, bid := range seatBid.Bid {
   146  			bidType, err := getMediaTypeForBid(bid)
   147  			if err != nil {
   148  				errors = append(errors, err)
   149  				continue
   150  			}
   151  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   152  				Bid:     &seatBid.Bid[i],
   153  				BidType: bidType,
   154  			})
   155  		}
   156  	}
   157  
   158  	return bidResponse, errors
   159  }
   160  
   161  // Builds endpoint url based on adapter-specific pub settings from imp.ext
   162  func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtImpAdView) (string, error) {
   163  	endpointParams := macros.EndpointTemplateParams{AccountID: params.AccountID}
   164  	return macros.ResolveMacros(a.endpoint, endpointParams)
   165  }
   166  
   167  func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
   168  	switch bid.MType {
   169  	case openrtb2.MarkupBanner:
   170  		return openrtb_ext.BidTypeBanner, nil
   171  	case openrtb2.MarkupVideo:
   172  		return openrtb_ext.BidTypeVideo, nil
   173  	case openrtb2.MarkupNative:
   174  		return openrtb_ext.BidTypeNative, nil
   175  	default:
   176  		return "", fmt.Errorf("Unable to fetch mediaType in impID: %s, mType: %d", bid.ImpID, bid.MType)
   177  	}
   178  }