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

     1  package flipp
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"net/url"
     8  	"strings"
     9  
    10  	"github.com/buger/jsonparser"
    11  	"github.com/gofrs/uuid"
    12  	"github.com/prebid/openrtb/v19/openrtb2"
    13  	"github.com/prebid/prebid-server/adapters"
    14  	"github.com/prebid/prebid-server/config"
    15  	"github.com/prebid/prebid-server/openrtb_ext"
    16  )
    17  
    18  const (
    19  	bannerType      = "banner"
    20  	inlineDivName   = "inline"
    21  	flippBidder     = "flipp"
    22  	defaultCurrency = "USD"
    23  )
    24  
    25  var (
    26  	count    int64 = 1
    27  	adTypes        = []int64{4309, 641}
    28  	dtxTypes       = []int64{5061}
    29  )
    30  
    31  type adapter struct {
    32  	endpoint string
    33  }
    34  
    35  // Builder builds a new instance of the Flipp adapter for the given bidder with the given config.
    36  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    37  	bidder := &adapter{
    38  		endpoint: config.Endpoint,
    39  	}
    40  	return bidder, nil
    41  }
    42  
    43  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    44  	adapterRequests := make([]*adapters.RequestData, 0, len(request.Imp))
    45  	var errors []error
    46  
    47  	for _, imp := range request.Imp {
    48  		adapterReq, err := a.processImp(request, imp)
    49  		if err != nil {
    50  			errors = append(errors, err)
    51  			continue
    52  		}
    53  		adapterRequests = append(adapterRequests, adapterReq)
    54  	}
    55  	if len(adapterRequests) == 0 {
    56  		return nil, append(errors, fmt.Errorf("adapterRequest is empty"))
    57  	}
    58  	return adapterRequests, errors
    59  }
    60  
    61  func (a *adapter) makeRequest(request *openrtb2.BidRequest, campaignRequestBody CampaignRequestBody) (*adapters.RequestData, error) {
    62  	campaignRequestBodyJSON, err := json.Marshal(campaignRequestBody)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	headers := http.Header{}
    68  	headers.Add("Content-Type", "application/json")
    69  	if request.Device != nil && request.Device.UA != "" {
    70  		headers.Add("User-Agent", request.Device.UA)
    71  	}
    72  	return &adapters.RequestData{
    73  		Method:  http.MethodPost,
    74  		Uri:     a.endpoint,
    75  		Body:    campaignRequestBodyJSON,
    76  		Headers: headers,
    77  	}, err
    78  }
    79  
    80  func (a *adapter) processImp(request *openrtb2.BidRequest, imp openrtb2.Imp) (*adapters.RequestData, error) {
    81  	var flippExtParams openrtb_ext.ImpExtFlipp
    82  	params, _, _, err := jsonparser.Get(imp.Ext, "bidder")
    83  	if err != nil {
    84  		return nil, fmt.Errorf("flipp params not found. %v", err)
    85  	}
    86  	err = json.Unmarshal(params, &flippExtParams)
    87  	if err != nil {
    88  		return nil, fmt.Errorf("unable to extract flipp params. %v", err)
    89  	}
    90  
    91  	publisherUrl, err := url.Parse(request.Site.Page)
    92  	if err != nil {
    93  		return nil, fmt.Errorf("unable to parse site url. %v", err)
    94  	}
    95  
    96  	var contentCode string
    97  	if flippExtParams.Options.ContentCode != "" {
    98  		contentCode = flippExtParams.Options.ContentCode
    99  	} else if publisherUrl != nil {
   100  		contentCode = publisherUrl.Query().Get("flipp-content-code")
   101  	}
   102  
   103  	placement := Placement{
   104  		DivName: inlineDivName,
   105  		SiteID:  &flippExtParams.SiteID,
   106  		AdTypes: getAdTypes(flippExtParams.CreativeType),
   107  		ZoneIds: flippExtParams.ZoneIds,
   108  		Count:   &count,
   109  		Prebid:  buildPrebidRequest(flippExtParams, request, imp),
   110  		Properties: &Properties{
   111  			ContentCode: &contentCode,
   112  		},
   113  		Options: flippExtParams.Options,
   114  	}
   115  
   116  	var userIP string
   117  	if request.Device != nil && request.Device.IP != "" {
   118  		userIP = request.Device.IP
   119  	} else {
   120  		return nil, fmt.Errorf("no IP set in flipp bidder params or request device")
   121  	}
   122  
   123  	var userKey string
   124  	if request.User != nil && request.User.ID != "" {
   125  		userKey = request.User.ID
   126  	} else if flippExtParams.UserKey != "" {
   127  		userKey = flippExtParams.UserKey
   128  	} else {
   129  		uid, err := uuid.NewV4()
   130  		if err != nil {
   131  			return nil, fmt.Errorf("unable to generate user uuid. %v", err)
   132  		}
   133  		userKey = uid.String()
   134  	}
   135  
   136  	keywordsArray := strings.Split(request.Site.Keywords, ",")
   137  
   138  	campaignRequestBody := CampaignRequestBody{
   139  		Placements: []*Placement{&placement},
   140  		URL:        request.Site.Page,
   141  		Keywords:   keywordsArray,
   142  		IP:         userIP,
   143  		User: &CampaignRequestBodyUser{
   144  			Key: &userKey,
   145  		},
   146  	}
   147  
   148  	adapterReq, err := a.makeRequest(request, campaignRequestBody)
   149  	if err != nil {
   150  		return nil, fmt.Errorf("make request failed with err %v", err)
   151  	}
   152  
   153  	return adapterReq, nil
   154  }
   155  
   156  func buildPrebidRequest(flippExtParams openrtb_ext.ImpExtFlipp, request *openrtb2.BidRequest, imp openrtb2.Imp) *PrebidRequest {
   157  	var height int64
   158  	var width int64
   159  	if imp.Banner != nil && len(imp.Banner.Format) > 0 {
   160  		height = imp.Banner.Format[0].H
   161  		width = imp.Banner.Format[0].W
   162  	}
   163  	prebidRequest := PrebidRequest{
   164  		CreativeType:            &flippExtParams.CreativeType,
   165  		PublisherNameIdentifier: &flippExtParams.PublisherNameIdentifier,
   166  		RequestID:               &imp.ID,
   167  		Height:                  &height,
   168  		Width:                   &width,
   169  	}
   170  	return &prebidRequest
   171  }
   172  
   173  func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   174  	if adapters.IsResponseStatusCodeNoContent(responseData) {
   175  		return nil, nil
   176  	}
   177  
   178  	if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil {
   179  		return nil, []error{err}
   180  	}
   181  
   182  	var campaignResponseBody CampaignResponseBody
   183  	if err := json.Unmarshal(responseData.Body, &campaignResponseBody); err != nil {
   184  		return nil, []error{err}
   185  	}
   186  
   187  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
   188  	bidResponse.Currency = defaultCurrency
   189  	for _, imp := range request.Imp {
   190  		for _, decision := range campaignResponseBody.Decisions.Inline {
   191  			if *decision.Prebid.RequestID == imp.ID {
   192  				b := &adapters.TypedBid{
   193  					Bid:     buildBid(decision, imp.ID),
   194  					BidType: openrtb_ext.BidType(bannerType),
   195  				}
   196  				bidResponse.Bids = append(bidResponse.Bids, b)
   197  			}
   198  		}
   199  	}
   200  	return bidResponse, nil
   201  }
   202  
   203  func getAdTypes(creativeType string) []int64 {
   204  	if creativeType == "DTX" {
   205  		return dtxTypes
   206  	}
   207  	return adTypes
   208  }
   209  
   210  func buildBid(decision *InlineModel, impId string) *openrtb2.Bid {
   211  	bid := &openrtb2.Bid{
   212  		CrID:  fmt.Sprint(decision.CreativeID),
   213  		Price: *decision.Prebid.Cpm,
   214  		AdM:   *decision.Prebid.Creative,
   215  		ID:    fmt.Sprint(decision.AdID),
   216  		ImpID: impId,
   217  	}
   218  	if len(decision.Contents) > 0 || decision.Contents[0] != nil || decision.Contents[0].Data != nil {
   219  		if decision.Contents[0].Data.Width != 0 {
   220  			bid.W = decision.Contents[0].Data.Width
   221  		}
   222  		bid.H = 0
   223  	}
   224  	return bid
   225  }