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

     1  package beachfront
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/prebid/openrtb/v20/adcom1"
    12  	"github.com/prebid/openrtb/v20/openrtb2"
    13  	"github.com/prebid/prebid-server/v2/adapters"
    14  	"github.com/prebid/prebid-server/v2/config"
    15  	"github.com/prebid/prebid-server/v2/errortypes"
    16  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    17  	"github.com/prebid/prebid-server/v2/util/ptrutil"
    18  )
    19  
    20  const Seat = "beachfront"
    21  const BidCapacity = 5
    22  
    23  const defaultVideoEndpoint = "https://reachms.bfmio.com/bid.json?exchange_id"
    24  
    25  const nurlVideoEndpointSuffix = "&prebidserver"
    26  
    27  const beachfrontAdapterName = "BF_PREBID_S2S"
    28  const beachfrontAdapterVersion = "1.0.0"
    29  
    30  const minBidFloor = 0.01
    31  
    32  const defaultVideoWidth = 300
    33  const defaultVideoHeight = 250
    34  const fakeIP = "255.255.255.255"
    35  
    36  type BeachfrontAdapter struct {
    37  	bannerEndpoint string
    38  	extraInfo      ExtraInfo
    39  }
    40  
    41  type ExtraInfo struct {
    42  	VideoEndpoint string `json:"video_endpoint,omitempty"`
    43  }
    44  
    45  type beachfrontRequests struct {
    46  	Banner    beachfrontBannerRequest
    47  	NurlVideo []beachfrontVideoRequest
    48  	ADMVideo  []beachfrontVideoRequest
    49  }
    50  
    51  // ---------------------------------------------------
    52  //              Video
    53  // ---------------------------------------------------
    54  
    55  type beachfrontVideoRequest struct {
    56  	AppId             string              `json:"appId"`
    57  	VideoResponseType string              `json:"videoResponseType"`
    58  	Request           openrtb2.BidRequest `json:"request"`
    59  }
    60  
    61  // ---------------------------------------------------
    62  //
    63  //	Banner
    64  //
    65  // ---------------------------------------------------
    66  type beachfrontBannerRequest struct {
    67  	Slots          []beachfrontSlot     `json:"slots"`
    68  	Domain         string               `json:"domain"`
    69  	Page           string               `json:"page"`
    70  	Referrer       string               `json:"referrer"`
    71  	Search         string               `json:"search"`
    72  	Secure         int8                 `json:"secure"`
    73  	DeviceOs       string               `json:"deviceOs"`
    74  	DeviceModel    string               `json:"deviceModel"`
    75  	IsMobile       int8                 `json:"isMobile"`
    76  	UA             string               `json:"ua"`
    77  	Dnt            int8                 `json:"dnt"`
    78  	User           openrtb2.User        `json:"user"`
    79  	AdapterName    string               `json:"adapterName"`
    80  	AdapterVersion string               `json:"adapterVersion"`
    81  	IP             string               `json:"ip"`
    82  	RequestID      string               `json:"requestId"`
    83  	Real204        bool                 `json:"real204"`
    84  	SChain         openrtb2.SupplyChain `json:"schain,omitempty"`
    85  }
    86  
    87  type beachfrontSlot struct {
    88  	Slot     string           `json:"slot"`
    89  	Id       string           `json:"id"`
    90  	Bidfloor float64          `json:"bidfloor"`
    91  	Sizes    []beachfrontSize `json:"sizes"`
    92  }
    93  
    94  type beachfrontSize struct {
    95  	W uint64 `json:"w"`
    96  	H uint64 `json:"h"`
    97  }
    98  
    99  // ---------------------------------------------------
   100  // 				Banner response
   101  // ---------------------------------------------------
   102  
   103  type beachfrontResponseSlot struct {
   104  	CrID  string  `json:"crid"`
   105  	Price float64 `json:"price"`
   106  	W     uint64  `json:"w"`
   107  	H     uint64  `json:"h"`
   108  	Slot  string  `json:"slot"`
   109  	Adm   string  `json:"adm"`
   110  }
   111  
   112  type beachfrontVideoBidExtension struct {
   113  	Duration int `json:"duration"`
   114  }
   115  
   116  func (a *BeachfrontAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
   117  	beachfrontRequests, errs := preprocess(request, reqInfo)
   118  
   119  	headers := http.Header{}
   120  	headers.Add("Content-Type", "application/json;charset=utf-8")
   121  	headers.Add("Accept", "application/json")
   122  
   123  	if request.Device != nil {
   124  		if request.Device.UA != "" {
   125  			headers.Add("User-Agent", request.Device.UA)
   126  		}
   127  
   128  		if request.Device.Language != "" {
   129  			headers.Add("Accept-Language", request.Device.Language)
   130  		}
   131  
   132  		if request.Device.DNT != nil {
   133  			headers.Add("DNT", strconv.Itoa(int(*request.Device.DNT)))
   134  		}
   135  	}
   136  
   137  	var reqCount = len(beachfrontRequests.ADMVideo) + len(beachfrontRequests.NurlVideo)
   138  	if len(beachfrontRequests.Banner.Slots) > 0 {
   139  		reqCount++
   140  	}
   141  
   142  	var reqs = make([]*adapters.RequestData, reqCount)
   143  
   144  	var nurlBump = 0
   145  	var admBump = 0
   146  
   147  	if len(beachfrontRequests.Banner.Slots) > 0 {
   148  		bytes, err := json.Marshal(beachfrontRequests.Banner)
   149  
   150  		if err == nil {
   151  			reqs[0] = &adapters.RequestData{
   152  				Method:  "POST",
   153  				Uri:     a.bannerEndpoint,
   154  				Body:    bytes,
   155  				Headers: headers,
   156  				ImpIDs:  getBannerImpIDs(beachfrontRequests.Banner.Slots),
   157  			}
   158  
   159  			nurlBump++
   160  			admBump++
   161  		} else {
   162  			errs = append(errs, err)
   163  		}
   164  	}
   165  
   166  	if request.User != nil && request.User.BuyerUID != "" && reqCount > 0 {
   167  		headers.Add("Cookie", "__io_cid="+request.User.BuyerUID)
   168  	}
   169  
   170  	for j := 0; j < len(beachfrontRequests.ADMVideo); j++ {
   171  		bytes, err := json.Marshal(beachfrontRequests.ADMVideo[j].Request)
   172  		if err == nil {
   173  			reqs[j+nurlBump] = &adapters.RequestData{
   174  				Method:  "POST",
   175  				Uri:     a.extraInfo.VideoEndpoint + "=" + beachfrontRequests.ADMVideo[j].AppId,
   176  				Body:    bytes,
   177  				Headers: headers,
   178  				ImpIDs:  openrtb_ext.GetImpIDs(beachfrontRequests.ADMVideo[j].Request.Imp),
   179  			}
   180  
   181  			admBump++
   182  
   183  		} else {
   184  			errs = append(errs, err)
   185  		}
   186  	}
   187  
   188  	for j := 0; j < len(beachfrontRequests.NurlVideo); j++ {
   189  		bytes, err := json.Marshal(beachfrontRequests.NurlVideo[j].Request)
   190  
   191  		if err == nil {
   192  			bytes = append([]byte(`{"isPrebid":true,`), bytes[1:]...)
   193  			reqs[j+admBump] = &adapters.RequestData{
   194  				Method:  "POST",
   195  				Uri:     a.extraInfo.VideoEndpoint + "=" + beachfrontRequests.NurlVideo[j].AppId + nurlVideoEndpointSuffix,
   196  				Body:    bytes,
   197  				Headers: headers,
   198  				ImpIDs:  openrtb_ext.GetImpIDs(beachfrontRequests.NurlVideo[j].Request.Imp),
   199  			}
   200  		} else {
   201  			errs = append(errs, err)
   202  		}
   203  	}
   204  
   205  	return reqs, errs
   206  }
   207  
   208  func preprocess(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) (beachfrontReqs beachfrontRequests, errs []error) {
   209  	var videoImps = make([]openrtb2.Imp, 0)
   210  	var bannerImps = make([]openrtb2.Imp, 0)
   211  
   212  	for i := 0; i < len(request.Imp); i++ {
   213  		if request.Imp[i].Banner != nil && request.Imp[i].Banner.Format != nil &&
   214  			request.Imp[i].Banner.Format[0].H != 0 && request.Imp[i].Banner.Format[0].W != 0 {
   215  			bannerImps = append(bannerImps, request.Imp[i])
   216  		}
   217  
   218  		if request.Imp[i].Video != nil {
   219  			videoImps = append(videoImps, request.Imp[i])
   220  		}
   221  	}
   222  
   223  	if len(bannerImps)+len(videoImps) == 0 {
   224  		errs = append(errs, errors.New("no valid impressions were found in the request"))
   225  		return
   226  	}
   227  
   228  	if len(bannerImps) > 0 {
   229  		request.Imp = bannerImps
   230  		beachfrontReqs.Banner, errs = getBannerRequest(request, reqInfo)
   231  	}
   232  
   233  	if len(videoImps) > 0 {
   234  		var videoErrs []error
   235  		var videoList []beachfrontVideoRequest
   236  
   237  		request.Imp = videoImps
   238  		request.Ext = nil
   239  
   240  		videoList, videoErrs = getVideoRequests(request, reqInfo)
   241  		errs = append(errs, videoErrs...)
   242  
   243  		for i := 0; i < len(videoList); i++ {
   244  			if videoList[i].VideoResponseType == "nurl" {
   245  				beachfrontReqs.NurlVideo = append(beachfrontReqs.NurlVideo, videoList[i])
   246  			}
   247  
   248  			if videoList[i].VideoResponseType == "adm" {
   249  				beachfrontReqs.ADMVideo = append(beachfrontReqs.ADMVideo, videoList[i])
   250  			}
   251  		}
   252  	}
   253  
   254  	return
   255  }
   256  
   257  func getAppId(ext openrtb_ext.ExtImpBeachfront, media openrtb_ext.BidType) (string, error) {
   258  	var appid string
   259  	var error error
   260  
   261  	if ext.AppId != "" {
   262  		appid = ext.AppId
   263  	} else if media == openrtb_ext.BidTypeVideo && ext.AppIds.Video != "" {
   264  		appid = ext.AppIds.Video
   265  	} else if media == openrtb_ext.BidTypeBanner && ext.AppIds.Banner != "" {
   266  		appid = ext.AppIds.Banner
   267  	} else {
   268  		error = errors.New("unable to determine the appId(s) from the supplied extension")
   269  	}
   270  
   271  	return appid, error
   272  }
   273  
   274  func getSchain(request *openrtb2.BidRequest) (openrtb_ext.ExtRequestPrebidSChain, error) {
   275  	var schain openrtb_ext.ExtRequestPrebidSChain
   276  	return schain, json.Unmarshal(request.Source.Ext, &schain)
   277  }
   278  
   279  func getBannerRequest(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) (beachfrontBannerRequest, []error) {
   280  	var bfr beachfrontBannerRequest
   281  	var errs = make([]error, 0, len(request.Imp))
   282  
   283  	for i := 0; i < len(request.Imp); i++ {
   284  
   285  		beachfrontExt, err := getBeachfrontExtension(request.Imp[i])
   286  
   287  		if err != nil {
   288  			errs = append(errs, err)
   289  			continue
   290  		}
   291  
   292  		appid, err := getAppId(beachfrontExt, openrtb_ext.BidTypeBanner)
   293  
   294  		if err != nil {
   295  			errs = append(errs, err)
   296  			continue
   297  		}
   298  
   299  		if fatal, err := setBidFloor(&beachfrontExt, &request.Imp[i], reqInfo); err != nil {
   300  			errs = append(errs, err)
   301  			if fatal {
   302  				continue
   303  			}
   304  		}
   305  
   306  		slot := beachfrontSlot{
   307  			Id:       appid,
   308  			Slot:     request.Imp[i].ID,
   309  			Bidfloor: request.Imp[i].BidFloor,
   310  		}
   311  
   312  		for j := 0; j < len(request.Imp[i].Banner.Format); j++ {
   313  
   314  			slot.Sizes = append(slot.Sizes, beachfrontSize{
   315  				H: uint64(request.Imp[i].Banner.Format[j].H),
   316  				W: uint64(request.Imp[i].Banner.Format[j].W),
   317  			})
   318  		}
   319  
   320  		bfr.Slots = append(bfr.Slots, slot)
   321  	}
   322  
   323  	if len(bfr.Slots) == 0 {
   324  		return bfr, errs
   325  	}
   326  
   327  	if request.Device != nil {
   328  		bfr.IP = request.Device.IP
   329  		bfr.DeviceModel = request.Device.Model
   330  		bfr.DeviceOs = request.Device.OS
   331  		if request.Device.DNT != nil {
   332  			bfr.Dnt = *request.Device.DNT
   333  		}
   334  		if request.Device.UA != "" {
   335  			bfr.UA = request.Device.UA
   336  		}
   337  	}
   338  
   339  	var t = fallBackDeviceType(request)
   340  
   341  	if t == adcom1.DeviceMobile {
   342  		bfr.Page = request.App.Bundle
   343  		if request.App.Domain == "" {
   344  			bfr.Domain = getDomain(request.App.Domain)
   345  		} else {
   346  			bfr.Domain = request.App.Domain
   347  		}
   348  
   349  		bfr.IsMobile = 1
   350  	} else if t == adcom1.DevicePC {
   351  		bfr.Page = request.Site.Page
   352  		if request.Site.Domain == "" {
   353  			bfr.Domain = getDomain(request.Site.Page)
   354  		} else {
   355  			bfr.Domain = request.Site.Domain
   356  		}
   357  
   358  		bfr.IsMobile = 0
   359  	}
   360  
   361  	bfr.Secure = isSecure(bfr.Page)
   362  
   363  	if request.User != nil && request.User.ID != "" {
   364  		if bfr.User.ID == "" {
   365  			bfr.User.ID = request.User.ID
   366  		}
   367  	}
   368  
   369  	if request.User != nil && request.User.BuyerUID != "" {
   370  		if bfr.User.BuyerUID == "" {
   371  			bfr.User.BuyerUID = request.User.BuyerUID
   372  		}
   373  	}
   374  
   375  	bfr.RequestID = request.ID
   376  	bfr.AdapterName = beachfrontAdapterName
   377  	bfr.AdapterVersion = beachfrontAdapterVersion
   378  
   379  	if request.Imp[0].Secure != nil {
   380  		bfr.Secure = *request.Imp[0].Secure
   381  	}
   382  	bfr.Real204 = true
   383  
   384  	if request.Source != nil && request.Source.Ext != nil {
   385  		schain, err := getSchain(request)
   386  		if err == nil {
   387  			bfr.SChain = schain.SChain
   388  		}
   389  	}
   390  
   391  	return bfr, errs
   392  }
   393  
   394  func fallBackDeviceType(request *openrtb2.BidRequest) adcom1.DeviceType {
   395  	if request.Site != nil {
   396  		return adcom1.DevicePC
   397  	}
   398  
   399  	return adcom1.DeviceMobile
   400  }
   401  
   402  func getVideoRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]beachfrontVideoRequest, []error) {
   403  	var bfReqs = make([]beachfrontVideoRequest, len(request.Imp))
   404  	var errs = make([]error, 0, len(request.Imp))
   405  	var failedRequestIndicies = make([]int, 0)
   406  
   407  	for i := 0; i < len(request.Imp); i++ {
   408  		beachfrontExt, err := getBeachfrontExtension(request.Imp[i])
   409  
   410  		if err != nil {
   411  			failedRequestIndicies = append(failedRequestIndicies, i)
   412  			errs = append(errs, err)
   413  			continue
   414  		}
   415  
   416  		appid, err := getAppId(beachfrontExt, openrtb_ext.BidTypeVideo)
   417  		bfReqs[i].AppId = appid
   418  
   419  		if err != nil {
   420  			failedRequestIndicies = append(failedRequestIndicies, i)
   421  			errs = append(errs, err)
   422  			continue
   423  		}
   424  
   425  		bfReqs[i].Request = *request
   426  		var secure int8
   427  
   428  		var deviceCopy openrtb2.Device
   429  		if bfReqs[i].Request.Device == nil {
   430  			deviceCopy = openrtb2.Device{}
   431  		} else {
   432  			deviceCopy = *bfReqs[i].Request.Device
   433  		}
   434  
   435  		if beachfrontExt.VideoResponseType == "nurl" {
   436  			bfReqs[i].VideoResponseType = "nurl"
   437  		} else {
   438  			bfReqs[i].VideoResponseType = "adm"
   439  
   440  			if deviceCopy.IP == "" {
   441  				deviceCopy.IP = fakeIP
   442  			}
   443  		}
   444  
   445  		if bfReqs[i].Request.Site != nil && bfReqs[i].Request.Site.Domain == "" && bfReqs[i].Request.Site.Page != "" {
   446  			siteCopy := *bfReqs[i].Request.Site
   447  			siteCopy.Domain = getDomain(bfReqs[i].Request.Site.Page)
   448  			bfReqs[i].Request.Site = &siteCopy
   449  			secure = isSecure(bfReqs[i].Request.Site.Page)
   450  		}
   451  
   452  		if bfReqs[i].Request.App != nil && bfReqs[i].Request.App.Domain == "" && bfReqs[i].Request.App.Bundle != "" {
   453  			if bfReqs[i].Request.App.Bundle != "" {
   454  				var chunks = strings.Split(strings.Trim(bfReqs[i].Request.App.Bundle, "_"), ".")
   455  
   456  				if len(chunks) > 1 {
   457  					appCopy := *bfReqs[i].Request.App
   458  					appCopy.Domain = fmt.Sprintf("%s.%s", chunks[len(chunks)-(len(chunks)-1)], chunks[0])
   459  					bfReqs[i].Request.App = &appCopy
   460  				}
   461  			}
   462  		}
   463  
   464  		if deviceCopy.DeviceType == 0 {
   465  			deviceCopy.DeviceType = fallBackDeviceType(request)
   466  		}
   467  		bfReqs[i].Request.Device = &deviceCopy
   468  
   469  		imp := request.Imp[i]
   470  
   471  		imp.Banner = nil
   472  		imp.Ext = nil
   473  		imp.Secure = &secure
   474  		if fatal, err := setBidFloor(&beachfrontExt, &imp, reqInfo); err != nil {
   475  			errs = append(errs, err)
   476  			if fatal {
   477  				failedRequestIndicies = append(failedRequestIndicies, i)
   478  				continue
   479  			}
   480  		}
   481  
   482  		wNilOrZero := imp.Video.W == nil || *imp.Video.W == 0
   483  		hNilOrZero := imp.Video.H == nil || *imp.Video.H == 0
   484  		if wNilOrZero || hNilOrZero {
   485  			videoCopy := *imp.Video
   486  
   487  			if wNilOrZero {
   488  				videoCopy.W = ptrutil.ToPtr[int64](defaultVideoWidth)
   489  			}
   490  
   491  			if hNilOrZero {
   492  				videoCopy.H = ptrutil.ToPtr[int64](defaultVideoHeight)
   493  			}
   494  
   495  			imp.Video = &videoCopy
   496  		}
   497  
   498  		if len(bfReqs[i].Request.Cur) == 0 {
   499  			bfReqs[i].Request.Cur = make([]string, 1)
   500  			bfReqs[i].Request.Cur[0] = "USD"
   501  		}
   502  
   503  		bfReqs[i].Request.Imp = nil
   504  		bfReqs[i].Request.Imp = make([]openrtb2.Imp, 1)
   505  		bfReqs[i].Request.Imp[0] = imp
   506  
   507  	}
   508  
   509  	if len(failedRequestIndicies) > 0 {
   510  		for i := 0; i < len(failedRequestIndicies); i++ {
   511  			bfReqs = removeVideoElement(bfReqs, failedRequestIndicies[i])
   512  		}
   513  
   514  	}
   515  	return bfReqs, errs
   516  }
   517  
   518  func (a *BeachfrontAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   519  	if response.StatusCode == http.StatusNoContent {
   520  		return nil, nil
   521  	}
   522  
   523  	if response.StatusCode >= http.StatusInternalServerError {
   524  		return nil, []error{&errortypes.BadServerResponse{
   525  			Message: fmt.Sprintf("server error status code %d from %s. Run with request.debug = 1 for more info", response.StatusCode, externalRequest.Uri),
   526  		}}
   527  	}
   528  
   529  	if response.StatusCode >= http.StatusBadRequest {
   530  		return nil, []error{&errortypes.BadInput{
   531  			Message: fmt.Sprintf("request error status code %d from %s. Run with request.debug = 1 for more info", response.StatusCode, externalRequest.Uri),
   532  		}}
   533  	}
   534  
   535  	if response.StatusCode != http.StatusOK {
   536  		return nil, []error{fmt.Errorf("unexpected status code %d from %s. Run with request.debug = 1 for more info", response.StatusCode, externalRequest.Uri)}
   537  	}
   538  
   539  	var bids []openrtb2.Bid
   540  	var errs = make([]error, 0)
   541  	var xtrnal openrtb2.BidRequest
   542  
   543  	if err := json.Unmarshal(externalRequest.Body, &xtrnal); err != nil {
   544  		errs = append(errs, err)
   545  	} else {
   546  		bids, errs = postprocess(response, xtrnal, externalRequest.Uri, internalRequest.ID)
   547  	}
   548  
   549  	if len(errs) != 0 {
   550  		return nil, errs
   551  	}
   552  
   553  	var dur beachfrontVideoBidExtension
   554  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(BidCapacity)
   555  
   556  	for i := 0; i < len(bids); i++ {
   557  
   558  		if err := json.Unmarshal(bids[i].Ext, &dur); err == nil && dur.Duration > 0 {
   559  
   560  			impVideo := openrtb_ext.ExtBidPrebidVideo{
   561  				Duration: int(dur.Duration),
   562  			}
   563  
   564  			if len(bids[i].Cat) > 0 {
   565  				impVideo.PrimaryCategory = bids[i].Cat[0]
   566  			}
   567  
   568  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   569  				Bid:      &bids[i],
   570  				BidType:  a.getBidType(externalRequest),
   571  				BidVideo: &impVideo,
   572  			})
   573  		} else {
   574  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   575  				Bid:     &bids[i],
   576  				BidType: a.getBidType(externalRequest),
   577  			})
   578  		}
   579  	}
   580  
   581  	return bidResponse, errs
   582  }
   583  
   584  func setBidFloor(ext *openrtb_ext.ExtImpBeachfront, imp *openrtb2.Imp, reqInfo *adapters.ExtraRequestInfo) (bool, error) {
   585  	var initialImpBidfloor float64 = imp.BidFloor
   586  	var err error
   587  
   588  	if imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" && imp.BidFloor > 0 {
   589  		imp.BidFloor, err = reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD")
   590  
   591  		var convertedFromCurrency = imp.BidFloorCur
   592  		imp.BidFloorCur = "USD"
   593  
   594  		if err != nil {
   595  			if ext.BidFloor > minBidFloor {
   596  				imp.BidFloor = ext.BidFloor
   597  				return false, &errortypes.Warning{
   598  					Message: fmt.Sprintf("The following error was recieved from the currency converter while attempting to convert the imp.bidfloor value of %.2f from %s to USD:\n%s\nThe provided value of imp.ext.beachfront.bidfloor, %.2f USD is being used as a fallback.",
   599  						initialImpBidfloor,
   600  						convertedFromCurrency,
   601  						err,
   602  						ext.BidFloor,
   603  					),
   604  				}
   605  			} else {
   606  				return true, &errortypes.BadInput{
   607  					Message: fmt.Sprintf("The following error was recieved from the currency converter while attempting to convert the imp.bidfloor value of %.2f from %s to USD:\n%s\nA value of imp.ext.beachfront.bidfloor was not provided. The bid is being skipped.",
   608  						initialImpBidfloor,
   609  						convertedFromCurrency,
   610  						err,
   611  					),
   612  				}
   613  			}
   614  		}
   615  	}
   616  
   617  	if imp.BidFloor < ext.BidFloor {
   618  		imp.BidFloor = ext.BidFloor
   619  	}
   620  
   621  	if imp.BidFloor > minBidFloor {
   622  		imp.BidFloorCur = "USD"
   623  	} else {
   624  		imp.BidFloor = 0
   625  		imp.BidFloorCur = ""
   626  	}
   627  
   628  	return false, nil
   629  }
   630  
   631  func (a *BeachfrontAdapter) getBidType(externalRequest *adapters.RequestData) openrtb_ext.BidType {
   632  	t := strings.Split(externalRequest.Uri, "=")[0]
   633  	if t == a.extraInfo.VideoEndpoint {
   634  		return openrtb_ext.BidTypeVideo
   635  	}
   636  
   637  	return openrtb_ext.BidTypeBanner
   638  }
   639  
   640  func postprocess(response *adapters.ResponseData, xtrnal openrtb2.BidRequest, uri string, id string) ([]openrtb2.Bid, []error) {
   641  	var beachfrontResp []beachfrontResponseSlot
   642  
   643  	var openrtbResp openrtb2.BidResponse
   644  
   645  	if err := json.Unmarshal(response.Body, &openrtbResp); err != nil || len(openrtbResp.SeatBid) == 0 {
   646  
   647  		if err := json.Unmarshal(response.Body, &beachfrontResp); err != nil {
   648  			return nil, []error{&errortypes.BadServerResponse{
   649  				Message: "server response failed to unmarshal as valid rtb. Run with request.debug = 1 for more info",
   650  			}}
   651  		} else {
   652  			return postprocessBanner(beachfrontResp, id)
   653  		}
   654  	}
   655  
   656  	return postprocessVideo(openrtbResp.SeatBid[0].Bid, xtrnal, uri, id)
   657  }
   658  
   659  func postprocessBanner(beachfrontResp []beachfrontResponseSlot, id string) ([]openrtb2.Bid, []error) {
   660  
   661  	var bids = make([]openrtb2.Bid, len(beachfrontResp))
   662  	var errs = make([]error, 0)
   663  
   664  	for i := 0; i < len(beachfrontResp); i++ {
   665  		bids[i] = openrtb2.Bid{
   666  			CrID:  beachfrontResp[i].CrID,
   667  			ImpID: beachfrontResp[i].Slot,
   668  			Price: beachfrontResp[i].Price,
   669  			ID:    fmt.Sprintf("%sBanner", beachfrontResp[i].Slot),
   670  			AdM:   beachfrontResp[i].Adm,
   671  			H:     int64(beachfrontResp[i].H),
   672  			W:     int64(beachfrontResp[i].W),
   673  		}
   674  	}
   675  
   676  	return bids, errs
   677  }
   678  
   679  func postprocessVideo(bids []openrtb2.Bid, xtrnal openrtb2.BidRequest, uri string, id string) ([]openrtb2.Bid, []error) {
   680  
   681  	var errs = make([]error, 0)
   682  
   683  	if uri[len(uri)-len(nurlVideoEndpointSuffix):] == nurlVideoEndpointSuffix {
   684  
   685  		for i := 0; i < len(bids); i++ {
   686  			crid := extractNurlVideoCrid(bids[i].NURL)
   687  
   688  			bids[i].CrID = crid
   689  			bids[i].ImpID = xtrnal.Imp[i].ID
   690  			bids[i].H = ptrutil.ValueOrDefault(xtrnal.Imp[i].Video.H)
   691  			bids[i].W = ptrutil.ValueOrDefault(xtrnal.Imp[i].Video.W)
   692  			bids[i].ID = fmt.Sprintf("%sNurlVideo", xtrnal.Imp[i].ID)
   693  		}
   694  
   695  	} else {
   696  		for i := 0; i < len(bids); i++ {
   697  			bids[i].ID = fmt.Sprintf("%sAdmVideo", bids[i].ImpID)
   698  		}
   699  
   700  	}
   701  	return bids, errs
   702  }
   703  
   704  func extractNurlVideoCrid(nurl string) string {
   705  	chunky := strings.SplitAfter(nurl, ":")
   706  	if len(chunky) > 1 {
   707  		return strings.TrimSuffix(chunky[2], ":")
   708  	}
   709  
   710  	return ""
   711  }
   712  
   713  func getBeachfrontExtension(imp openrtb2.Imp) (openrtb_ext.ExtImpBeachfront, error) {
   714  	var err error
   715  	var bidderExt adapters.ExtImpBidder
   716  	var beachfrontExt openrtb_ext.ExtImpBeachfront
   717  
   718  	if err = json.Unmarshal(imp.Ext, &bidderExt); err != nil {
   719  		return beachfrontExt, &errortypes.BadInput{
   720  			Message: fmt.Sprintf("ignoring imp id=%s, error while decoding extImpBidder, err: %s", imp.ID, err),
   721  		}
   722  	}
   723  
   724  	if err = json.Unmarshal(bidderExt.Bidder, &beachfrontExt); err != nil {
   725  		return beachfrontExt, &errortypes.BadInput{
   726  			Message: fmt.Sprintf("ignoring imp id=%s, error while decoding extImpBeachfront, err: %s", imp.ID, err),
   727  		}
   728  	}
   729  
   730  	return beachfrontExt, err
   731  }
   732  
   733  func getDomain(page string) string {
   734  	protoURL := strings.Split(page, "//")
   735  	var domainPage string
   736  
   737  	if len(protoURL) > 1 {
   738  		domainPage = protoURL[1]
   739  	} else {
   740  		domainPage = protoURL[0]
   741  	}
   742  
   743  	return strings.Split(domainPage, "/")[0]
   744  
   745  }
   746  
   747  func isSecure(page string) int8 {
   748  	protoURL := strings.Split(page, "://")
   749  
   750  	if len(protoURL) > 1 && protoURL[0] == "https" {
   751  		return 1
   752  	}
   753  
   754  	return 0
   755  
   756  }
   757  
   758  func removeVideoElement(slice []beachfrontVideoRequest, s int) []beachfrontVideoRequest {
   759  	if len(slice) >= s+1 {
   760  		return append(slice[:s], slice[s+1:]...)
   761  	}
   762  
   763  	return []beachfrontVideoRequest{}
   764  }
   765  
   766  // Builder builds a new instance of the Beachfront adapter for the given bidder with the given config.
   767  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
   768  	extraInfo, err := getExtraInfo(config.ExtraAdapterInfo)
   769  	if err != nil {
   770  		return nil, err
   771  	}
   772  
   773  	bidder := &BeachfrontAdapter{
   774  		bannerEndpoint: config.Endpoint,
   775  		extraInfo:      extraInfo,
   776  	}
   777  	return bidder, nil
   778  }
   779  
   780  func getExtraInfo(v string) (ExtraInfo, error) {
   781  	if len(v) == 0 {
   782  		return getDefaultExtraInfo(), nil
   783  	}
   784  
   785  	var extraInfo ExtraInfo
   786  	if err := json.Unmarshal([]byte(v), &extraInfo); err != nil {
   787  		return extraInfo, fmt.Errorf("invalid extra info: %v", err)
   788  	}
   789  
   790  	if extraInfo.VideoEndpoint == "" {
   791  		extraInfo.VideoEndpoint = defaultVideoEndpoint
   792  	}
   793  
   794  	return extraInfo, nil
   795  }
   796  
   797  func getDefaultExtraInfo() ExtraInfo {
   798  	return ExtraInfo{
   799  		VideoEndpoint: defaultVideoEndpoint,
   800  	}
   801  }
   802  
   803  func getBannerImpIDs(bfs []beachfrontSlot) []string {
   804  	impIDs := make([]string, len(bfs))
   805  	for i := range bfs {
   806  		impIDs[i] = bfs[i].Slot
   807  	}
   808  	return impIDs
   809  }