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

     1  package gumgum
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strconv"
     8  	"strings"
     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/openrtb_ext"
    15  )
    16  
    17  // adapter implements Bidder interface.
    18  type adapter struct {
    19  	URI string
    20  }
    21  
    22  // MakeRequests makes the HTTP requests which should be made to fetch bids.
    23  func (g *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    24  	var validImps []openrtb2.Imp
    25  	var siteCopy openrtb2.Site
    26  	if request.Site != nil {
    27  		siteCopy = *request.Site
    28  	}
    29  
    30  	numRequests := len(request.Imp)
    31  	errs := make([]error, 0, numRequests)
    32  
    33  	for i := 0; i < numRequests; i++ {
    34  		imp := request.Imp[i]
    35  		gumgumExt, err := preprocess(&imp)
    36  		if err != nil {
    37  			errs = append(errs, err)
    38  		} else {
    39  			if gumgumExt.Zone != "" {
    40  				siteCopy.ID = gumgumExt.Zone
    41  			}
    42  
    43  			if gumgumExt.PubID != 0 {
    44  				if siteCopy.Publisher != nil {
    45  					siteCopy.Publisher.ID = strconv.FormatFloat(gumgumExt.PubID, 'f', -1, 64)
    46  				} else {
    47  					siteCopy.Publisher = &openrtb2.Publisher{ID: strconv.FormatFloat(gumgumExt.PubID, 'f', -1, 64)}
    48  				}
    49  			}
    50  
    51  			validImps = append(validImps, imp)
    52  		}
    53  	}
    54  
    55  	if len(validImps) == 0 {
    56  		return nil, errs
    57  	}
    58  
    59  	request.Imp = validImps
    60  
    61  	if request.Site != nil {
    62  		request.Site = &siteCopy
    63  	}
    64  
    65  	reqJSON, err := json.Marshal(request)
    66  	if err != nil {
    67  		errs = append(errs, err)
    68  		return nil, errs
    69  	}
    70  
    71  	headers := http.Header{}
    72  	headers.Add("Content-Type", "application/json;charset=utf-8")
    73  	headers.Add("Accept", "application/json")
    74  	return []*adapters.RequestData{{
    75  		Method:  "POST",
    76  		Uri:     g.URI,
    77  		Body:    reqJSON,
    78  		Headers: headers,
    79  		ImpIDs:  openrtb_ext.GetImpIDs(request.Imp),
    80  	}}, errs
    81  }
    82  
    83  // MakeBids unpacks the server's response into Bids.
    84  func (g *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
    85  	if response.StatusCode == http.StatusNoContent {
    86  		return nil, nil
    87  	}
    88  
    89  	if response.StatusCode == http.StatusBadRequest {
    90  		return nil, []error{&errortypes.BadInput{
    91  			Message: fmt.Sprintf("Bad user input: HTTP status %d", response.StatusCode),
    92  		}}
    93  	}
    94  
    95  	if response.StatusCode != http.StatusOK {
    96  		return nil, []error{&errortypes.BadServerResponse{
    97  			Message: fmt.Sprintf("Bad server response: HTTP status %d", response.StatusCode),
    98  		}}
    99  	}
   100  	var bidResp openrtb2.BidResponse
   101  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   102  		return nil, []error{&errortypes.BadServerResponse{
   103  			Message: fmt.Sprintf("Bad server response: %d. ", err),
   104  		}}
   105  	}
   106  
   107  	var errs []error
   108  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)
   109  
   110  	for _, sb := range bidResp.SeatBid {
   111  		for i := range sb.Bid {
   112  			mediaType := getMediaTypeForImpID(sb.Bid[i].ImpID, internalRequest.Imp)
   113  			if mediaType == openrtb_ext.BidTypeVideo {
   114  				price := strconv.FormatFloat(sb.Bid[i].Price, 'f', -1, 64)
   115  				sb.Bid[i].AdM = strings.Replace(sb.Bid[i].AdM, "${AUCTION_PRICE}", price, -1)
   116  			}
   117  
   118  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   119  				Bid:     &sb.Bid[i],
   120  				BidType: mediaType,
   121  			})
   122  		}
   123  	}
   124  
   125  	return bidResponse, errs
   126  }
   127  
   128  func preprocess(imp *openrtb2.Imp) (*openrtb_ext.ExtImpGumGum, error) {
   129  	var bidderExt adapters.ExtImpBidder
   130  	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
   131  		err = &errortypes.BadInput{
   132  			Message: err.Error(),
   133  		}
   134  		return nil, err
   135  	}
   136  
   137  	var gumgumExt openrtb_ext.ExtImpGumGum
   138  	if err := json.Unmarshal(bidderExt.Bidder, &gumgumExt); err != nil {
   139  		err = &errortypes.BadInput{
   140  			Message: err.Error(),
   141  		}
   142  		return nil, err
   143  	}
   144  
   145  	if imp.Banner != nil && imp.Banner.W == nil && imp.Banner.H == nil && len(imp.Banner.Format) > 0 {
   146  		bannerCopy := *imp.Banner
   147  		format := bannerCopy.Format[0]
   148  		bannerCopy.W = &(format.W)
   149  		bannerCopy.H = &(format.H)
   150  
   151  		if gumgumExt.Slot != 0 {
   152  			var err error
   153  			bannerExt := getBiggerFormat(bannerCopy.Format, gumgumExt.Slot)
   154  			bannerCopy.Ext, err = json.Marshal(&bannerExt)
   155  			if err != nil {
   156  				return nil, err
   157  			}
   158  		}
   159  
   160  		imp.Banner = &bannerCopy
   161  	}
   162  
   163  	if imp.Video != nil {
   164  		err := validateVideoParams(imp.Video)
   165  		if err != nil {
   166  			return nil, err
   167  		}
   168  
   169  		if gumgumExt.IrisID != "" {
   170  			videoCopy := *imp.Video
   171  			videoExt := openrtb_ext.ExtImpGumGumVideo{IrisID: gumgumExt.IrisID}
   172  			videoCopy.Ext, err = json.Marshal(&videoExt)
   173  			if err != nil {
   174  				return nil, err
   175  			}
   176  			imp.Video = &videoCopy
   177  		}
   178  	}
   179  
   180  	if gumgumExt.Product != "" {
   181  		var err error
   182  		imp.Ext, err = json.Marshal(map[string]string{"product": gumgumExt.Product})
   183  		if err != nil {
   184  			return nil, err
   185  		}
   186  	}
   187  
   188  	return &gumgumExt, nil
   189  }
   190  
   191  func getBiggerFormat(formatList []openrtb2.Format, slot float64) openrtb_ext.ExtImpGumGumBanner {
   192  	maxw := int64(0)
   193  	maxh := int64(0)
   194  	greatestVal := int64(0)
   195  	for _, size := range formatList {
   196  		var biggerSide int64
   197  		if size.W > size.H {
   198  			biggerSide = size.W
   199  		} else {
   200  			biggerSide = size.H
   201  		}
   202  
   203  		if biggerSide > greatestVal || (biggerSide == greatestVal && size.W >= maxw && size.H >= maxh) {
   204  			greatestVal = biggerSide
   205  			maxh = size.H
   206  			maxw = size.W
   207  		}
   208  	}
   209  
   210  	bannerExt := openrtb_ext.ExtImpGumGumBanner{Si: slot, MaxW: float64(maxw), MaxH: float64(maxh)}
   211  
   212  	return bannerExt
   213  }
   214  
   215  func getMediaTypeForImpID(impID string, imps []openrtb2.Imp) openrtb_ext.BidType {
   216  	for _, imp := range imps {
   217  		if imp.ID == impID && imp.Banner != nil {
   218  			return openrtb_ext.BidTypeBanner
   219  		}
   220  	}
   221  	return openrtb_ext.BidTypeVideo
   222  }
   223  
   224  func validateVideoParams(video *openrtb2.Video) (err error) {
   225  	if video.W == nil || *video.W == 0 || video.H == nil || *video.H == 0 || video.MinDuration == 0 || video.MaxDuration == 0 || video.Placement == 0 || video.Linearity == 0 {
   226  		return &errortypes.BadInput{
   227  			Message: "Invalid or missing video field(s)",
   228  		}
   229  	}
   230  	return nil
   231  }
   232  
   233  // Builder builds a new instance of the GumGum adapter for the given bidder with the given config.
   234  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
   235  	bidder := &adapter{
   236  		URI: config.Endpoint,
   237  	}
   238  	return bidder, nil
   239  }