diff --git a/endpoints/openrtb2/ctv_auction.go b/endpoints/openrtb2/ctv_auction.go index 602b431c490..9aa2ef331e7 100644 --- a/endpoints/openrtb2/ctv_auction.go +++ b/endpoints/openrtb2/ctv_auction.go @@ -421,10 +421,78 @@ func (deps *ctvEndpointDeps) createBidRequest(req *openrtb2.BidRequest) *openrtb //createImpressions ctvRequest.Imp = deps.createImpressions() + filterImpsVastTagsByDuration(&ctvRequest) + //TODO: remove adpod extension if not required to send further return &ctvRequest } +//filterImpsVastTagsByDuration checks if a Vast tag should be called for a generated impression based on the duration of tag and impression +func filterImpsVastTagsByDuration(bidReq *openrtb2.BidRequest) { + + allExtMap := make(map[string]map[string]map[string]interface{}) + for impCount, imp := range bidReq.Imp { + index := strings.LastIndex(imp.ID, "_") + if index == -1 { + continue + } + + originalImpID := imp.ID[:index] + + if _, exists := allExtMap[originalImpID]; !exists { + var impExt map[string]map[string]interface{} + if err := json.Unmarshal(imp.Ext, &impExt); err != nil { + continue + } + + allExtMap[originalImpID] = impExt + } + + impExtMap := allExtMap[originalImpID] + newImpExtMap := make(map[string]map[string]interface{}) + for k, v := range impExtMap { + newImpExtMap[k] = v + } + + for partnerName, partnerExt := range newImpExtMap { + if partnerExt["tags"] != nil { + impVastTags, ok := partnerExt["tags"].([]interface{}) + if !ok { + continue + } + + var compatibleVasts []interface{} + for _, tag := range impVastTags { + vastTag, ok := tag.(map[string]interface{}) + if !ok { + continue + } + + tagDuration := int(vastTag["dur"].(float64)) + if int(imp.Video.MinDuration) <= tagDuration && tagDuration <= int(imp.Video.MaxDuration) { + compatibleVasts = append(compatibleVasts, tag) + } + } + + if len(compatibleVasts) < 1 { + delete(newImpExtMap, partnerName) + } else { + newImpExtMap[partnerName] = map[string]interface{}{ + "tags": compatibleVasts, + } + } + + bExt, err := json.Marshal(newImpExtMap) + if err != nil { + continue + } + imp.Ext = bExt + } + } + bidReq.Imp[impCount] = imp + } +} + //getAllAdPodImpsConfigs will return all impression adpod configurations func (deps *ctvEndpointDeps) getAllAdPodImpsConfigs() { for index, imp := range deps.request.Imp { diff --git a/endpoints/openrtb2/ctv_auction_test.go b/endpoints/openrtb2/ctv_auction_test.go index 22ca7da4986..6ec48bcd35c 100644 --- a/endpoints/openrtb2/ctv_auction_test.go +++ b/endpoints/openrtb2/ctv_auction_test.go @@ -149,3 +149,76 @@ func TestAdjustBidIDInVideoEventTrackers(t *testing.T) { } } } + +func TestFilterImpsVastTagsByDuration(t *testing.T) { + tt := []struct { + testName string + inputBid openrtb2.BidRequest + expectedOutputBid openrtb2.BidRequest + }{ + { + testName: "test_single_impression_single_vast_partner", + inputBid: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp1_1", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 10}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":35},{"dur":25},{"dur":20}]}}`)}, + {ID: "imp1_2", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 20}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":35},{"dur":25},{"dur":20}]}}`)}, + {ID: "imp1_3", Video: &openrtb2.Video{MinDuration: 25, MaxDuration: 30}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":35},{"dur":25},{"dur":20}]}}`)}, + }, + }, + expectedOutputBid: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp1_1", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 10}, Ext: []byte(`{}`)}, + {ID: "imp1_2", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 20}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":20}]}}`)}, + {ID: "imp1_3", Video: &openrtb2.Video{MinDuration: 25, MaxDuration: 30}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":25}]}}`)}, + }, + }, + }, + { + testName: "test_single_impression_multiple_vast_partners", + inputBid: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp1_1", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 10}, Ext: []byte(`{"spotx_vast_bidder":{"tags":[{"dur":15},{"dur":25},{"dur":30}]},"openx_vast_bidder":{"tags":[{"dur":35},{"dur":25},{"dur":20}]}}`)}, + {ID: "imp1_2", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 20}, Ext: []byte(`{"spotx_vast_bidder":{"tags":[{"dur":15},{"dur":25},{"dur":30}]},"openx_vast_bidder":{"tags":[{"dur":35},{"dur":25},{"dur":20}]}}`)}, + {ID: "imp1_3", Video: &openrtb2.Video{MinDuration: 25, MaxDuration: 30}, Ext: []byte(`{"spotx_vast_bidder":{"tags":[{"dur":15},{"dur":25},{"dur":30}]},"openx_vast_bidder":{"tags":[{"dur":35},{"dur":25},{"dur":20}]}}`)}, + }, + }, + expectedOutputBid: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp1_1", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 10}, Ext: []byte(`{}`)}, + {ID: "imp1_2", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 20}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":20}]},"spotx_vast_bidder":{"tags":[{"dur":15}]}}`)}, + {ID: "imp1_3", Video: &openrtb2.Video{MinDuration: 25, MaxDuration: 30}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":25}]},"spotx_vast_bidder":{"tags":[{"dur":25},{"dur":30}]}}`)}, + }, + }, + }, + { + testName: "test_multi_impression_multi_partner", + inputBid: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp1_1", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 10}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":35},{"dur":25},{"dur":20}]}}`)}, + {ID: "imp1_2", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 20}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":35},{"dur":25},{"dur":20}]}}`)}, + {ID: "imp1_3", Video: &openrtb2.Video{MinDuration: 25, MaxDuration: 30}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":35},{"dur":25},{"dur":20}]}}`)}, + {ID: "imp2_1", Video: &openrtb2.Video{MinDuration: 5, MaxDuration: 30}, Ext: []byte(`{"spotx_vast_bidder":{"tags":[{"dur":30},{"dur":40}]}}`)}, + }, + }, + expectedOutputBid: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp1_1", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 10}, Ext: []byte(`{}`)}, + {ID: "imp1_2", Video: &openrtb2.Video{MinDuration: 10, MaxDuration: 20}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":20}]}}`)}, + {ID: "imp1_3", Video: &openrtb2.Video{MinDuration: 25, MaxDuration: 30}, Ext: []byte(`{"openx_vast_bidder":{"tags":[{"dur":25}]}}`)}, + {ID: "imp2_1", Video: &openrtb2.Video{MinDuration: 5, MaxDuration: 30}, Ext: []byte(`{"spotx_vast_bidder":{"tags":[{"dur":30}]}}`)}, + }, + }, + }, + } + + for _, tc := range tt { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + + outputBids := &tc.inputBid + filterImpsVastTagsByDuration(outputBids) + assert.Equal(t, tc.expectedOutputBid, *outputBids, "Expected length of impressions array was %d but actual was %d", tc.expectedOutputBid, outputBids) + }) + } +}