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 }