From 81d33ead6e8723ce730c9b0528bdd6f73e8b1439 Mon Sep 17 00:00:00 2001 From: Serhii Nahornyi Date: Thu, 10 Dec 2020 13:59:25 +0200 Subject: [PATCH] RP adapter: use video placement parameter to set size ID (#1607) --- adapters/rubicon/rubicon.go | 50 +++++- adapters/rubicon/rubicon_test.go | 49 +++++- .../rubicontest/exemplary/simple-video.json | 159 ++++++++++++++++++ .../supplemental/required-video-size-id.json | 2 +- 4 files changed, 249 insertions(+), 11 deletions(-) create mode 100644 adapters/rubicon/rubicontest/exemplary/simple-video.json diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go index 08f492a7ae8..91211a61d4e 100644 --- a/adapters/rubicon/rubicon.go +++ b/adapters/rubicon/rubicon.go @@ -489,7 +489,18 @@ func (a *RubiconAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder * rubiReq.Device = &deviceCopy if thisImp.Video != nil { - videoExt := rubiconVideoExt{Skip: params.Video.Skip, SkipDelay: params.Video.SkipDelay, RP: rubiconVideoExtRP{SizeID: params.Video.VideoSizeID}} + + videoSizeId := params.Video.VideoSizeID + if videoSizeId == 0 { + resolvedSizeId, err := resolveVideoSizeId(thisImp.Video.Placement, thisImp.Instl, thisImp.ID) + if err == nil { + videoSizeId = resolvedSizeId + } else { + continue + } + } + + videoExt := rubiconVideoExt{Skip: params.Video.Skip, SkipDelay: params.Video.SkipDelay, RP: rubiconVideoExtRP{SizeID: videoSizeId}} thisImp.Video.Ext, err = json.Marshal(&videoExt) } else { primarySizeID, altSizeIDs, err := parseRubiconSizes(unit.Sizes) @@ -601,6 +612,24 @@ func (a *RubiconAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder * return bids, nil } +func resolveVideoSizeId(placement openrtb.VideoPlacementType, instl int8, impId string) (sizeID int, err error) { + if placement != 0 { + if placement == 1 { + return 201, nil + } + if placement == 3 { + return 203, nil + } + } + + if instl == 1 { + return 202, nil + } + return 0, &errortypes.BadInput{ + Message: fmt.Sprintf("video.size_id can not be resolved in impression with id : %s", impId), + } +} + func appendTrackerToUrl(uri string, tracker string) (res string) { // Append integration method. Adapter init happens once urlObject, err := url.Parse(uri) @@ -771,11 +800,16 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap isVideo := isVideo(thisImp) if isVideo { - if rubiconExt.Video.VideoSizeID == 0 { - errs = append(errs, &errortypes.BadInput{ - Message: fmt.Sprintf("imp[%d].ext.bidder.rubicon.video.size_id must be defined for video impression", i), - }) - continue + videoCopy := *thisImp.Video + + videoSizeId := rubiconExt.Video.VideoSizeID + if videoSizeId == 0 { + resolvedSizeId, err := resolveVideoSizeId(thisImp.Video.Placement, thisImp.Instl, thisImp.ID) + if err != nil { + errs = append(errs, err) + continue + } + videoSizeId = resolvedSizeId } // if imp.ext.is_rewarded_inventory = 1, set imp.video.ext.videotype = "rewarded" @@ -783,9 +817,7 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap if bidderExt.Prebid != nil && bidderExt.Prebid.IsRewardedInventory == 1 { videoType = "rewarded" } - - videoCopy := *thisImp.Video - videoExt := rubiconVideoExt{Skip: rubiconExt.Video.Skip, SkipDelay: rubiconExt.Video.SkipDelay, VideoType: videoType, RP: rubiconVideoExtRP{SizeID: rubiconExt.Video.VideoSizeID}} + videoExt := rubiconVideoExt{Skip: rubiconExt.Video.Skip, SkipDelay: rubiconExt.Video.SkipDelay, VideoType: videoType, RP: rubiconVideoExtRP{SizeID: videoSizeId}} videoCopy.Ext, err = json.Marshal(&videoExt) thisImp.Video = &videoCopy thisImp.Banner = nil diff --git a/adapters/rubicon/rubicon_test.go b/adapters/rubicon/rubicon_test.go index 9d99ec4dfe0..41e37f41126 100644 --- a/adapters/rubicon/rubicon_test.go +++ b/adapters/rubicon/rubicon_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "github.com/prebid/prebid-server/errortypes" "io/ioutil" "net/http" "net/http/httptest" @@ -278,7 +279,7 @@ func TestRubiconBasicResponse(t *testing.T) { bids, err := an.Call(ctx, pbReq, pbReq.Bidders[0]) assert.Nil(t, err, "Should not have gotten an error: %v", err) - assert.Equal(t, 3, len(bids), "Received %d bids instead of 3", len(bids)) + assert.Equal(t, 2, len(bids), "Received %d bids instead of 3", len(bids)) for _, bid := range bids { matched := false @@ -524,6 +525,52 @@ func TestAppendTracker(t *testing.T) { } } +func TestResolveVideoSizeId(t *testing.T) { + testScenarios := []struct { + placement openrtb.VideoPlacementType + instl int8 + impId string + expected int + expectedErr error + }{ + { + placement: 1, + instl: 1, + impId: "impId", + expected: 201, + expectedErr: nil, + }, + { + placement: 3, + instl: 1, + impId: "impId", + expected: 203, + expectedErr: nil, + }, + { + placement: 4, + instl: 1, + impId: "impId", + expected: 202, + expectedErr: nil, + }, + { + placement: 4, + instl: 3, + impId: "impId", + expectedErr: &errortypes.BadInput{ + Message: "video.size_id can not be resolved in impression with id : impId", + }, + }, + } + + for _, scenario := range testScenarios { + res, err := resolveVideoSizeId(scenario.placement, scenario.instl, scenario.impId) + assert.Equal(t, scenario.expected, res) + assert.Equal(t, scenario.expectedErr, err) + } +} + func TestNoContentResponse(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) diff --git a/adapters/rubicon/rubicontest/exemplary/simple-video.json b/adapters/rubicon/rubicontest/exemplary/simple-video.json new file mode 100644 index 00000000000..76c64ff95ec --- /dev/null +++ b/adapters/rubicon/rubicontest/exemplary/simple-video.json @@ -0,0 +1,159 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + }, + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "imp": [ + { + "id": "test-imp-id", + "instl": 1, + "video": { + "placement": 3, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 1024, + "h": 576 + }, + "ext": { + "bidder": { + "video": { + }, + "accountId": 1001, + "siteId":113932, + "zoneId":535510 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "uri?tk_xint=pbs-test-tracker", + "body": { + "id": "test-request-id", + "device": { + "ext": { + "rp": { + "pixelratio": 0 + } + }, + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + }, + "app": { + "id": "1", + "ext": { + "rp": { + "site_id": 113932 + } + }, + "publisher": { + "ext": { + "rp": { + "account_id": 1001 + } + } + }, + "bundle": "com.wls.testwlsapplication" + }, + "imp": [ + { + "id": "test-imp-id", + "instl": 1, + "video": { + "placement": 3, + "ext": { + "rp": { + "size_id": 203 + } + }, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 1024, + "h": 576 + }, + "ext": { + "rp": { + "track":{ + "mint": "", + "mint_version": "" + }, + "zone_id": 535510 + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "some-test-ad", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "ext": { + "prebid": { + "type": "video" + } + } + } + ], + "seat": "adman" + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "some-test-ad", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "ext": { + "prebid": { + "type": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/rubicon/rubicontest/supplemental/required-video-size-id.json b/adapters/rubicon/rubicontest/supplemental/required-video-size-id.json index cf0a594e13c..8e7482adfc0 100644 --- a/adapters/rubicon/rubicontest/supplemental/required-video-size-id.json +++ b/adapters/rubicon/rubicontest/supplemental/required-video-size-id.json @@ -24,7 +24,7 @@ }, "expectedMakeRequestsErrors": [ { - "value": "imp[0].ext.bidder.rubicon.video.size_id must be defined for video impression", + "value": "video.size_id can not be resolved in impression with id : test-imp-1", "comparison": "literal" } ]