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

     1  package richaudience
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"net/url"
     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  )
    15  
    16  type adapter struct {
    17  	endpoint string
    18  }
    19  
    20  // Builder builds a new instance of the RichAudience adapter for the given bidder with the given config.
    21  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    22  	bidder := &adapter{
    23  		endpoint: config.Endpoint,
    24  	}
    25  	return bidder, nil
    26  }
    27  
    28  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    29  	var requestDataRequest []*adapters.RequestData
    30  	errs := make([]error, 0, len(request.Imp))
    31  	raiHeaders := http.Header{}
    32  	setHeaders(&raiHeaders)
    33  
    34  	isUrlSecure := getIsUrlSecure(request)
    35  
    36  	if err := validateDevice(request); err != nil {
    37  		errs = append(errs, &errortypes.BadInput{
    38  			Message: err.Error(),
    39  		})
    40  		return nil, errs
    41  	}
    42  
    43  	for _, imp := range request.Imp {
    44  		var secure = int8(0)
    45  
    46  		raiExt, err := parseImpExt(&imp)
    47  		if err != nil {
    48  			errs = append(errs, &errortypes.BadInput{
    49  				Message: err.Error(),
    50  			})
    51  			continue
    52  		}
    53  
    54  		if request.App != nil {
    55  			appCopy := *request.App
    56  			request.App = &appCopy
    57  
    58  			request.App.Keywords = "tagid=" + imp.TagID
    59  		}
    60  
    61  		if request.Site != nil {
    62  			siteCopy := *request.Site
    63  			request.Site = &siteCopy
    64  
    65  			request.Site.Keywords = "tagid=" + imp.TagID
    66  		}
    67  
    68  		if raiExt != nil {
    69  			if raiExt.Pid != "" {
    70  				imp.TagID = raiExt.Pid
    71  			}
    72  
    73  			if raiExt.Test {
    74  				if request.Device != nil {
    75  					deviceCopy := *request.Device
    76  					request.Device = &deviceCopy
    77  				} else {
    78  					request.Device = &openrtb2.Device{}
    79  				}
    80  
    81  				request.Device.IP = "11.222.33.44"
    82  				request.Test = int8(1)
    83  			}
    84  
    85  			if raiExt.BidFloorCur != "" {
    86  				imp.BidFloorCur = raiExt.BidFloorCur
    87  			} else if imp.BidFloorCur == "" {
    88  				imp.BidFloorCur = "USD"
    89  			}
    90  		}
    91  		if isUrlSecure {
    92  			secure = int8(1)
    93  		}
    94  
    95  		imp.Secure = &secure
    96  
    97  		if imp.Banner != nil {
    98  			if imp.Banner.W == nil && imp.Banner.H == nil {
    99  				if len(imp.Banner.Format) == 0 {
   100  					errs = append(errs, &errortypes.BadInput{
   101  						Message: "request.Banner.Format is required",
   102  					})
   103  					continue
   104  				}
   105  			}
   106  		}
   107  
   108  		if imp.Video != nil {
   109  			if imp.Video.W == nil || *imp.Video.W == 0 || imp.Video.H == nil || *imp.Video.H == 0 {
   110  				errs = append(errs, &errortypes.BadInput{
   111  					Message: "request.Video.Sizes is required",
   112  				})
   113  				continue
   114  			}
   115  		}
   116  
   117  		request.Imp = []openrtb2.Imp{imp}
   118  
   119  		req, err := json.Marshal(request)
   120  		if err != nil {
   121  			errs = append(errs, &errortypes.BadInput{
   122  				Message: err.Error(),
   123  			})
   124  			continue
   125  		}
   126  
   127  		requestDataRequest = append(requestDataRequest, &adapters.RequestData{
   128  			Method:  "POST",
   129  			Uri:     a.endpoint,
   130  			Body:    req,
   131  			Headers: raiHeaders,
   132  			ImpIDs:  openrtb_ext.GetImpIDs(request.Imp),
   133  		})
   134  
   135  	}
   136  
   137  	return requestDataRequest, errs
   138  }
   139  
   140  func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   141  	if responseData.StatusCode == http.StatusNoContent {
   142  		return nil, nil
   143  	}
   144  
   145  	if responseData.StatusCode == http.StatusBadRequest {
   146  		err := &errortypes.BadInput{
   147  			Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
   148  		}
   149  		return nil, []error{err}
   150  	}
   151  
   152  	if responseData.StatusCode != http.StatusOK {
   153  		err := &errortypes.BadServerResponse{
   154  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
   155  		}
   156  		return nil, []error{err}
   157  	}
   158  
   159  	var bidReq openrtb2.BidRequest
   160  	if err := json.Unmarshal(requestData.Body, &bidReq); err != nil {
   161  		return nil, []error{&errortypes.BadServerResponse{
   162  			Message: err.Error(),
   163  		}}
   164  	}
   165  
   166  	var bidResp openrtb2.BidResponse
   167  	if err := json.Unmarshal(responseData.Body, &bidResp); err != nil {
   168  		return nil, []error{&errortypes.BadServerResponse{
   169  			Message: err.Error(),
   170  		}}
   171  	}
   172  
   173  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidReq.Imp))
   174  	bidResponse.Currency = bidResp.Cur
   175  
   176  	for _, reqBid := range bidReq.Imp {
   177  		for _, seatBid := range bidResp.SeatBid {
   178  			for i := range seatBid.Bid {
   179  
   180  				bidType := getMediaType(seatBid.Bid[i].ImpID, reqBid)
   181  
   182  				if bidType == "video" {
   183  					seatBid.Bid[i].W = *reqBid.Video.W
   184  					seatBid.Bid[i].H = *reqBid.Video.H
   185  				}
   186  
   187  				b := &adapters.TypedBid{
   188  					Bid:     &seatBid.Bid[i],
   189  					BidType: bidType,
   190  				}
   191  
   192  				bidResponse.Bids = append(bidResponse.Bids, b)
   193  			}
   194  		}
   195  	}
   196  
   197  	return bidResponse, nil
   198  }
   199  
   200  func setHeaders(raiHeaders *http.Header) {
   201  	raiHeaders.Set("Content-Type", "application/json;charset=utf-8")
   202  	raiHeaders.Set("Accept", "application/json")
   203  	raiHeaders.Add("X-Openrtb-Version", "2.5")
   204  }
   205  
   206  func getIsUrlSecure(request *openrtb2.BidRequest) (isUrlSecure bool) {
   207  	if request.Site != nil {
   208  		if request.Site.Page != "" {
   209  			pageURL, err := url.Parse(request.Site.Page)
   210  			if err == nil {
   211  				if request.Site.Domain == "" {
   212  					request.Site.Domain = pageURL.Host
   213  				}
   214  				isUrlSecure = pageURL.Scheme == "https"
   215  			}
   216  		}
   217  	}
   218  	return
   219  }
   220  
   221  func validateDevice(request *openrtb2.BidRequest) (err error) {
   222  	if request.Device != nil && request.Device.IP == "" && request.Device.IPv6 == "" {
   223  		err = &errortypes.BadInput{
   224  			Message: "request.Device.IP is required",
   225  		}
   226  		return err
   227  	}
   228  	return err
   229  }
   230  
   231  func parseImpExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpRichaudience, error) {
   232  	var bidderExt adapters.ExtImpBidder
   233  	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
   234  		err = &errortypes.BadInput{
   235  			Message: fmt.Sprintf("not found parameters ext in ImpID : %s", imp.ID),
   236  		}
   237  		return nil, err
   238  	}
   239  
   240  	var richaudienceExt openrtb_ext.ExtImpRichaudience
   241  	if err := json.Unmarshal(bidderExt.Bidder, &richaudienceExt); err != nil {
   242  		err = &errortypes.BadInput{
   243  			Message: fmt.Sprintf("invalid parameters ext in ImpID: %s", imp.ID),
   244  		}
   245  		return nil, err
   246  	}
   247  
   248  	return &richaudienceExt, nil
   249  }
   250  
   251  func getMediaType(impId string, imp openrtb2.Imp) openrtb_ext.BidType {
   252  	if imp.ID == impId {
   253  		if imp.Video != nil {
   254  			return openrtb_ext.BidTypeVideo
   255  		}
   256  		return openrtb_ext.BidTypeBanner
   257  	}
   258  	return "no bidtype assigned"
   259  }