diff --git a/analytics/pubmatic/record.go b/analytics/pubmatic/record.go index a583fafb725..2fd1081199d 100644 --- a/analytics/pubmatic/record.go +++ b/analytics/pubmatic/record.go @@ -211,6 +211,8 @@ func (wlog *WloggerRecord) logIntegrationType(endpoint string) { wlog.IntegrationType = models.TypeInline case models.EndpointORTB: wlog.IntegrationType = models.TypeS2S + case models.EndpointWebS2S: + wlog.IntegrationType = models.TypeWebS2S } } diff --git a/analytics/pubmatic/record_test.go b/analytics/pubmatic/record_test.go index 9a07a2ce62a..1826e1f8cb1 100644 --- a/analytics/pubmatic/record_test.go +++ b/analytics/pubmatic/record_test.go @@ -214,6 +214,11 @@ func TestLogIntegrationType(t *testing.T) { endpoint: "invalid", integrationType: "", }, + { + name: "ows2s", + endpoint: models.EndpointWebS2S, + integrationType: models.TypeWebS2S, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/endpoints/events/vtrack_ow.go b/endpoints/events/vtrack_ow.go index 3e2a7b8a81e..303554cd6b3 100644 --- a/endpoints/events/vtrack_ow.go +++ b/endpoints/events/vtrack_ow.go @@ -59,8 +59,8 @@ func InjectVideoEventTrackers(trackerURL, vastXML string, bid *openrtb2.Bid, pre doc := etree.NewDocument() err := doc.ReadFromString(vastXML) if nil != err { - err = fmt.Errorf("Error parsing VAST XML. '%v'", err.Error()) - glog.Errorf(err.Error()) + err = fmt.Errorf("account:[%s] bidder:[%s] err:[vast_xml_parsing_failed:%s] vast:[%s] ", accountID, requestingBidder, err.Error(), vastXML) + glog.Error(err.Error()) return []byte(vastXML), false, err // false indicates events trackers are not injected } diff --git a/exchange/exchange.go b/exchange/exchange.go index 6b5fbc487f6..d89afee1587 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -448,7 +448,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog for _, seatBid := range adapterBids { for _, pbsBid := range seatBid.Bids { pbsBid.GeneratedBidID, err = e.bidIDGenerator.New() - glog.Infof("Original BidID = %s Generated BidID = %s", pbsBid.Bid.ID, pbsBid.GeneratedBidID) + glog.V(3).Infof("Original BidID = %s Generated BidID = %s", pbsBid.Bid.ID, pbsBid.GeneratedBidID) if err != nil { errs = append(errs, errors.New("Error generating bid.ext.prebid.bidid")) } diff --git a/exchange/exchange_ow.go b/exchange/exchange_ow.go index db7bb06aa78..f2bd186fcc5 100644 --- a/exchange/exchange_ow.go +++ b/exchange/exchange_ow.go @@ -60,7 +60,7 @@ func recordAdaptorDuplicateBidIDs(metricsEngine metrics.MetricsEngine, adapterBi if collisions, ok := bidIDColisionMap[thisBid.Bid.ID]; ok { bidIDCollisionFound = true bidIDColisionMap[thisBid.Bid.ID]++ - glog.Warningf("Bid.id %v :: %v collision(s) [imp.id = %v] for bidder '%v'", thisBid.Bid.ID, collisions, thisBid.Bid.ImpID, string(bidder)) + glog.V(3).Infof("Bid.id %v :: %v collision(s) [imp.id = %v] for bidder '%v'", thisBid.Bid.ID, collisions, thisBid.Bid.ImpID, string(bidder)) metricsEngine.RecordAdapterDuplicateBidID(string(bidder), 1) } else { bidIDColisionMap[thisBid.Bid.ID] = 1 diff --git a/modules/pubmatic/openwrap/auctionresponsehook.go b/modules/pubmatic/openwrap/auctionresponsehook.go index 09abd3bd8a8..9a8859c6edc 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook.go +++ b/modules/pubmatic/openwrap/auctionresponsehook.go @@ -27,12 +27,12 @@ func (m OpenWrap) handleAuctionResponseHook( // absence of rctx at this hook means the first hook failed!. Do nothing if len(moduleCtx.ModuleContext) == 0 { - result.DebugMessages = append(result.DebugMessages, "error: module-ctx not found in handleBeforeValidationHook()") + result.DebugMessages = append(result.DebugMessages, "error: module-ctx not found in handleAuctionResponseHook()") return result, nil } rctx, ok := moduleCtx.ModuleContext["rctx"].(models.RequestCtx) if !ok { - result.DebugMessages = append(result.DebugMessages, "error: request-ctx not found in handleBeforeValidationHook()") + result.DebugMessages = append(result.DebugMessages, "error: request-ctx not found in handleAuctionResponseHook()") return result, nil } @@ -40,9 +40,6 @@ func (m OpenWrap) handleAuctionResponseHook( if rctx.Sshb == "1" || rctx.Endpoint == models.EndpointHybrid { return result, nil } - if rctx.Endpoint == models.EndpointOWS2S { - return result, nil - } defer func() { moduleCtx.ModuleContext["rctx"] = rctx @@ -283,6 +280,19 @@ func (m OpenWrap) handleAuctionResponseHook( result.DebugMessages = append(result.DebugMessages, string(rCtxBytes)) } + if rctx.Endpoint == models.EndpointWebS2S { + result.ChangeSet.AddMutation(func(ap hookstage.AuctionResponsePayload) (hookstage.AuctionResponsePayload, error) { + rctx := moduleCtx.ModuleContext["rctx"].(models.RequestCtx) + var err error + ap.BidResponse, err = tracker.InjectTrackers(rctx, ap.BidResponse) + if err == nil { + resetBidIdtoOriginal(ap.BidResponse) + } + return ap, err + }, hookstage.MutationUpdate, "response-body-with-webs2s-format") + return result, nil + } + result.ChangeSet.AddMutation(func(ap hookstage.AuctionResponsePayload) (hookstage.AuctionResponsePayload, error) { rctx := moduleCtx.ModuleContext["rctx"].(models.RequestCtx) var err error diff --git a/modules/pubmatic/openwrap/auctionresponsehook_test.go b/modules/pubmatic/openwrap/auctionresponsehook_test.go index 2f98e480edb..ce2aa400b96 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook_test.go +++ b/modules/pubmatic/openwrap/auctionresponsehook_test.go @@ -10,8 +10,10 @@ import ( "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/openrtb/v19/openrtb3" "github.com/prebid/prebid-server/hooks/hookstage" + mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/tbf" "github.com/prebid/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -1134,3 +1136,156 @@ func TestResetBidIdtoOriginal(t *testing.T) { }) } } + +func TestAuctionResponseHookForEndpointWebS2S(t *testing.T) { + ctrl := gomock.NewController(t) + mockCache := mock_cache.NewMockCache(ctrl) + tbf.Init(1, mockCache) + defer func() { + ctrl.Finish() + tbf.StopTBFReloaderService() + }() + + type args struct { + ctx context.Context + moduleCtx hookstage.ModuleInvocationContext + payload hookstage.AuctionResponsePayload + } + + type want struct { + bidResponse *openrtb2.BidResponse + err error + } + + tests := []struct { + name string + args args + want want + getMetricsEngine func() *mock_metrics.MockMetricsEngine + }{ + { + name: "inject_tracker_in_respose_for_WebS2S_endpoint", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + Endpoint: models.EndpointWebS2S, + Trackers: map[string]models.OWTracker{ + "bid1": { + BidType: models.Video, + }, + }, + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: ``, + }, + }, + }, + }, + }, + }, + }, + want: want{ + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: "
"}, + }, + }, + }, + }, + err: nil, + }, + getMetricsEngine: func() *mock_metrics.MockMetricsEngine { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats(gomock.Any(), gomock.Any(), gomock.Any()) + mockEngine.EXPECT().RecordNobidErrPrebidServerResponse(gomock.Any()) + mockEngine.EXPECT().RecordPublisherResponseTimeStats(gomock.Any(), gomock.Any()) + return mockEngine + }, + }, + { + name: "inject_tracker_in_respose_and_reset_bidID_to_orignal_for_WebS2S_endpoint", + args: args{ + ctx: nil, + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + Endpoint: models.EndpointWebS2S, + Trackers: map[string]models.OWTracker{ + "bid1": { + BidType: models.Video, + }, + }, + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345:: 123422222225", + AdM: ``, + }, + }, + }, + }, + }, + }, + }, + want: want{ + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: "
"}, + }, + }, + }, + }, + err: nil, + }, + getMetricsEngine: func() *mock_metrics.MockMetricsEngine { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats(gomock.Any(), gomock.Any(), gomock.Any()) + mockEngine.EXPECT().RecordNobidErrPrebidServerResponse(gomock.Any()) + mockEngine.EXPECT().RecordPublisherResponseTimeStats(gomock.Any(), gomock.Any()) + return mockEngine + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := OpenWrap{ + metricEngine: tt.getMetricsEngine(), + cache: mockCache, + } + mockCache.EXPECT().GetTBFTrafficForPublishers().Return(map[int]map[int]int{1: {2: 3}}, nil).AnyTimes() + hookResult, err := o.handleAuctionResponseHook(tt.args.ctx, tt.args.moduleCtx, tt.args.payload) + assert.Equal(t, tt.want.err, err, tt.name) + mutations := hookResult.ChangeSet.Mutations() + assert.NotEmpty(t, mutations, tt.name) + for _, mut := range mutations { + result, err := mut.Apply(tt.args.payload) + assert.Nil(t, err, tt.name) + assert.Equal(t, tt.want.bidResponse, result.BidResponse, tt.name) + } + }) + } +} diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go index ee9bfb60ca8..fa637eb5fa4 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook.go +++ b/modules/pubmatic/openwrap/beforevalidationhook.go @@ -191,7 +191,7 @@ func (m OpenWrap) handleBeforeValidationHook( return result, err } } - if rCtx.Endpoint == models.EndpointOWS2S { + if rCtx.Endpoint == models.EndpointWebS2S { imp.TagID = getTagID(imp, impExt) } if imp.TagID == "" { @@ -258,7 +258,8 @@ func (m OpenWrap) handleBeforeValidationHook( bannerAdUnitCtx = adunitconfig.UpdateBannerObjectWithAdunitConfig(rCtx, imp, div) } - if !isSlotEnabled(videoAdUnitCtx, bannerAdUnitCtx) { + // ignore adunit config status for native as it is not supported for native + if (!isSlotEnabled(videoAdUnitCtx, bannerAdUnitCtx)) && imp.Native == nil { disabledSlots++ rCtx.ImpBidCtx[imp.ID] = models.ImpCtx{ // for wrapper logger sz @@ -910,6 +911,10 @@ func (m OpenWrap) setTimeout(rCtx models.RequestCtx, req *openrtb2.BidRequest) i // if ssauction flag is not set and platform is dislay, then by default send all bids // if ssauction flag is not set and platform is in-app, then check if profile setting sendAllBids is set to 1 func isSendAllBids(rctx models.RequestCtx) bool { + //for webs2s endpoint SendAllBids is always true + if rctx.Endpoint == models.EndpointWebS2S { + return true + } //if ssauction is set to 0 in the request if rctx.SSAuction == 0 { return true diff --git a/modules/pubmatic/openwrap/beforevalidationhook_test.go b/modules/pubmatic/openwrap/beforevalidationhook_test.go index 1806961a26a..45a560c9e40 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook_test.go +++ b/modules/pubmatic/openwrap/beforevalidationhook_test.go @@ -550,6 +550,15 @@ func TestIsSendAllBids(t *testing.T) { args args want bool }{ + { + name: "sendallbids_always_true_for_webs2s_endpoint", + args: args{ + rctx: models.RequestCtx{ + Endpoint: models.EndpointWebS2S, + }, + }, + want: true, + }, { name: "Don't_do_ssauction", args: args{ @@ -2203,7 +2212,7 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { ModuleContext: hookstage.ModuleContext{ "rctx": func() models.RequestCtx { testRctx := rctx - testRctx.Endpoint = models.EndpointOWS2S + testRctx.Endpoint = models.EndpointWebS2S return testRctx }(), }, @@ -2232,9 +2241,9 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { mockCache.EXPECT().GetAdunitConfigFromCache(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&adunitconfig.AdUnitConfig{}) //prometheus metrics mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") - mockEngine.EXPECT().RecordBadRequests(models.EndpointOWS2S, getPubmaticErrorCode(nbr.InvalidImpressionTagID)) + mockEngine.EXPECT().RecordBadRequests(models.EndpointWebS2S, getPubmaticErrorCode(nbr.InvalidImpressionTagID)) mockEngine.EXPECT().RecordNobidErrPrebidServerRequests("5890", nbr.InvalidImpressionTagID) - mockEngine.EXPECT().RecordPublisherRequests(models.EndpointOWS2S, "5890", rctx.Platform) + mockEngine.EXPECT().RecordPublisherRequests(models.EndpointWebS2S, "5890", rctx.Platform) }, want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ Reject: true, @@ -2288,7 +2297,7 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { wantErr: true, }, { - name: "allSotsDisabled", + name: "allSotsDisabled-native-not-present", args: args{ ctx: context.Background(), moduleCtx: hookstage.ModuleInvocationContext{ @@ -2375,6 +2384,111 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { }, wantErr: false, }, + { + name: "allSotsDisabled-native-present", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + ProfileID: 1234, + DisplayID: 1, + SSAuction: -1, + Platform: "in-app", + Debug: true, + UA: "go-test", + IP: "127.0.0.1", + IsCTVRequest: false, + TrackerEndpoint: "t.pubmatic.com", + VideoErrorTrackerEndpoint: "t.pubmatic.com/error", + UidCookie: &http.Cookie{ + Name: "uids", + Value: `eyJ0ZW1wVUlEcyI6eyIzM2Fjcm9zcyI6eyJ1aWQiOiIxMTkxNzkxMDk5Nzc2NjEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTo0My4zODg4Nzk5NVoifSwiYWRmIjp7InVpZCI6IjgwNDQ2MDgzMzM3Nzg4MzkwNzgiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMS4wMzMwNTQ3MjdaIn0sImFka2VybmVsIjp7InVpZCI6IkE5MTYzNTAwNzE0OTkyOTMyOTkwIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuMzczMzg1NjYyWiJ9LCJhZGtlcm5lbEFkbiI6eyJ1aWQiOiJBOTE2MzUwMDcxNDk5MjkzMjk5MCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEzLjQzNDkyNTg5NloifSwiYWRtaXhlciI6eyJ1aWQiOiIzNjZhMTdiMTJmMjI0ZDMwOGYzZTNiOGRhOGMzYzhhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjU5MjkxNDgwMVoifSwiYWRueHMiOnsidWlkIjoiNDE5Mjg5ODUzMDE0NTExOTMiLCJleHBpcmVzIjoiMjAyMy0wMS0xOFQwOTo1MzowOC44MjU0NDI2NzZaIn0sImFqYSI6eyJ1aWQiOiJzMnN1aWQ2RGVmMFl0bjJveGQ1aG9zS1AxVmV3IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTMuMjM5MTc2MDU0WiJ9LCJlcGxhbm5pbmciOnsidWlkIjoiQUoxRjBTOE5qdTdTQ0xWOSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjkyOTk2MDQ3M1oifSwiZ2Ftb3NoaSI6eyJ1aWQiOiJndXNyXzM1NmFmOWIxZDhjNjQyYjQ4MmNiYWQyYjdhMjg4MTYxIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuNTI0MTU3MjI1WiJ9LCJncmlkIjp7InVpZCI6IjRmYzM2MjUwLWQ4NTItNDU5Yy04NzcyLTczNTZkZTE3YWI5NyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE0LjY5NjMxNjIyN1oifSwiZ3JvdXBtIjp7InVpZCI6IjdENzVEMjVGLUZBQzktNDQzRC1CMkQxLUIxN0ZFRTExRTAyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjM5LjIyNjIxMjUzMloifSwiaXgiOnsidWlkIjoiWW9ORlNENlc5QkphOEh6eEdtcXlCUUFBXHUwMDI2Mjk3IiwiZXhwaXJlcyI6IjIwMjMtMDUtMzFUMDc6NTM6MzguNTU1ODI3MzU0WiJ9LCJqaXhpZSI6eyJ1aWQiOiI3MzY3MTI1MC1lODgyLTExZWMtYjUzOC0xM2FjYjdhZjBkZTQiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi4xOTEwOTk3MzJaIn0sImxvZ2ljYWQiOnsidWlkIjoiQVZ4OVROQS11c25pa3M4QURzTHpWa3JvaDg4QUFBR0JUREh0UUEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS40NTUxNDk2MTZaIn0sIm1lZGlhbmV0Ijp7InVpZCI6IjI5Nzg0MjM0OTI4OTU0MTAwMDBWMTAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMy42NzIyMTUxMjhaIn0sIm1naWQiOnsidWlkIjoibTU5Z1hyN0xlX1htIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTcuMDk3MDAxNDcxWiJ9LCJuYW5vaW50ZXJhY3RpdmUiOnsidWlkIjoiNmFlYzhjMTAzNzlkY2I3ODQxMmJjODBiNmRkOWM5NzMxNzNhYjdkNzEyZTQzMWE1YTVlYTcwMzRlNTZhNThhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE2LjcxNDgwNzUwNVoifSwib25ldGFnIjp7InVpZCI6IjdPelZoVzFOeC1LOGFVak1HMG52NXVNYm5YNEFHUXZQbnVHcHFrZ3k0ckEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS4xNDE3NDEyNjJaIn0sIm9wZW54Ijp7InVpZCI6IjVkZWNlNjIyLTBhMjMtMGRhYi0zYTI0LTVhNzcwMTBlNDU4MiIsImV4cGlyZXMiOiIyMDIzLTA1LTMxVDA3OjUyOjQ3LjE0MDQxNzM2M1oifSwicHVibWF0aWMiOnsidWlkIjoiN0Q3NUQyNUYtRkFDOS00NDNELUIyRDEtQjE3RkVFMTFFMDI3IiwiZXhwaXJlcyI6IjIwMjItMTAtMzFUMDk6MTQ6MjUuNzM3MjU2ODk5WiJ9LCJyaWNoYXVkaWVuY2UiOnsidWlkIjoiY2I2YzYzMjAtMzNlMi00Nzc0LWIxNjAtMXp6MTY1NDg0MDc0OSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjUyNTA3NDE4WiJ9LCJzbWFydHlhZHMiOnsidWlkIjoiMTJhZjE1ZTQ0ZjAwZDA3NjMwZTc0YzQ5MTU0Y2JmYmE0Zjg0N2U4ZDRhMTU0YzhjM2Q1MWY1OGNmNzJhNDYyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjgyNTAzMTg4NFoifSwic21pbGV3YW50ZWQiOnsidWlkIjoiZGQ5YzNmZTE4N2VmOWIwOWNhYTViNzExNDA0YzI4MzAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNC4yNTU2MDkzNjNaIn0sInN5bmFjb3JtZWRpYSI6eyJ1aWQiOiJHRFBSIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuOTc5NTgzNDM4WiJ9LCJ0cmlwbGVsaWZ0Ijp7InVpZCI6IjcwMjE5NzUwNTQ4MDg4NjUxOTQ2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA4Ljk4OTY3MzU3NFoifSwidmFsdWVpbXByZXNzaW9uIjp7InVpZCI6IjlkMDgxNTVmLWQ5ZmUtNGI1OC04OThlLWUyYzU2MjgyYWIzZSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjA2NzgzOTE2NFoifSwidmlzeCI6eyJ1aWQiOiIyN2UwYWMzYy1iNDZlLTQxYjMtOTkyYy1mOGQyNzE0OTQ5NWUiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi45ODk1MjM1NzNaIn0sInlpZWxkbGFiIjp7InVpZCI6IjY5NzE0ZDlmLWZiMDAtNGE1Zi04MTljLTRiZTE5MTM2YTMyNSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjExLjMwMzAyNjYxNVoifSwieWllbGRtbyI6eyJ1aWQiOiJnOTZjMmY3MTlmMTU1MWIzMWY2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjExMDUyODYwOVoifSwieWllbGRvbmUiOnsidWlkIjoiMmE0MmZiZDMtMmM3MC00ZWI5LWIxYmQtMDQ2OTY2NTBkOTQ4IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuMzE4MzMzOTM5WiJ9LCJ6ZXJvY2xpY2tmcmF1ZCI6eyJ1aWQiOiJiOTk5NThmZS0yYTg3LTJkYTQtOWNjNC05NjFmZDExM2JlY2UiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNS43MTk1OTQ1NjZaIn19LCJiZGF5IjoiMjAyMi0wNS0xN1QwNjo0ODozOC4wMTc5ODgyMDZaIn0=`, + }, + KADUSERCookie: &http.Cookie{ + Name: "KADUSERCOOKIE", + Value: `7D75D25F-FAC9-443D-B2D1-B17FEE11E027`, + }, + OriginCookie: "go-test", + Aliases: make(map[string]string), + ImpBidCtx: make(map[string]models.ImpCtx), + PrebidBidderCode: make(map[string]string), + BidderResponseTimeMillis: make(map[string]int), + ProfileIDStr: "1234", + Endpoint: models.EndpointV25, + SeatNonBids: make(map[string][]openrtb_ext.NonBid), + MetricsEngine: mockEngine, + }, + }, + }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","native": {},"banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"tagid":"adunit","ext":{"bidder":{"pubmatic":{"keywords":[{"key":"pmzoneid","value":["val1","val2"]}]}},"prebid":{}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ua":"Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36","ip":"123.145.167.10"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":{"prebid":{},"wrapper":{"test":123,"profileid":123,"versionid":1,"wiid":"test_display_wiid"}}}`), + }, + fields: fields{ + cache: mockCache, + metricEngine: mockEngine, + }, + setup: func() { + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + 2: { + models.PARTNER_ID: "2", + models.PREBID_PARTNER_NAME: "appnexus", + models.BidderCode: "appnexus", + models.SERVER_SIDE_FLAG: "1", + models.KEY_GEN_PATTERN: "_AU_@_W_x_H_", + models.TIMEOUT: "200", + }, + -1: { + models.DisplayVersionID: "1", + models.PLATFORM_KEY: models.PLATFORM_APP, + }, + }, nil) + mockCache.EXPECT().GetAdunitConfigFromCache(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&adunitconfig.AdUnitConfig{ + ConfigPattern: "_AU_@_W_x_H_", + Config: map[string]*adunitconfig.AdConfig{ + "adunit@700x900": { + Banner: &adunitconfig.Banner{ + Enabled: ptrutil.ToPtr(false), + }, + }, + "adunit@640x480": { + Video: &adunitconfig.Video{ + Enabled: ptrutil.ToPtr(false), + }, + }, + }, + }) + mockCache.EXPECT().GetMappingsFromCacheV25(gomock.Any(), gomock.Any()).Return(map[string]models.SlotMapping{ + "adunit@700x900": { + SlotName: "adunit@700x900", + SlotMappings: map[string]interface{}{ + models.SITE_CACHE_KEY: "12313", + models.TAG_CACHE_KEY: "45343", + }, + }, + }) + mockCache.EXPECT().GetSlotToHashValueMapFromCacheV25(gomock.Any(), gomock.Any()).Return(models.SlotMappingInfo{ + OrderedSlotList: []string{"adunit@700x900"}, + HashValueMap: map[string]string{ + "adunit@700x900": "1232433543534543", + }, + }) + + //prometheus metrics + mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") + mockEngine.EXPECT().RecordPublisherRequests(rctx.Endpoint, "5890", rctx.Platform) + mockEngine.EXPECT().RecordPlatformPublisherPartnerReqStats(rctx.Platform, "5890", "appnexus") + mockEngine.EXPECT().RecordImpDisabledViaConfigStats(models.ImpTypeVideo, "5890", "1234") + mockEngine.EXPECT().RecordImpDisabledViaConfigStats(models.ImpTypeBanner, "5890", "1234") + }, + want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ + Reject: false, + ChangeSet: hookstage.ChangeSet[hookstage.BeforeValidationRequestPayload]{}, + DebugMessages: []string{"new imp: {\"123\":{\"ImpID\":\"123\",\"TagID\":\"adunit\",\"Div\":\"\",\"SlotName\":\"adunit\",\"AdUnitName\":\"adunit\",\"Secure\":0,\"BidFloor\":0,\"BidFloorCur\":\"\",\"IsRewardInventory\":null,\"Banner\":true,\"Video\":{\"mimes\":[\"video/mp4\",\"video/mpeg\"],\"w\":640,\"h\":480},\"Native\":{\"request\":\"\"},\"IncomingSlots\":[\"700x900\",\"728x90\",\"300x250\",\"640x480v\"],\"Type\":\"video\",\"Bidders\":{\"appnexus\":{\"PartnerID\":2,\"PrebidBidderCode\":\"appnexus\",\"MatchedSlot\":\"adunit@700x900\",\"KGP\":\"_AU_@_W_x_H_\",\"KGPV\":\"\",\"IsRegex\":false,\"Params\":{\"placementId\":0,\"site\":\"12313\",\"adtag\":\"45343\"},\"VASTTagFlag\":false,\"VASTTagFlags\":null}},\"NonMapped\":{},\"NewExt\":{\"data\":{\"pbadslot\":\"adunit\"},\"prebid\":{\"bidder\":{\"appnexus\":{\"placementId\":0,\"site\":\"12313\",\"adtag\":\"45343\"}}}},\"BidCtx\":{},\"BannerAdUnitCtx\":{\"MatchedSlot\":\"adunit@700x900\",\"IsRegex\":false,\"MatchedRegex\":\"\",\"SelectedSlotAdUnitConfig\":{\"banner\":{\"enabled\":false}},\"AppliedSlotAdUnitConfig\":{\"banner\":{\"enabled\":false}},\"UsingDefaultConfig\":false,\"AllowedConnectionTypes\":null},\"VideoAdUnitCtx\":{\"MatchedSlot\":\"adunit@640x480\",\"IsRegex\":false,\"MatchedRegex\":\"\",\"SelectedSlotAdUnitConfig\":{\"video\":{\"enabled\":false}},\"AppliedSlotAdUnitConfig\":{\"video\":{\"enabled\":false}},\"UsingDefaultConfig\":false,\"AllowedConnectionTypes\":null},\"BidderError\":\"\",\"IsAdPodRequest\":false}}"}, + AnalyticsTags: hookanalytics.Analytics{}, + }, + wantErr: false, + isRequestNotRejected: true, + }, { name: "no_serviceSideBidderPresent", args: args{ diff --git a/modules/pubmatic/openwrap/entrypointhook.go b/modules/pubmatic/openwrap/entrypointhook.go index cc5d76cd479..b7bd89569fb 100644 --- a/modules/pubmatic/openwrap/entrypointhook.go +++ b/modules/pubmatic/openwrap/entrypointhook.go @@ -59,7 +59,7 @@ func (m OpenWrap) handleEntrypointHook( case hookexecution.EndpointAuction: switch source { case "pbjs": - endpoint = models.EndpointOWS2S + endpoint = models.EndpointWebS2S requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body) case "inapp": requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body, "ext", "wrapper") @@ -100,7 +100,7 @@ func (m OpenWrap) handleEntrypointHook( if err != nil { result.NbrCode = nbr.InvalidRequestWrapperExtension - result.Errors = append(result.Errors, "InvalidRequest") + result.Errors = append(result.Errors, err.Error()) return result, err } diff --git a/modules/pubmatic/openwrap/entrypointhook_test.go b/modules/pubmatic/openwrap/entrypointhook_test.go index 1ca4b25b06b..ceb7ebfd1f0 100644 --- a/modules/pubmatic/openwrap/entrypointhook_test.go +++ b/modules/pubmatic/openwrap/entrypointhook_test.go @@ -266,7 +266,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { PrebidBidderCode: make(map[string]string), BidderResponseTimeMillis: make(map[string]int), ProfileIDStr: "43563", - Endpoint: models.EndpointOWS2S, + Endpoint: models.EndpointWebS2S, MetricsEngine: mockEngine, SeatNonBids: make(map[string][]openrtb_ext.NonBid), }, @@ -325,7 +325,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { PrebidBidderCode: make(map[string]string), BidderResponseTimeMillis: make(map[string]int), ProfileIDStr: "43563", - Endpoint: models.EndpointOWS2S, + Endpoint: models.EndpointWebS2S, MetricsEngine: mockEngine, SeatNonBids: make(map[string][]openrtb_ext.NonBid), }, diff --git a/modules/pubmatic/openwrap/models/constants.go b/modules/pubmatic/openwrap/models/constants.go index 8922673983d..e8c38e85835 100755 --- a/modules/pubmatic/openwrap/models/constants.go +++ b/modules/pubmatic/openwrap/models/constants.go @@ -424,7 +424,7 @@ const ( EndpointJson = "json" EndpointORTB = "ortb" EndpointVAST = "vast" - EndpointOWS2S = "ows2s" + EndpointWebS2S = "webs2s" EndPointCTV = "ctv" EndpointHybrid = "hybrid" Openwrap = "openwrap" @@ -470,6 +470,7 @@ const ( TypeAmp = "amp" TypeSDK = "sdk" TypeS2S = "s2s" + TypeWebS2S = "webs2s" ) // constants to accept request-test value diff --git a/modules/pubmatic/vastunwrap/entryhook.go b/modules/pubmatic/vastunwrap/entryhook.go index 71725cf6a33..6b9bcbaf462 100644 --- a/modules/pubmatic/vastunwrap/entryhook.go +++ b/modules/pubmatic/vastunwrap/entryhook.go @@ -10,15 +10,15 @@ import ( "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/models" ) +var getRandomNumber = func() int { + return rand.Intn(100) +} + func getVastUnwrapperEnable(ctx context.Context, field string) bool { vastEnableUnwrapper, _ := ctx.Value(field).(string) return vastEnableUnwrapper == "1" } -var getRandomNumber = func() int { - return rand.Intn(100) -} - func handleEntrypointHook( _ context.Context, _ hookstage.ModuleInvocationContext, @@ -31,7 +31,11 @@ func handleEntrypointHook( }() result := hookstage.HookResult[hookstage.EntrypointPayload]{} vastRequestContext := models.RequestCtx{ - VastUnwrapEnabled: getVastUnwrapperEnable(payload.Request.Context(), VastUnwrapEnabled) && getRandomNumber() < config.TrafficPercentage, + VastUnwrapEnabled: getVastUnwrapperEnable(payload.Request.Context(), VastUnwrapEnabled), + } + + if !vastRequestContext.VastUnwrapEnabled { + vastRequestContext.VastUnwrapStatsEnabled = getRandomNumber() < config.StatTrafficPercentage } result.ModuleContext = make(hookstage.ModuleContext) result.ModuleContext[RequestContext] = vastRequestContext diff --git a/modules/pubmatic/vastunwrap/entryhook_test.go b/modules/pubmatic/vastunwrap/entryhook_test.go index bda0bd6ee8f..0ef9d120397 100644 --- a/modules/pubmatic/vastunwrap/entryhook_test.go +++ b/modules/pubmatic/vastunwrap/entryhook_test.go @@ -39,7 +39,7 @@ func TestHandleEntrypointHook(t *testing.T) { want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false}}}, }, { - name: "Enable Vast Unwrapper with random number less than traffic percentage", + name: "Enable Vast Unwrapper", args: args{ payload: hookstage.EntrypointPayload{ Request: func() *http.Request { @@ -55,48 +55,9 @@ func TestHandleEntrypointHook(t *testing.T) { randomNum: 1, want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: true}}}, }, - { - name: "Enable Vast Unwrapper with random number equal to traffic percenatge", - args: args{ - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - ctx := context.WithValue(context.Background(), VastUnwrapEnabled, "1") - r, _ := http.NewRequestWithContext(ctx, "", "", nil) - return r - }(), - }, - config: VastUnwrapModule{ - TrafficPercentage: 2, - }, - }, - randomNum: 2, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false}}}, - }, - { - name: "Enable Vast Unwrapper with random number greater than traffic percenatge", - args: args{ - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - ctx := context.WithValue(context.Background(), VastUnwrapEnabled, "1") - r, _ := http.NewRequestWithContext(ctx, "", "", nil) - return r - }(), - }, - config: VastUnwrapModule{ - TrafficPercentage: 2, - }, - }, - randomNum: 5, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false}}}, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - oldRandomNumberGen := getRandomNumber - getRandomNumber = func() int { return tt.randomNum } - defer func() { - getRandomNumber = oldRandomNumberGen - }() got, _ := handleEntrypointHook(nil, hookstage.ModuleInvocationContext{}, tt.args.payload, tt.args.config) if !reflect.DeepEqual(got, tt.want) { t.Errorf("handleEntrypointHook() = %v, want %v", got, tt.want) diff --git a/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go b/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go index a51a85d7b98..a2a725a5806 100644 --- a/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go +++ b/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go @@ -19,26 +19,38 @@ func (m VastUnwrapModule) handleRawBidderResponseHook( result.DebugMessages = append(result.DebugMessages, "error: request-ctx not found in handleRawBidderResponseHook()") return result, nil } - if !vastRequestContext.VastUnwrapEnabled { + if !vastRequestContext.VastUnwrapEnabled && !vastRequestContext.VastUnwrapStatsEnabled { result.DebugMessages = append(result.DebugMessages, "error: vast unwrap flag is not enabled in handleRawBidderResponseHook()") return result, nil } defer func() { miCtx.ModuleContext[RequestContext] = vastRequestContext }() - wg := new(sync.WaitGroup) - for _, bid := range payload.Bids { - if string(bid.BidType) == MediaTypeVideo { - wg.Add(1) - go func(bid *adapters.TypedBid) { - defer wg.Done() - m.doUnwrapandUpdateBid(bid, vastRequestContext.UA, unwrapURL, miCtx.AccountID, payload.Bidder) - }(bid) + + // Below code collects stats only + if vastRequestContext.VastUnwrapStatsEnabled { + for _, bid := range payload.Bids { + if string(bid.BidType) == MediaTypeVideo { + go func(bid *adapters.TypedBid) { + m.doUnwrapandUpdateBid(vastRequestContext.VastUnwrapStatsEnabled, bid, vastRequestContext.UA, unwrapURL, miCtx.AccountID, payload.Bidder) + }(bid) + } + } + } else { + wg := new(sync.WaitGroup) + for _, bid := range payload.Bids { + if string(bid.BidType) == MediaTypeVideo { + wg.Add(1) + go func(bid *adapters.TypedBid) { + defer wg.Done() + m.doUnwrapandUpdateBid(vastRequestContext.VastUnwrapStatsEnabled, bid, vastRequestContext.UA, unwrapURL, miCtx.AccountID, payload.Bidder) + }(bid) + } } + wg.Wait() + changeSet := hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{} + changeSet.RawBidderResponse().Bids().Update(payload.Bids) + result.ChangeSet = changeSet } - wg.Wait() - changeSet := hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{} - changeSet.RawBidderResponse().Bids().Update(payload.Bids) - result.ChangeSet = changeSet return result, nil } diff --git a/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go b/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go index 933c1123ec6..b4677ab47b5 100644 --- a/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go +++ b/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go @@ -70,6 +70,42 @@ func TestHandleRawBidderResponseHook(t *testing.T) { wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, wantErr: false, }, + { + name: "Set Vast Unwrapper to false in request context with type video, stats enabled true", + args: args{ + module: VastUnWrapModule, + payload: hookstage.RawBidderResponsePayload{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + ID: "Bid-123", + ImpID: fmt.Sprintf("div-adunit-%d", 123), + Price: 2.1, + AdM: vastXMLAdM, + CrID: "Cr-234", + W: 100, + H: 50, + }, + BidType: "video", + }}, + Bidder: "pubmatic", + }, + moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false, VastUnwrapStatsEnabled: true}}}, + }, + wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, + setup: func() { + mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() + }, + unwrapRequest: func(w http.ResponseWriter, req *http.Request) { + w.Header().Add("unwrap-status", "0") + w.Header().Add("unwrap-count", "1") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(inlineXMLAdM)) + }, + wantErr: false, + }, { name: "Set Vast Unwrapper to true in request context with invalid vast xml", args: args{ @@ -96,8 +132,8 @@ func TestHandleRawBidderResponseHook(t *testing.T) { }, wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "1").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "1").AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "1") @@ -132,9 +168,9 @@ func TestHandleRawBidderResponseHook(t *testing.T) { }, wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "1").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") @@ -182,9 +218,9 @@ func TestHandleRawBidderResponseHook(t *testing.T) { }, wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "1").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") @@ -256,9 +292,9 @@ func TestHandleRawBidderResponseHook(t *testing.T) { }, }, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") diff --git a/modules/pubmatic/vastunwrap/models/request.go b/modules/pubmatic/vastunwrap/models/request.go index c558fdf0912..60dc655a600 100644 --- a/modules/pubmatic/vastunwrap/models/request.go +++ b/modules/pubmatic/vastunwrap/models/request.go @@ -1,6 +1,7 @@ package models type RequestCtx struct { - UA string - VastUnwrapEnabled bool + UA string + VastUnwrapEnabled bool + VastUnwrapStatsEnabled bool } diff --git a/modules/pubmatic/vastunwrap/module.go b/modules/pubmatic/vastunwrap/module.go index aca37238997..e98d5cf58b4 100644 --- a/modules/pubmatic/vastunwrap/module.go +++ b/modules/pubmatic/vastunwrap/module.go @@ -17,11 +17,12 @@ import ( ) type VastUnwrapModule struct { - Cfg unWrapCfg.VastUnWrapCfg `mapstructure:"vastunwrap_cfg" json:"vastunwrap_cfg"` - TrafficPercentage int `mapstructure:"traffic_percentage" json:"traffic_percentage"` - Enabled bool `mapstructure:"enabled" json:"enabled"` - MetricsEngine metrics.MetricsEngine - unwrapRequest func(w http.ResponseWriter, r *http.Request) + Cfg unWrapCfg.VastUnWrapCfg `mapstructure:"vastunwrap_cfg" json:"vastunwrap_cfg"` + TrafficPercentage int `mapstructure:"traffic_percentage" json:"traffic_percentage"` + StatTrafficPercentage int `mapstructure:"stat_traffic_percentage" json:"stat_traffic_percentage"` + Enabled bool `mapstructure:"enabled" json:"enabled"` + MetricsEngine metrics.MetricsEngine + unwrapRequest func(w http.ResponseWriter, r *http.Request) } func Builder(rawCfg json.RawMessage, deps moduledeps.ModuleDeps) (interface{}, error) { @@ -42,11 +43,12 @@ func initVastUnwrap(rawCfg json.RawMessage, deps moduledeps.ModuleDeps) (VastUnw return vastUnwrapModuleCfg, fmt.Errorf("Prometheus registry is nil") } return VastUnwrapModule{ - Cfg: vastUnwrapModuleCfg.Cfg, - TrafficPercentage: vastUnwrapModuleCfg.TrafficPercentage, - Enabled: vastUnwrapModuleCfg.Enabled, - MetricsEngine: metricEngine, - unwrapRequest: vastunwrap.UnwrapRequest, + Cfg: vastUnwrapModuleCfg.Cfg, + TrafficPercentage: vastUnwrapModuleCfg.TrafficPercentage, + StatTrafficPercentage: vastUnwrapModuleCfg.StatTrafficPercentage, + Enabled: vastUnwrapModuleCfg.Enabled, + MetricsEngine: metricEngine, + unwrapRequest: vastunwrap.UnwrapRequest, }, nil } diff --git a/modules/pubmatic/vastunwrap/module_test.go b/modules/pubmatic/vastunwrap/module_test.go index 749cf4c4aaf..0cda74ef82a 100644 --- a/modules/pubmatic/vastunwrap/module_test.go +++ b/modules/pubmatic/vastunwrap/module_test.go @@ -87,11 +87,6 @@ func TestVastUnwrapModuleHandleEntrypointHook(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - oldRandomNumberGen := getRandomNumber - getRandomNumber = func() int { return 1 } - defer func() { - getRandomNumber = oldRandomNumberGen - }() m := VastUnwrapModule{ Cfg: tt.fields.cfg.Cfg, Enabled: tt.fields.cfg.Enabled, @@ -161,9 +156,9 @@ func TestVastUnwrapModuleHandleRawBidderResponseHook(t *testing.T) { wantAdM: true, }, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0") - mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "1") - mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0") + mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1") + mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") diff --git a/modules/pubmatic/vastunwrap/stats/metrics.go b/modules/pubmatic/vastunwrap/stats/metrics.go index af93b333e97..b56ba2e0a25 100644 --- a/modules/pubmatic/vastunwrap/stats/metrics.go +++ b/modules/pubmatic/vastunwrap/stats/metrics.go @@ -20,9 +20,9 @@ const ( // MetricsEngine is a generic interface to record metrics into the desired backend type MetricsEngine interface { - RecordRequestStatus(bidder, status string) - RecordWrapperCount(bidder string, wrapper_count string) - RecordRequestTime(bidder string, readTime time.Duration) + RecordRequestStatus(accountId, bidder, status string) + RecordWrapperCount(accountId, bidder string, wrapper_count string) + RecordRequestTime(accountId, bidder string, readTime time.Duration) } // Metrics defines the datatype which will implement MetricsEngine @@ -48,14 +48,14 @@ func NewMetricsEngine(cfg moduledeps.ModuleDeps) (*Metrics, error) { metrics.requests = newCounter(cfg.MetricsCfg.Prometheus, metrics.Registry, "vastunwrap_status", "Count of vast unwrap requests labeled by status", - []string{bidderLabel, statusLabel}) + []string{pubIdLabel, bidderLabel, statusLabel}) metrics.wrapperCount = newCounter(cfg.MetricsCfg.Prometheus, metrics.Registry, "vastunwrap_wrapper_count", "Count of vast unwrap levels labeled by bidder", - []string{bidderLabel, wrapperCountLabel}) + []string{pubIdLabel, bidderLabel, wrapperCountLabel}) metrics.requestTime = newHistogramVec(cfg.MetricsCfg.Prometheus, metrics.Registry, "vastunwrap_request_time", - "Time taken to serve the vast unwrap request in Milliseconds", []string{bidderLabel}, + "Time taken to serve the vast unwrap request in Milliseconds", []string{pubIdLabel, bidderLabel}, []float64{50, 100, 200, 300, 500}) return &metrics, nil } @@ -86,24 +86,27 @@ func newHistogramVec(cfg config.PrometheusMetrics, registry *prometheus.Registry } // RecordRequest record counter with vast unwrap status -func (m *Metrics) RecordRequestStatus(bidder, status string) { +func (m *Metrics) RecordRequestStatus(accountId, bidder, status string) { m.requests.With(prometheus.Labels{ + pubIdLabel: accountId, bidderLabel: bidder, statusLabel: status, }).Inc() } // RecordWrapperCount record counter of wrapper levels -func (m *Metrics) RecordWrapperCount(bidder, wrapper_count string) { +func (m *Metrics) RecordWrapperCount(accountId, bidder, wrapper_count string) { m.wrapperCount.With(prometheus.Labels{ + pubIdLabel: accountId, bidderLabel: bidder, wrapperCountLabel: wrapper_count, }).Inc() } // RecordRequestReadTime records time takent to complete vast unwrap -func (m *Metrics) RecordRequestTime(bidder string, requestTime time.Duration) { +func (m *Metrics) RecordRequestTime(accountId, bidder string, requestTime time.Duration) { m.requestTime.With(prometheus.Labels{ + pubIdLabel: accountId, bidderLabel: bidder, }).Observe(float64(requestTime.Milliseconds())) } diff --git a/modules/pubmatic/vastunwrap/stats/metrics_test.go b/modules/pubmatic/vastunwrap/stats/metrics_test.go index fd0ee09f4f1..4cd87879993 100644 --- a/modules/pubmatic/vastunwrap/stats/metrics_test.go +++ b/modules/pubmatic/vastunwrap/stats/metrics_test.go @@ -36,7 +36,7 @@ func createMetricsForTesting() *Metrics { func TestRecordRequestTime(t *testing.T) { m := createMetricsForTesting() - m.RecordRequestTime("pubmatic", time.Millisecond*250) + m.RecordRequestTime("1234", "pubmatic", time.Millisecond*250) result := getHistogramFromHistogramVec(m.requestTime, "bidder", "pubmatic") assertHistogram(t, result, 1, 250) @@ -44,9 +44,10 @@ func TestRecordRequestTime(t *testing.T) { func TestRecordRequestStatus(t *testing.T) { m := createMetricsForTesting() - m.RecordRequestStatus("pubmatic", "0") + m.RecordRequestStatus("1234", "pubmatic", "0") assertCounterVecValue(t, "Record_Request_Status", "Record_Request_Status_Success", m.requests, float64(1), prometheus.Labels{ + "pub_id": "1234", "bidder": "pubmatic", "status": "0", }) @@ -55,9 +56,10 @@ func TestRecordRequestStatus(t *testing.T) { func TestRecordWrapperCount(t *testing.T) { m := createMetricsForTesting() - m.RecordWrapperCount("pubmatic", "1") + m.RecordWrapperCount("1234", "pubmatic", "1") assertCounterVecValue(t, "Record_Wrapper_Count", "Record_Wrapper_Count", m.wrapperCount, float64(1), prometheus.Labels{ + "pub_id": "1234", "bidder": "pubmatic", "wrapper_count": "1", }) diff --git a/modules/pubmatic/vastunwrap/stats/mock/mock.go b/modules/pubmatic/vastunwrap/stats/mock/mock.go index b0c899010eb..ae18d8fe3af 100644 --- a/modules/pubmatic/vastunwrap/stats/mock/mock.go +++ b/modules/pubmatic/vastunwrap/stats/mock/mock.go @@ -34,37 +34,37 @@ func (m *MockMetricsEngine) EXPECT() *MockMetricsEngineMockRecorder { } // RecordRequestStatus mocks base method -func (m *MockMetricsEngine) RecordRequestStatus(arg0, arg1 string) { +func (m *MockMetricsEngine) RecordRequestStatus(arg0, arg1, arg2 string) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordRequestStatus", arg0, arg1) + m.ctrl.Call(m, "RecordRequestStatus", arg0, arg1, arg2) } // RecordRequestStatus indicates an expected call of RecordRequestStatus -func (mr *MockMetricsEngineMockRecorder) RecordRequestStatus(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockMetricsEngineMockRecorder) RecordRequestStatus(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordRequestStatus", reflect.TypeOf((*MockMetricsEngine)(nil).RecordRequestStatus), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordRequestStatus", reflect.TypeOf((*MockMetricsEngine)(nil).RecordRequestStatus), arg0, arg1, arg2) } // RecordWrapperCount mocks base method -func (m *MockMetricsEngine) RecordWrapperCount(arg0, arg1 string) { +func (m *MockMetricsEngine) RecordWrapperCount(arg0, arg1, arg2 string) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordWrapperCount", arg0, arg1) + m.ctrl.Call(m, "RecordWrapperCount", arg0, arg1, arg2) } // RecordWrapperCount indicates an expected call of RecordRequestStatus -func (mr *MockMetricsEngineMockRecorder) RecordWrapperCount(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockMetricsEngineMockRecorder) RecordWrapperCount(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWrapperCount", reflect.TypeOf((*MockMetricsEngine)(nil).RecordWrapperCount), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWrapperCount", reflect.TypeOf((*MockMetricsEngine)(nil).RecordWrapperCount), arg0, arg1, arg2) } // RecordRequestTime mocks base method -func (m *MockMetricsEngine) RecordRequestTime(arg0 string, arg1 time.Duration) { +func (m *MockMetricsEngine) RecordRequestTime(arg0, arg1 string, arg2 time.Duration) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordRequestTime", arg0, arg1) + m.ctrl.Call(m, "RecordRequestTime", arg0, arg1, arg2) } // RecordRequestTime indicates an expected call of RecordRequestTime -func (mr *MockMetricsEngineMockRecorder) RecordRequestTime(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockMetricsEngineMockRecorder) RecordRequestTime(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordRequestTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordRequestTime), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordRequestTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordRequestTime), arg0, arg1, arg2) } diff --git a/modules/pubmatic/vastunwrap/unwrap_service.go b/modules/pubmatic/vastunwrap/unwrap_service.go index 186a5100ce6..9b432f5ebb1 100644 --- a/modules/pubmatic/vastunwrap/unwrap_service.go +++ b/modules/pubmatic/vastunwrap/unwrap_service.go @@ -11,7 +11,7 @@ import ( "github.com/prebid/prebid-server/adapters" ) -func (m VastUnwrapModule) doUnwrapandUpdateBid(bid *adapters.TypedBid, userAgent string, unwrapURL string, accountID string, bidder string) { +func (m VastUnwrapModule) doUnwrapandUpdateBid(isStatsEnabled bool, bid *adapters.TypedBid, userAgent string, unwrapURL string, accountID string, bidder string) { startTime := time.Now() var wrapperCnt int64 var respStatus string @@ -23,10 +23,10 @@ func (m VastUnwrapModule) doUnwrapandUpdateBid(bid *adapters.TypedBid, userAgent glog.Errorf("AdM:[%s] Error:[%v] stacktrace:[%s]", bid.Bid.AdM, r, string(debug.Stack())) } respTime := time.Since(startTime) - m.MetricsEngine.RecordRequestTime(bidder, respTime) - m.MetricsEngine.RecordRequestStatus(bidder, respStatus) + m.MetricsEngine.RecordRequestTime(accountID, bidder, respTime) + m.MetricsEngine.RecordRequestStatus(accountID, bidder, respStatus) if respStatus == "0" { - m.MetricsEngine.RecordWrapperCount(bidder, strconv.Itoa(int(wrapperCnt))) + m.MetricsEngine.RecordWrapperCount(accountID, bidder, strconv.Itoa(int(wrapperCnt))) } }() headers := http.Header{} @@ -42,8 +42,8 @@ func (m VastUnwrapModule) doUnwrapandUpdateBid(bid *adapters.TypedBid, userAgent m.unwrapRequest(httpResp, httpReq) respStatus = httpResp.Header().Get(UnwrapStatus) wrapperCnt, _ = strconv.ParseInt(httpResp.Header().Get(UnwrapCount), 10, 0) - respBody := httpResp.Body.Bytes() - if httpResp.Code == http.StatusOK { + if !isStatsEnabled && httpResp.Code == http.StatusOK { + respBody := httpResp.Body.Bytes() bid.Bid.AdM = string(respBody) return } diff --git a/modules/pubmatic/vastunwrap/unwrap_service_test.go b/modules/pubmatic/vastunwrap/unwrap_service_test.go index 453861b3700..4025124ab8e 100644 --- a/modules/pubmatic/vastunwrap/unwrap_service_test.go +++ b/modules/pubmatic/vastunwrap/unwrap_service_test.go @@ -21,6 +21,7 @@ func TestDoUnwrap(t *testing.T) { mockMetricsEngine := mock_stats.NewMockMetricsEngine(ctrl) type args struct { module VastUnwrapModule + statsEnabled bool bid *adapters.TypedBid userAgent string unwrapDefaultTimeout int @@ -83,8 +84,8 @@ func TestDoUnwrap(t *testing.T) { url: "testURL", }, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "2") - mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "2") + mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "2") @@ -113,9 +114,9 @@ func TestDoUnwrap(t *testing.T) { wantAdM: true, }, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0") - mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "1") - mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0") + mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1") + mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") @@ -145,8 +146,8 @@ func TestDoUnwrap(t *testing.T) { wantAdM: false, }, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "1") - mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "1") + mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "1") @@ -167,7 +168,7 @@ func TestDoUnwrap(t *testing.T) { MetricsEngine: mockMetricsEngine, unwrapRequest: tt.unwrapRequest, } - m.doUnwrapandUpdateBid(tt.args.bid, tt.args.userAgent, tt.args.url, "5890", "pubmatic") + m.doUnwrapandUpdateBid(tt.args.statsEnabled, tt.args.bid, tt.args.userAgent, tt.args.url, "5890", "pubmatic") if tt.args.bid.Bid.AdM != "" && tt.args.wantAdM { assert.Equal(t, inlineXMLAdM, tt.args.bid.Bid.AdM, "AdM is not updated correctly after executing RawBidderResponse hook.") }