From df05e99db117ed24c08e24ae55c90807e75c16af Mon Sep 17 00:00:00 2001 From: Jim Naumann Date: Mon, 9 Jul 2018 14:26:06 -0400 Subject: [PATCH] Beachfront bidder (#538) * copied beachfront bidder from defunct fork. * debugging cookies * debugging cookies * debugging cookies * debugging cookies * debugging cookies * debugging cookies * trying live endpoint because qa is returning non-usable stuff * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * back to qa * got a jam * is that how they want the bid response returned? What happens to bidType? * so adapters.bidResponse is just a wrapper? * so adapters.bidResponse is just a wrapper? * maybe that gets it? * maybe that gets it? * cleaned up debugging a bit * cleaned up debugging a bit * cleaned up debugging a bit * cleaned up debugging a bit * trying a config file * trying a config file * trying a config file * trying a config file * trying a config file * trying a config file * trying a config file * trying a config file * trying a config file * trying a config file * trying a config file * trying a config file * trying a config file * looking at publisher id as the key for getting __io_cid * looking at publisher id as the key for getting __io_cid * looking at publisher id as the key for getting __io_cid * looking at publisher id as the key for getting __io_cid * looking at publisher id as the key for getting __io_cid * looking at publisher id as the key for getting __io_cid * looking at publisher id as the key for getting __io_cid * The usersync endpoint was working yesterday! WHat the hell happened???? * The usersync endpoint was working yesterday! WHat the hell happened???? * The usersync endpoint was working yesterday! WHat the hell happened???? * simplifying usersync * simplifying usersync * simplifying usersync * switched banner request back to a string user and passing the publisher id in for that. * debugging * debugging * debugging * switched banner request back to a string user and passing the publisher id in for that. * switched to a configured usersync URL * typo in the syncer * typo in the syncer * messing with syncer url formatting * messing with syncer url formatting * messing with syncer url formatting * trying live endpoint * trying live endpoint worked - trying live nurl * some code cleanup - making the endpoint selection better * some code cleanup - making the endpoint selection better * some code cleanup - making the endpoint selection better * some code cleanup - making the endpoint selection better * removed some redundant debugging * removed some redundant debugging * trying a test endpoint * nope. * trying to use the built in cookie * trying to use the built in cookie * trying to use the built in cookie * trying to use the built in cookie * trying to use the built in cookie * trying to use the built in cookie * added cookie back to request * added cookie back to request * testing with skip_no_cookie=true * trying platform ID as sync ID. * straightening pout request cookies * Almost ready for my pull request. About to do a merge from upstream which may break it ALLgit add . * trying a default platfor_id * testing a host cookie * that didn't work * cleaned out some glog.Infos * trying multis * trying multis * trying multis * trying multis * trying multis * trying multis * trying multis * trying multis * trying multis * trying multis * trying multis * trying multis * trying multis * got multi banners working, but video still fails. * I've got the 'prefer video' behavior working correctly. Also, video is going to have to be single response only because that's what I get from Beachfront * a few changes to get mixed media working with aliases * added some error tests and multiple format tests * Might fix validation error #538 - The validation error came from this command on the Travis CI build: ./validate.sh --nofmt --cov --race 10 The command worked for me when I ran it locally on go version 1.10.1 and 1.9.3. * go version go1.9.3 darwin/amd64 * go version go1.10.1 darwin/amd64 * both installed with homebrew I then did a pull and ran the command on Beachfront's staging server go version go1.10 linux/amd64 That reproduced the error : gofmt needs to be run, 1 files have issues. Below is a list of files to review: adapters/beachfront/beachfront.go So I ran go fmt to see what it would fix. The diff of this commit will show that the problem was several conditionals where I, being new to Go, used parenthesis. The Darwin builds were letting that slide for whatever reason. The Linux builds were not (I assume that Travis CL is running in a Linux VM somewhere). * Eliminates the JSON template mechanism for the beachfront requests as commented by hhhjort for #538. Also corrected my accidental .gitignore commit. * Eliminates the JSON template mechanism for the beachfront Video requests. * No empty beachfrontReq.Imp by default - they all just get appended as needed. I'm still looking at how to do this using len(req.Imp) as suggested by @hhhjort. * Hit another 'go fmt' discrepency between Mac/Linux. * That straightens out the index clunckyness and gets the test cases back to matching what those produce. I still need to change that to slice. * eliminated some weirdness that resulted from working on this on 2 different boxes so that I can use Linux version of 'go fmt' * cleaned up some of logic related to video / banner requests * Another correction related to Mac/Linux go fmt - #538. This was a result of differences in comment formating. @dbemiller - note that this time when I ran go fmt on the Linux box it 'corrected' some comments in pbs_light.go. I'm not sure if that's OK, as it seems weird that, if the formating was 'wrong' on your master, then all travis CL tests would fail, and specificly brightroll's tests as the 'wrong' formating is in a commented out block of viper.SetDefault code. I've taken care of most of what @hhhjort pointed out, but still not quite there. I'll hold of on commiting though because this looks weird to me.. * forgot to add the len() based index to video. * reformated in Linux to match Travis CI * some documentation updates * Failed on a previous conflict resolution * added some documentation and parameter testing * migrated beachfront viper defaults into the updated config setup * reverted .gitignore to upstream master. I think that in the process of rmoving some changes that I made while debugging, to avoid pushing some notes I was keeping, I accidentally removed a line that I should not have and pushed it. (e6fdab368976c3555e5d389a330eee8559d0c05b, ) * Changed the endpoint to a named function. Also added 'expectedBids' to my video tests, but they don't actually seem to be processed. I can put known errors in and validation still passes. I tried putting a known error into the tests for another bidder's expectedBids, and also passed validation. So I'll go over these carefully to make sure that they look right, but I don't think that they are currently being run in the tests. * Moved regex out of the postProcessbanner function, so it will only get compiled once instead of recompiling fro evey banner * This covers all the points that dbemiller pointed out, except the errors for audio / native. So the post processing now works with a slice of bids instead packing and unpacking a seatbid etc. * eliminated the regex for the banner crid. I'm assuming that the string functions are less expensive than compiling a regex. * added race condition parameters and modified some of the Domain/Page logic * updated expectedBids for video to reflect changes to crid extraction * fixed an index out of range error in post process video response * Removed some dead code, some glog output and corrected error type for bad response. * ... except that error needs to be pushed to the stack, not just returned. * improved non-fatal error handling and some clean up --- adapters/beachfront/beachfront.go | 512 ++++++++++++++++++ adapters/beachfront/beachfront_test.go | 10 + .../exemplary/minimal-banner.json | 92 ++++ .../exemplary/simple-video.json | 151 ++++++ .../beachfronttest/params/race/banner.json | 4 + .../beachfronttest/params/race/video.json | 4 + .../supplemental/minimal-mobile-banner.json | 93 ++++ .../supplemental/minimal-mobile-video.json | 135 +++++ .../supplemental/multi-banner.json | 132 +++++ .../supplemental/multi-mix.json | 216 ++++++++ .../supplemental/multi-video.json | 168 ++++++ .../supplemental/unmarshal-error-banner.json | 34 ++ .../supplemental/unmarshal-error-video.json | 34 ++ adapters/beachfront/params_test.go | 42 ++ config/config.go | 2 + docs/bidders/beachfront.md | 6 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_beachfront.go | 6 + static/bidder-info/beachfront.yaml | 11 + static/bidder-params/beachfront.json | 18 + usersync/usersyncers/beachfront.go | 16 + usersync/usersyncers/beachfront_test.go | 15 + usersync/usersyncers/syncer.go | 3 +- 24 files changed, 1707 insertions(+), 1 deletion(-) create mode 100644 adapters/beachfront/beachfront.go create mode 100644 adapters/beachfront/beachfront_test.go create mode 100644 adapters/beachfront/beachfronttest/exemplary/minimal-banner.json create mode 100644 adapters/beachfront/beachfronttest/exemplary/simple-video.json create mode 100644 adapters/beachfront/beachfronttest/params/race/banner.json create mode 100644 adapters/beachfront/beachfronttest/params/race/video.json create mode 100644 adapters/beachfront/beachfronttest/supplemental/minimal-mobile-banner.json create mode 100644 adapters/beachfront/beachfronttest/supplemental/minimal-mobile-video.json create mode 100644 adapters/beachfront/beachfronttest/supplemental/multi-banner.json create mode 100644 adapters/beachfront/beachfronttest/supplemental/multi-mix.json create mode 100644 adapters/beachfront/beachfronttest/supplemental/multi-video.json create mode 100644 adapters/beachfront/beachfronttest/supplemental/unmarshal-error-banner.json create mode 100644 adapters/beachfront/beachfronttest/supplemental/unmarshal-error-video.json create mode 100644 adapters/beachfront/params_test.go create mode 100644 docs/bidders/beachfront.md create mode 100644 openrtb_ext/imp_beachfront.go create mode 100644 static/bidder-info/beachfront.yaml create mode 100644 static/bidder-params/beachfront.json create mode 100644 usersync/usersyncers/beachfront.go create mode 100644 usersync/usersyncers/beachfront_test.go diff --git a/adapters/beachfront/beachfront.go b/adapters/beachfront/beachfront.go new file mode 100644 index 00000000000..23e79c3ad69 --- /dev/null +++ b/adapters/beachfront/beachfront.go @@ -0,0 +1,512 @@ +package beachfront + +import ( + "encoding/json" + "fmt" + "github.com/mxmCherry/openrtb" + "github.com/pkg/errors" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" + "strings" +) + +const Seat = "beachfront" +const BidCapacity = 5 + +const BannerEndpoint = "https://display.bfmio.com/prebid_display" +const VideoEndpoint = "https://reachms.bfmio.com/bid.json?exchange_id=" +const VideoEndpointSuffix = "&prebidserver" + +const beachfrontAdapterName = "BF_PREBID_S2S" +const beachfrontAdapterVersion = "0.1.1" + +type BeachfrontAdapter struct { +} + +type BeachfrontRequests struct { + Banner BeachfrontBannerRequest + Video BeachfrontVideoRequest +} + +// --------------------------------------------------- +// Video +// --------------------------------------------------- + +type BeachfrontVideoRequest struct { + IsPrebid bool `json:"isPrebid"` + AppId string `json:"appId"` + Domain string `json:"domain"` + Id string `json:"id"` + Imp []BeachfrontVideoImp `json:"imp"` + Site BeachfrontSite `json:"site"` + Device BeachfrontVideoDevice `json:"device"` + User openrtb.User `json:"user"` + Cur []string `json:"cur"` +} + +type BeachfrontSite struct { + Page string `json:"page"` +} + +type BeachfrontPublisher struct { + Id string `json:"id"` +} + +type BeachfrontVideoDevice struct { + Ua string `json:"ua"` + Devicetype int `json:"deviceType"` + Geo BeachfrontVideoGeo `json:"geo"` +} + +type BeachfrontVideoGeo struct { + Ip string `json:"ip"` +} + +type BeachfrontVideoImp struct { + Video BeachfrontSize `json:"video"` + Bidfloor float64 `json:"bidfloor"` + Id int `json:"id"` + ImpId string `json:"impid"` +} + +// --------------------------------------------------- +// Banner +// --------------------------------------------------- +type BeachfrontBannerRequest struct { + Slots []BeachfrontSlot `json:"slots"` + Domain string `json:"domain"` + Page string `json:"page"` + Referrer string `json:"referrer"` + Search string `json:"search"` + Secure int8 `json:"secure"` + DeviceOs string `json:"deviceOs"` + DeviceModel string `json:"deviceModel"` + IsMobile int8 `json:"isMobile"` + Ua string `json:"ua"` + Dnt int8 `json:"dnt"` + User string `json:"user"` + AdapterName string `json:"adapterName"` + AdapterVersion string `json:"adapterVersion"` + Ip string `json:"ip"` +} + +type BeachfrontSlot struct { + Slot string `json:"slot"` + Id string `json:"id"` + Bidfloor float64 `json:"bidfloor"` + Sizes []BeachfrontSize `json:"sizes"` +} + +type BeachfrontSize struct { + W uint64 `json:"w"` + H uint64 `json:"h"` +} + +type BeachfrontResponseSlot struct { + CrID string `json:"crid"` + Price float64 `json:"price"` + W uint64 `json:"w"` + H uint64 `json:"h"` + Slot string `json:"slot"` + Adm string `json:"adm"` +} + +func NewBeachfrontBannerRequest() BeachfrontBannerRequest { + r := BeachfrontBannerRequest{} + r.AdapterName = beachfrontAdapterName + r.AdapterVersion = beachfrontAdapterVersion + + return r +} + +func NewBeachfrontVideoRequest() BeachfrontVideoRequest { + r := BeachfrontVideoRequest{} + r.Cur = append(r.Cur, "USD") + r.IsPrebid = true + + return r +} + +func getEndpoint(request *openrtb.BidRequest) (uri string) { + for i := range request.Imp { + if request.Imp[i].Video != nil { + // If there are any video imps, we will be running a video auction + // and dropping all of the banner actions. + return VideoEndpoint + } + } + return BannerEndpoint +} + +func (a *BeachfrontAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) { + var beachfrontRequests BeachfrontRequests + var reqJSON []byte + var uri string + var errs = make([]error, 0) + var err error + var imps int + + uri = getEndpoint(request) + + beachfrontRequests, errs, imps = preprocess(request, uri) + + // These are fatal errors ------------- + if uri == VideoEndpoint { + reqJSON, err = json.Marshal(beachfrontRequests.Video) + uri = uri + beachfrontRequests.Video.AppId + VideoEndpointSuffix + } else { + /* + We will get here if request contains no Video imps, though it might have + Audio or Native imps as well as banner. + */ + reqJSON, err = json.Marshal(beachfrontRequests.Banner) + } + + if imps == 0 { + errs = append(errs, errors.New("No valid impressions were found")) + return nil, errs + } + + if err != nil { + errs = append(errs, err) + return nil, errs + } + // ------------------------------------ + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + if uri == BannerEndpoint { + if request.User != nil { + headers.Add("Cookie", "UserID="+request.User.ID+"; __io_cid="+request.User.BuyerUID) + } + } + + return []*adapters.RequestData{{ + Method: "POST", + Uri: uri, + Body: reqJSON, + Headers: headers, + }}, errs +} + +/* +We have received a prebid request. It needs to be converted to a beachfront request. This is complicated +by the fact that we have different servers for video/display and they have different contracts. +*/ +func preprocess(req *openrtb.BidRequest, uri string) (BeachfrontRequests, []error, int) { + var beachfrontReqs BeachfrontRequests + var errs = make([]error, 0, len(req.Imp)) + var imps int + + if uri == BannerEndpoint { + beachfrontReqs.Banner, errs, imps = getBannerRequest(req) + } else { + // If there were any Video imps in the request, we have skipped to here. + beachfrontReqs.Video, errs, imps = getVideoRequest(req) + } + + return beachfrontReqs, errs, imps +} + +func getBannerRequest(req *openrtb.BidRequest) (BeachfrontBannerRequest, []error, int) { + var bannerImpsIndex int = 0 + var beachfrontReq BeachfrontBannerRequest = NewBeachfrontBannerRequest() + var errs = make([]error, 0, len(req.Imp)) + var imps int = 0 + + /* + step through the prebid request "imp" and inject into the beachfront request. If we got to here, + then we have already stepped through the requested imps and verified that none are Video, so no + reason to check that here, but there could be Audio or Native (or maybe they are filtered out before + I get here based on the capabilities in bidder-info/beachfront.yaml? Regardless, I'll leave + place holders here) . + */ + + for _, imp := range req.Imp { + if imp.Audio != nil { + errs = append(errs, &adapters.BadInputError{ + Message: fmt.Sprintf("Beachfront doesn't support audio Imps. Ignoring Imp ID=%s", imp.ID), + }) + continue + } else if imp.Native != nil { + errs = append(errs, &adapters.BadInputError{ + Message: fmt.Sprintf("Beachfront doesn't support native Imps. Ignoring Imp ID=%s", imp.ID), + }) + continue + } else if imp.Banner != nil { + beachfrontReq.Slots = append(beachfrontReq.Slots, BeachfrontSlot{}) + bannerImpsIndex = len(beachfrontReq.Slots) - 1 + + beachfrontReq.Slots[bannerImpsIndex].Sizes = append(beachfrontReq.Slots[bannerImpsIndex].Sizes, BeachfrontSize{}) + for j := range imp.Banner.Format { + if j > 0 { + beachfrontReq.Slots[bannerImpsIndex].Sizes = append(beachfrontReq.Slots[bannerImpsIndex].Sizes, BeachfrontSize{}) + } + beachfrontReq.Slots[bannerImpsIndex].Sizes[j].H = imp.Banner.Format[j].H + beachfrontReq.Slots[bannerImpsIndex].Sizes[j].W = imp.Banner.Format[j].W + } + + beachfrontReq.Slots[bannerImpsIndex].Bidfloor = imp.BidFloor + + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + // possible banner error 2 + errs = append(errs, err) + continue + } + + var beachfrontExt openrtb_ext.ExtImpBeachfront + if err := json.Unmarshal(bidderExt.Bidder, &beachfrontExt); err != nil { + // possible banner error 3 - supplemental/unmarshal-error-banner.json + errs = append(errs, err) + continue + } + + if req.Device != nil { + beachfrontReq.Ip = req.Device.IP + beachfrontReq.DeviceModel = req.Device.Model + beachfrontReq.DeviceOs = req.Device.OS + beachfrontReq.Dnt = req.Device.DNT + if req.Device.UA != "" { + beachfrontReq.Ua = req.Device.UA + } + } + + beachfrontReq.Slots[bannerImpsIndex].Slot = req.Imp[bannerImpsIndex].ID + beachfrontReq.Slots[bannerImpsIndex].Id = beachfrontExt.AppId + } + + imps++ + } + + if req.User != nil { + beachfrontReq.User = req.User.BuyerUID + } + + if req.App != nil { + beachfrontReq.Domain = req.App.Domain + beachfrontReq.Page = req.App.ID + } else { + beachfrontReq.Domain = strings.Split(strings.Split(req.Site.Page, "//")[1], "/")[0] + beachfrontReq.Page = req.Site.Page + } + + return beachfrontReq, errs, imps +} + +/* +Prepare the request that has been received from Prebid.js, translating it to the beachfront format +*/ +func getVideoRequest(req *openrtb.BidRequest) (BeachfrontVideoRequest, []error, int) { + var videoImpsIndex int = 0 + var beachfrontReq BeachfrontVideoRequest = NewBeachfrontVideoRequest() + var errs = make([]error, 0, len(req.Imp)) + var imps int = 0 + + /* + The req could contain banner,audio,native and video imps when It arrives here. I am only + interested in video + + The beach front video endpoint is only capable of returning a single nurl and price, wrapped in + an openrtb format, so even though I'm building a request here that will include multiple video + impressions, only a single URL will be returned. Hopefully the beachfront endpoint can be modified + in the future to return multiple video ads + + */ + for _, imp := range req.Imp { + if imp.Video != nil { + beachfrontReq.Id = req.ID + + beachfrontReq.Imp = append(beachfrontReq.Imp, BeachfrontVideoImp{}) + videoImpsIndex = len(beachfrontReq.Imp) - 1 + + beachfrontReq.Imp[videoImpsIndex].Video.H = imp.Video.H + beachfrontReq.Imp[videoImpsIndex].Video.W = imp.Video.W + + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + errs = append(errs, err) + continue + } + + var beachfrontVideoExt openrtb_ext.ExtImpBeachfront + if err := json.Unmarshal(bidderExt.Bidder, &beachfrontVideoExt); err != nil { + errs = append(errs, err) + continue + } + + beachfrontReq.Imp[videoImpsIndex].Bidfloor = beachfrontVideoExt.BidFloor + beachfrontReq.Imp[videoImpsIndex].Id = videoImpsIndex + beachfrontReq.Imp[videoImpsIndex].ImpId = imp.ID + + if req.Device != nil { + beachfrontReq.Device.Geo.Ip = req.Device.IP + beachfrontReq.Device.Ua = req.Device.UA + } + + beachfrontReq.AppId = beachfrontVideoExt.AppId + imps++ + } + } + + if req.User != nil { + if req.User.ID != "" { + // Exchange-specific ID for the user. At least one of id or + // buyeruid is recommended. + beachfrontReq.User.ID = req.User.ID + } + + if req.User.BuyerUID != "" { + // Buyer-specific ID for the user as mapped by the exchange for + // the buyer. At least one of buyeruid or id is recommended. + beachfrontReq.User.BuyerUID = req.User.BuyerUID + } + + } + + if req.App != nil { + if req.App.Domain != "" { + beachfrontReq.Domain = req.App.Domain + beachfrontReq.Site.Page = req.App.ID + } + } else { + if req.Site.Page != "" { + if req.Site.Domain == "" { + if strings.Contains(req.Site.Page, "//") { + // Remove protocol if exists + beachfrontReq.Domain = strings.Split(req.Site.Page, "//")[1] + } + if strings.Contains(beachfrontReq.Domain, "/") { + // Drop everything after the first "/" + beachfrontReq.Domain = strings.Split(beachfrontReq.Domain, "/")[0] + } + } else { + beachfrontReq.Domain = req.Site.Domain + } + beachfrontReq.Site.Page = req.Site.Page + } + } + + return beachfrontReq, errs, imps +} + +func (a *BeachfrontAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + var bids []openrtb.Bid + var bidtype openrtb_ext.BidType = getBidType(internalRequest) + var errors = make([]error, 0) + + bids, errs := postprocess(response, externalRequest, internalRequest.ID, bidtype) + + if len(errs) != 0 { + errors = append(errors, errs...) + err := &adapters.BadServerResponseError{ + Message: "Failed to process the beachfront response", + } + + errors = append(errors, err) + return nil, errors + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(BidCapacity) + + for i := 0; i < len(bids); i++ { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bids[i], + BidType: bidtype, + }) + } + + return bidResponse, errors +} + +func postprocess(response *adapters.ResponseData, externalRequest *adapters.RequestData, id string, bidtype openrtb_ext.BidType) ([]openrtb.Bid, []error) { + var beachfrontResp []BeachfrontResponseSlot + var errs = make([]error, 0) + + if bidtype == openrtb_ext.BidTypeVideo { + var openrtbResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &openrtbResp); err != nil { + errs = append(errs, err) + return nil, errs + } + return postprocessVideo(openrtbResp.SeatBid[0].Bid, externalRequest, id) + } else { + if err := json.Unmarshal(response.Body, &beachfrontResp); err != nil { + errs = append(errs, err) + return nil, errs + } + + return postprocessBanner(beachfrontResp, id) + } +} + +func postprocessBanner(beachfrontResp []BeachfrontResponseSlot, id string) ([]openrtb.Bid, []error) { + var bids []openrtb.Bid = make([]openrtb.Bid, len(beachfrontResp)) + var errs = make([]error, 0) + + for i := range beachfrontResp { + crid := extractBannerCrid(beachfrontResp[i].Adm) + + bids = append(bids, openrtb.Bid{ + CrID: crid, + ImpID: beachfrontResp[i].Slot, + Price: beachfrontResp[i].Price, + ID: id, + AdM: beachfrontResp[i].Adm, + H: beachfrontResp[i].H, + W: beachfrontResp[i].W, + }) + } + + // Am not adding any errors + return bids, errs +} + +func postprocessVideo(bids []openrtb.Bid, externalRequest *adapters.RequestData, id string) ([]openrtb.Bid, []error) { + var xtrnal BeachfrontVideoRequest + var errs = make([]error, 0) + + if err := json.Unmarshal(externalRequest.Body, &xtrnal); err != nil { + errs = append(errs, err) + return bids, errs + } + + for i := range bids { + crid := extractVideoCrid(bids[i].NURL) + + bids[i].CrID = crid + bids[i].ImpID = xtrnal.Imp[0].ImpId + bids[i].H = xtrnal.Imp[0].Video.H + bids[i].W = xtrnal.Imp[0].Video.W + bids[i].ID = id + } + + return bids, errs +} + +func extractBannerCrid(adm string) string { + chunky := strings.SplitAfter(adm, "\"") + return strings.TrimSuffix(chunky[1], "\"") +} + +func getBidType(internal *openrtb.BidRequest) openrtb_ext.BidType { + for i := range internal.Imp { + if internal.Imp[i].Video != nil { + return openrtb_ext.BidTypeVideo + } + } + + return openrtb_ext.BidTypeBanner +} + +func extractVideoCrid(nurl string) string { + chunky := strings.SplitAfter(nurl, ":") + return strings.TrimSuffix(chunky[2], ":") +} + +func NewBeachfrontBidder() *BeachfrontAdapter { + return &BeachfrontAdapter{} +} diff --git a/adapters/beachfront/beachfront_test.go b/adapters/beachfront/beachfront_test.go new file mode 100644 index 00000000000..0a76794d26a --- /dev/null +++ b/adapters/beachfront/beachfront_test.go @@ -0,0 +1,10 @@ +package beachfront + +import ( + "github.com/prebid/prebid-server/adapters/adapterstest" + "testing" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "beachfronttest", new(BeachfrontAdapter)) +} diff --git a/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json b/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json new file mode 100644 index 00000000000..50e92cb11d2 --- /dev/null +++ b/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json @@ -0,0 +1,92 @@ +{ + "mockBidRequest": { + "id": "some_test_ad", + "site": { + "page": "https://test.opposingviews.com/i/society/republican-sen-collins-may-change-vote-tax-bill?cb=1234534" + }, + "imp": [ + { + "bidfloor": 0.02, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "bidfloor": 0.02, + "appId": "3b16770b-17af-4d22-daff-9606bdf2c9c3" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://display.bfmio.com/prebid_display", + "body": { + "slots": [ + { + "slot": "", + "id": "3b16770b-17af-4d22-daff-9606bdf2c9c3", + "bidfloor": 0.02, + "sizes": [ + { + "w": 300, + "h": 250 + } + ] + } + ], + "domain": "test.opposingviews.com", + "page": "https://test.opposingviews.com/i/society/republican-sen-collins-may-change-vote-tax-bill?cb=1234534", + "referrer": "", + "search": "", + "secure": 0, + "isMobile": 0, + "ip": "", + "deviceModel": "", + "deviceOs": "", + "dnt": 0, + "ua": "", + "adapterName": "BF_PREBID_S2S", + "adapterVersion": "0.1.1", + "user": "" + } + }, + "mockResponse": { + "status": 200, + "body": [ + { + "crid":"crid_1", + "price":2.942808, + "w":300, + "h":250, + "slot":"div-gpt-ad-1460505748561-0", + "adm":"