From c2a0176a394c93df581f28152de8891812112c90 Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Tue, 24 Oct 2023 13:08:53 +0530 Subject: [PATCH 01/30] run validate-merge.yml for ci branch --- .github/workflows/validate-merge.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validate-merge.yml b/.github/workflows/validate-merge.yml index 2e78d638eb2..0fe3e5523d3 100644 --- a/.github/workflows/validate-merge.yml +++ b/.github/workflows/validate-merge.yml @@ -2,7 +2,10 @@ name: Validate Merge on: pull_request: - branches: [master] + branches: + - master + - main + - ci jobs: validate-merge: From c0b01e315cf3e30755e17228c796589e3fcb2e10 Mon Sep 17 00:00:00 2001 From: Pubmatic-Supriya-Patil <131644110+Pubmatic-Supriya-Patil@users.noreply.github.com> Date: Thu, 26 Oct 2023 10:06:03 +0530 Subject: [PATCH 02/30] OTT-1319: add prometheus stats to count number of bids with vast unwrapped (#590) --- exchange/bidder.go | 1 + exchange/exchange_ow.go | 37 ++++ exchange/exchange_ow_test.go | 213 +++++++++++++++++++++++ metrics/config/metrics.go | 56 ------ metrics/config/metrics_ow.go | 68 ++++++++ metrics/go_metrics_ow.go | 4 + metrics/metrics.go | 9 +- metrics/metrics_mock_ow.go | 5 + metrics/metrics_ow.go | 11 ++ metrics/prometheus/prometheus.go | 129 +------------- metrics/prometheus/prometheus_ow.go | 166 ++++++++++++++++-- metrics/prometheus/prometheus_ow_test.go | 65 +++++-- 12 files changed, 548 insertions(+), 216 deletions(-) create mode 100644 metrics/metrics_ow.go diff --git a/exchange/bidder.go b/exchange/bidder.go index 263d27af878..2ac15c10fe5 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -261,6 +261,7 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde errs = append(errs, moreErrs...) if bidResponse != nil { + recordVASTTagType(bidder.me, bidResponse, bidder.BidderName) reject := hookExecutor.ExecuteRawBidderResponseStage(bidResponse, string(bidder.BidderName)) if reject != nil { errs = append(errs, reject) diff --git a/exchange/exchange_ow.go b/exchange/exchange_ow.go index 395a828b790..db7bb06aa78 100644 --- a/exchange/exchange_ow.go +++ b/exchange/exchange_ow.go @@ -11,6 +11,7 @@ import ( "github.com/golang/glog" "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/openrtb/v19/openrtb3" + "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/exchange/entities" "github.com/prebid/prebid-server/metrics" pubmaticstats "github.com/prebid/prebid-server/metrics/pubmatic_stats" @@ -27,6 +28,21 @@ const ( vastVersionUndefined = "undefined" ) +const ( + VASTTypeWrapperEndTag = "" + VASTTypeInLineEndTag = "" +) + +// VASTTagType describes the allowed values for VASTTagType +type VASTTagType string + +const ( + WrapperVASTTagType VASTTagType = "Wrapper" + InLineVASTTagType VASTTagType = "InLine" + URLVASTTagType VASTTagType = "URL" + UnknownVASTTagType VASTTagType = "Unknown" +) + var ( vastVersionRegex = regexp.MustCompile(``) ) @@ -174,6 +190,27 @@ func recordVastVersion(metricsEngine metrics.MetricsEngine, adapterBids map[open } } +func recordVASTTagType(metricsEngine metrics.MetricsEngine, adapterBids *adapters.BidderResponse, bidder openrtb_ext.BidderName) { + for _, adapterBid := range adapterBids.Bids { + if adapterBid.BidType == openrtb_ext.BidTypeVideo { + vastTagType := UnknownVASTTagType + if index := strings.LastIndex(adapterBid.Bid.AdM, VASTTypeWrapperEndTag); index != -1 { + vastTagType = WrapperVASTTagType + } else if index := strings.LastIndex(adapterBid.Bid.AdM, VASTTypeInLineEndTag); index != -1 { + vastTagType = InLineVASTTagType + } else if IsUrl(adapterBid.Bid.AdM) { + vastTagType = URLVASTTagType + } + metricsEngine.RecordVASTTagType(string(bidder), string(vastTagType)) + } + } +} + +func IsUrl(adm string) bool { + url, err := url.Parse(adm) + return err == nil && url.Scheme != "" && url.Host != "" +} + // recordPartnerTimeout captures the partnertimeout if any at publisher profile level func recordPartnerTimeout(ctx context.Context, pubID, aliasBidder string) { if metricEnabled, ok := ctx.Value(bidCountMetricEnabled).(bool); metricEnabled && ok { diff --git a/exchange/exchange_ow_test.go b/exchange/exchange_ow_test.go index 8240cd74957..e660ced3377 100644 --- a/exchange/exchange_ow_test.go +++ b/exchange/exchange_ow_test.go @@ -1615,3 +1615,216 @@ func Test_updateSeatNonBidsFloors(t *testing.T) { }) } } + +func TestRecordVASTTagType(t *testing.T) { + var vastXMLAdM = "" + var inlineXMLAdM = "Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1" + var URLAdM = "http://pubmatic.com" + type args struct { + metricsEngine metrics.MetricsEngine + adapterBids *adapters.BidderResponse + getMetricsEngine func() *metrics.MetricsEngineMock + } + tests := []struct { + name string + args args + }{ + { + name: "no_bids", + args: args{ + adapterBids: &adapters.BidderResponse{}, + getMetricsEngine: func() *metrics.MetricsEngineMock { + metricEngine := &metrics.MetricsEngineMock{} + return metricEngine + }, + }, + }, + { + name: "empty_bids_in_seatbids", + args: args{ + adapterBids: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{}, + }, + getMetricsEngine: func() *metrics.MetricsEngineMock { + metricEngine := &metrics.MetricsEngineMock{} + return metricEngine + }, + }, + }, + { + name: "empty_adm", + args: args{ + adapterBids: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + AdM: "", + }, + Seat: "pubmatic", + BidType: openrtb_ext.BidTypeVideo, + }, + }, + }, + getMetricsEngine: func() *metrics.MetricsEngineMock { + metricEngine := &metrics.MetricsEngineMock{} + metricEngine.Mock.On("RecordVASTTagType", "pubmatic", "Unknown").Return() + return metricEngine + }, + }, + }, + { + name: "adm_has_wrapped_xml", + args: args{ + adapterBids: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + AdM: vastXMLAdM, + }, + Seat: "pubmatic", + BidType: openrtb_ext.BidTypeVideo, + }, + }, + }, + getMetricsEngine: func() *metrics.MetricsEngineMock { + metricEngine := &metrics.MetricsEngineMock{} + metricEngine.Mock.On("RecordVASTTagType", "pubmatic", "Wrapper").Return() + return metricEngine + }, + }, + }, + { + name: "adm_has_inline_xml", + args: args{ + adapterBids: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + AdM: inlineXMLAdM, + }, + Seat: "pubmatic", + BidType: openrtb_ext.BidTypeVideo, + }, + }, + }, + getMetricsEngine: func() *metrics.MetricsEngineMock { + metricEngine := &metrics.MetricsEngineMock{} + metricEngine.Mock.On("RecordVASTTagType", "pubmatic", "InLine").Return() + return metricEngine + }, + }, + }, + { + name: "adm_has_url", + args: args{ + adapterBids: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + AdM: URLAdM, + }, + Seat: "pubmatic", + BidType: openrtb_ext.BidTypeVideo, + }, + }, + }, + getMetricsEngine: func() *metrics.MetricsEngineMock { + metricEngine := &metrics.MetricsEngineMock{} + metricEngine.Mock.On("RecordVASTTagType", "pubmatic", "URL").Return() + return metricEngine + }, + }, + }, + { + name: "adm_has_wrapper_inline_url_adm", + args: args{ + adapterBids: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + AdM: vastXMLAdM, + }, + Seat: "pubmatic", + BidType: openrtb_ext.BidTypeVideo, + }, + { + Bid: &openrtb2.Bid{ + AdM: inlineXMLAdM, + }, + Seat: "pubmatic", + BidType: openrtb_ext.BidTypeVideo, + }, + { + Bid: &openrtb2.Bid{ + AdM: URLAdM, + }, + Seat: "pubmatic", + BidType: openrtb_ext.BidTypeVideo, + }, + }, + }, + getMetricsEngine: func() *metrics.MetricsEngineMock { + metricEngine := &metrics.MetricsEngineMock{} + metricEngine.Mock.On("RecordVASTTagType", "pubmatic", "Wrapper").Return() + metricEngine.Mock.On("RecordVASTTagType", "pubmatic", "InLine").Return() + metricEngine.Mock.On("RecordVASTTagType", "pubmatic", "URL").Return() + return metricEngine + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockMetricEngine := tt.args.getMetricsEngine() + recordVASTTagType(mockMetricEngine, tt.args.adapterBids, "pubmatic") + mockMetricEngine.AssertExpectations(t) + }) + } +} + +func TestIsUrl(t *testing.T) { + type args struct { + adm string + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "empty_url", + args: args{ + adm: "", + }, + want: false, + }, + { + name: "valid_url", + args: args{ + adm: "http://www.test.com", + }, + want: true, + }, + { + name: "invalid_url_without_protocol", + args: args{ + adm: "//www.test.com/vast.xml", + }, + want: false, + }, + { + name: "invalid_url_without_host", + args: args{ + adm: "http://", + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsUrl(tt.args.adm); got != tt.want { + t.Errorf("IsUrl() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/metrics/config/metrics.go b/metrics/config/metrics.go index ce3799e4b99..13fd1b53bfd 100644 --- a/metrics/config/metrics.go +++ b/metrics/config/metrics.go @@ -150,20 +150,6 @@ func (me *MultiMetricsEngine) RecordAdapterRequest(labels metrics.AdapterLabels) } } -// RecordRejectedBidsForBidder across all engines -func (me *MultiMetricsEngine) RecordRejectedBidsForBidder(bidder openrtb_ext.BidderName) { - for _, thisME := range *me { - thisME.RecordRejectedBidsForBidder(bidder) - } -} - -// RecordDynamicFetchFailure across all engines -func (me *MultiMetricsEngine) RecordDynamicFetchFailure(pubId, code string) { - for _, thisME := range *me { - thisME.RecordDynamicFetchFailure(pubId, code) - } -} - // Keeps track of created and reused connections to adapter bidders and the time from the // connection request, to the connection creation, or reuse from the pool across all engines func (me *MultiMetricsEngine) RecordAdapterConnections(bidderName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) { @@ -317,10 +303,6 @@ func (me *MultiMetricsEngine) RecordPodImpGenTime(labels metrics.PodLabels, star } } -// RecordRejectedBidsForBidder as a noop -func (me *NilMetricsEngine) RecordRejectedBidsForBidder(bidder openrtb_ext.BidderName) { -} - // RecordPodCombGenTime as a noop func (me *MultiMetricsEngine) RecordPodCombGenTime(labels metrics.PodLabels, elapsedTime time.Duration) { for _, thisME := range *me { @@ -472,25 +454,6 @@ func (me *MultiMetricsEngine) RecordModuleTimeout(labels metrics.ModuleLabels) { thisME.RecordModuleTimeout(labels) } } -func (me *MultiMetricsEngine) RecordRejectedBids(pubid, bidder, code string) { - for _, thisME := range *me { - thisME.RecordRejectedBids(pubid, bidder, code) - } -} - -func (me *MultiMetricsEngine) RecordBids(pubid, profileid, biddder, deal string) { - for _, thisME := range *me { - thisME.RecordBids(pubid, profileid, biddder, deal) - } -} -func (me *MultiMetricsEngine) RecordHttpCounter() { -} - -func (me *MultiMetricsEngine) RecordVastVersion(biddder, vastVersion string) { - for _, thisME := range *me { - thisME.RecordVastVersion(biddder, vastVersion) - } -} // NilMetricsEngine implements the MetricsEngine interface where no metrics are actually captured. This is // used if no metric backend is configured and also for tests. @@ -699,22 +662,3 @@ func (me *NilMetricsEngine) RecordModuleExecutionError(labels metrics.ModuleLabe func (me *NilMetricsEngine) RecordModuleTimeout(labels metrics.ModuleLabels) { } - -// RecordDynamicFetchFailure as a noop -func (me *NilMetricsEngine) RecordDynamicFetchFailure(pubId, code string) { -} - -// RecordRejectedBids as a noop -func (me *NilMetricsEngine) RecordRejectedBids(pubid, bidder, code string) { -} - -// RecordBids as a noop -func (me *NilMetricsEngine) RecordBids(pubid, profileid, biddder, deal string) { -} - -// RecordVastVersion as a noop -func (me *NilMetricsEngine) RecordVastVersion(biddder, vastVersion string) { -} - -func (m *NilMetricsEngine) RecordHttpCounter() { -} diff --git a/metrics/config/metrics_ow.go b/metrics/config/metrics_ow.go index f076df3e61c..5590984a5ec 100644 --- a/metrics/config/metrics_ow.go +++ b/metrics/config/metrics_ow.go @@ -1,6 +1,7 @@ package config import ( + "github.com/prebid/prebid-server/openrtb_ext" "github.com/prometheus/client_golang/prometheus" gometrics "github.com/rcrowley/go-metrics" ) @@ -20,3 +21,70 @@ func NewMetricsRegistry() MetricsRegistry { InfluxRegistry: gometrics.NewPrefixedRegistry("prebidserver."), } } + +func (me *MultiMetricsEngine) RecordVASTTagType(biddder, vastTag string) { + for _, thisME := range *me { + thisME.RecordVASTTagType(biddder, vastTag) + } +} + +func (me *MultiMetricsEngine) RecordRejectedBids(pubid, bidder, code string) { + for _, thisME := range *me { + thisME.RecordRejectedBids(pubid, bidder, code) + } +} + +func (me *MultiMetricsEngine) RecordBids(pubid, profileid, biddder, deal string) { + for _, thisME := range *me { + thisME.RecordBids(pubid, profileid, biddder, deal) + } +} +func (me *MultiMetricsEngine) RecordHttpCounter() { +} + +func (me *MultiMetricsEngine) RecordVastVersion(biddder, vastVersion string) { + for _, thisME := range *me { + thisME.RecordVastVersion(biddder, vastVersion) + } +} + +// RecordRejectedBidsForBidder across all engines +func (me *MultiMetricsEngine) RecordRejectedBidsForBidder(bidder openrtb_ext.BidderName) { + for _, thisME := range *me { + thisME.RecordRejectedBidsForBidder(bidder) + } +} + +// RecordDynamicFetchFailure across all engines +func (me *MultiMetricsEngine) RecordDynamicFetchFailure(pubId, code string) { + for _, thisME := range *me { + thisME.RecordDynamicFetchFailure(pubId, code) + } +} + +// RecordVASTTagType as a noop +func (me *NilMetricsEngine) RecordVASTTagType(biddder, vastTag string) { +} + +// RecordDynamicFetchFailure as a noop +func (me *NilMetricsEngine) RecordDynamicFetchFailure(pubId, code string) { +} + +// RecordRejectedBids as a noop +func (me *NilMetricsEngine) RecordRejectedBids(pubid, bidder, code string) { +} + +// RecordBids as a noop +func (me *NilMetricsEngine) RecordBids(pubid, profileid, biddder, deal string) { +} + +// RecordVastVersion as a noop +func (me *NilMetricsEngine) RecordVastVersion(biddder, vastVersion string) { +} + +func (m *NilMetricsEngine) RecordHttpCounter() { +} + +// RecordRejectedBidsForBidder as a noop +func (me *NilMetricsEngine) RecordRejectedBidsForBidder(bidder openrtb_ext.BidderName) { +} diff --git a/metrics/go_metrics_ow.go b/metrics/go_metrics_ow.go index 2ef51f2f132..e03a402ee7a 100644 --- a/metrics/go_metrics_ow.go +++ b/metrics/go_metrics_ow.go @@ -40,3 +40,7 @@ func (me *Metrics) RecordVastVersion(biddder, vastVersion string) { func (me *Metrics) RecordHttpCounter() { } + +// RecordVASTTagType as a noop +func (me *Metrics) RecordVASTTagType(biddder, vastTag string) { +} diff --git a/metrics/metrics.go b/metrics/metrics.go index 09423971c23..8a77a2f1278 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -429,6 +429,7 @@ func SyncerSetUidStatuses() []SyncerSetUidStatus { // two groups should be consistent within themselves, but comparing numbers between groups // is generally not useful. type MetricsEngine interface { + OWMetricsEngine RecordConnectionAccept(success bool) RecordTMaxTimeout() RecordConnectionClose(success bool) @@ -521,12 +522,4 @@ type MetricsEngine interface { //RecordRejectedBids records the rejected bids labeled by pubid, bidder and reason code RecordRejectedBids(pubid, bidder, code string) - - //RecordBids records the bidder deal bids labeled by pubid, profile, bidder and deal - RecordBids(pubid, profileid, bidder, deal string) - - //RecordVastVersion record the count of vast version labelled by bidder and vast version - RecordVastVersion(coreBidder, vastVersion string) - - RecordHttpCounter() } diff --git a/metrics/metrics_mock_ow.go b/metrics/metrics_mock_ow.go index 40e70c0d79e..a3b06ddd9c8 100644 --- a/metrics/metrics_mock_ow.go +++ b/metrics/metrics_mock_ow.go @@ -40,3 +40,8 @@ func (me *MetricsEngineMock) RecordBids(pubid, profileid, biddder, deal string) func (me *MetricsEngineMock) RecordVastVersion(coreBidder, vastVersion string) { me.Called(coreBidder, vastVersion) } + +// RecordVASTTagType mock +func (me *MetricsEngineMock) RecordVASTTagType(bidder, vastTagType string) { + me.Called(bidder, vastTagType) +} diff --git a/metrics/metrics_ow.go b/metrics/metrics_ow.go new file mode 100644 index 00000000000..958394dd03d --- /dev/null +++ b/metrics/metrics_ow.go @@ -0,0 +1,11 @@ +package metrics + +type OWMetricsEngine interface { + RecordHttpCounter() + //RecordBids records the bidder deal bids labeled by pubid, profile, bidder and deal + RecordBids(pubid, profileid, bidder, deal string) + //RecordVastVersion record the count of vast version labelled by bidder and vast version + RecordVastVersion(coreBidder, vastVersion string) + //RecordVASTTagType record the count of vast tag type labeled by bidder and vast tag + RecordVASTTagType(bidder, vastTagType string) +} diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index 3e7d366b2c4..30615024197 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -14,6 +14,7 @@ import ( // Metrics defines the Prometheus metrics backing the MetricsEngine implementation. type Metrics struct { + OWMetrics Registerer prometheus.Registerer Gatherer *prometheus.Registry @@ -57,8 +58,6 @@ type Metrics struct { adsCertSignTimer prometheus.Histogram bidderServerResponseTimer prometheus.Histogram - requestsDuplicateBidIDCounter prometheus.Counter // total request having duplicate bid.id for given bidder - // Adapter Metrics adapterBids *prometheus.CounterVec adapterErrors *prometheus.CounterVec @@ -76,25 +75,12 @@ type Metrics struct { adapterBidResponseSecureMarkupError *prometheus.CounterVec adapterBidResponseSecureMarkupWarn *prometheus.CounterVec - adapterDuplicateBidIDCounter *prometheus.CounterVec - adapterVideoBidDuration *prometheus.HistogramVec - tlsHandhakeTimer *prometheus.HistogramVec + tlsHandhakeTimer *prometheus.HistogramVec // Syncer Metrics syncerRequests *prometheus.CounterVec syncerSets *prometheus.CounterVec - // Rejected Bids - rejectedBids *prometheus.CounterVec - bids *prometheus.CounterVec - vastVersion *prometheus.CounterVec - //rejectedBids *prometheus.CounterVec - accountRejectedBid *prometheus.CounterVec - accountFloorsRequest *prometheus.CounterVec - - //Dynamic Fetch Failure - dynamicFetchFailure *prometheus.CounterVec - // Account Metrics accountRequests *prometheus.CounterVec accountDebugRequests *prometheus.CounterVec @@ -130,21 +116,7 @@ type Metrics struct { moduleTimeouts map[string]*prometheus.CounterVec // Ad Pod Metrics - // podImpGenTimer indicates time taken by impression generator - // algorithm to generate impressions for given ad pod request - podImpGenTimer *prometheus.HistogramVec - - // podImpGenTimer indicates time taken by combination generator - // algorithm to generate combination based on bid response and ad pod request - podCombGenTimer *prometheus.HistogramVec - - // podCompExclTimer indicates time taken by compititve exclusion - // algorithm to generate final pod response based on bid response and ad pod request - podCompExclTimer *prometheus.HistogramVec - metricsDisabled config.DisabledMetrics - - httpCounter prometheus.Counter } const ( @@ -224,6 +196,7 @@ func NewMetrics(cfg config.PrometheusMetrics, reg *prometheus.Registry, disabled if reg == nil { reg = prometheus.NewRegistry() } + metrics.init(cfg, reg) metrics.metricsDisabled = disabledMetrics metrics.connectionsClosed = newCounterWithoutLabels(cfg, reg, @@ -369,11 +342,6 @@ func NewMetrics(cfg config.PrometheusMetrics, reg *prometheus.Registry, disabled // "Seconds to perform TLS Handshake", // standardTimeBuckets) - metrics.bids = newCounter(cfg, reg, - "bids", - "Count of no of bids by publisher id, profile, bidder and deal", - []string{pubIDLabel, profileLabel, bidderLabel, dealLabel}) - metrics.privacyCCPA = newCounter(cfg, reg, "privacy_ccpa", "Count of total requests to Prebid Server where CCPA was provided by source and opt-out .", @@ -554,31 +522,6 @@ func NewMetrics(cfg config.PrometheusMetrics, reg *prometheus.Registry, disabled "Count of total requests to Prebid Server that have stored responses labled by account", []string{accountLabel}) - metrics.accountRejectedBid = newCounter(cfg, reg, - "floors_account_rejected_bid_requests", - "Count of total requests to Prebid Server that have rejected bids due to floors enfocement labled by account", - []string{accountLabel}) - - metrics.accountFloorsRequest = newCounter(cfg, reg, - "floors_account_requests", - "Count of total requests to Prebid Server that have non-zero imp.bidfloor labled by account", - []string{accountLabel}) - - metrics.rejectedBids = newCounter(cfg, reg, - "rejected_bids", - "Count of rejected bids by publisher id, bidder and rejection reason code", - []string{pubIDLabel, bidderLabel, codeLabel}) - - metrics.vastVersion = newCounter(cfg, reg, - "vast_version", - "Count of vast version by bidder and vast version", - []string{adapterLabel, versionLabel}) - - metrics.dynamicFetchFailure = newCounter(cfg, reg, - "floors_account_fetch_err", - "Count of failures in case of dynamic fetch labeled by account", - []string{codeLabel, accountLabel}) - metrics.adsCertSignTimer = newHistogram(cfg, reg, "ads_cert_sign_time", "Seconds to generate an AdsCert header", @@ -645,48 +588,7 @@ func NewMetrics(cfg config.PrometheusMetrics, reg *prometheus.Registry, disabled metrics.Registerer = prometheus.WrapRegistererWithPrefix(metricsPrefix, reg) metrics.Registerer.MustRegister(promCollector.NewGoCollector()) - - metrics.adapterDuplicateBidIDCounter = newCounter(cfg, reg, - "duplicate_bid_ids", - "Number of collisions observed for given adaptor", - []string{adapterLabel}) - - metrics.requestsDuplicateBidIDCounter = newCounterWithoutLabels(cfg, reg, - "requests_having_duplicate_bid_ids", - "Count of number of request where bid collision is detected.") - - // adpod specific metrics - metrics.podImpGenTimer = newHistogramVec(cfg, reg, - "impr_gen", - "Time taken by Ad Pod Impression Generator in seconds", []string{podAlgorithm, podNoOfImpressions}, - // 200 µS, 250 µS, 275 µS, 300 µS - //[]float64{0.000200000, 0.000250000, 0.000275000, 0.000300000}) - // 100 µS, 200 µS, 300 µS, 400 µS, 500 µS, 600 µS, - []float64{0.000100000, 0.000200000, 0.000300000, 0.000400000, 0.000500000, 0.000600000}) - - metrics.podCombGenTimer = newHistogramVec(cfg, reg, - "comb_gen", - "Time taken by Ad Pod Combination Generator in seconds", []string{podAlgorithm, podTotalCombinations}, - // 200 µS, 250 µS, 275 µS, 300 µS - //[]float64{0.000200000, 0.000250000, 0.000275000, 0.000300000}) - []float64{0.000100000, 0.000200000, 0.000300000, 0.000400000, 0.000500000, 0.000600000}) - - metrics.podCompExclTimer = newHistogramVec(cfg, reg, - "comp_excl", - "Time taken by Ad Pod Compititve Exclusion in seconds", []string{podAlgorithm, podNoOfResponseBids}, - // 200 µS, 250 µS, 275 µS, 300 µS - //[]float64{0.000200000, 0.000250000, 0.000275000, 0.000300000}) - []float64{0.000100000, 0.000200000, 0.000300000, 0.000400000, 0.000500000, 0.000600000}) - - metrics.adapterVideoBidDuration = newHistogramVec(cfg, reg, - "adapter_vidbid_dur", - "Video Ad durations returned by the bidder", []string{adapterLabel}, - []float64{4, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 120}) - preloadLabelValues(&metrics, syncerKeys, moduleStageNames) - - metrics.httpCounter = newHttpCounter(cfg, reg) - return &metrics } @@ -905,22 +807,6 @@ func (m *Metrics) RecordStoredResponse(pubId string) { } } -func (m *Metrics) RecordRejectedBidsForAccount(pubId string) { - if pubId != metrics.PublisherUnknown { - m.accountRejectedBid.With(prometheus.Labels{ - accountLabel: pubId, - }).Inc() - } -} - -func (m *Metrics) RecordFloorsRequestForAccount(pubId string) { - if pubId != metrics.PublisherUnknown { - m.accountFloorsRequest.With(prometheus.Labels{ - accountLabel: pubId, - }).Inc() - } -} - func (m *Metrics) RecordImps(labels metrics.ImpLabels) { m.impressions.With(prometheus.Labels{ isBannerLabel: strconv.FormatBool(labels.BannerImps), @@ -1019,15 +905,6 @@ func (m *Metrics) RecordRejectedBidsForBidder(Adapter openrtb_ext.BidderName) { } } -func (m *Metrics) RecordDynamicFetchFailure(pubId, code string) { - if pubId != metrics.PublisherUnknown { - m.dynamicFetchFailure.With(prometheus.Labels{ - accountLabel: pubId, - codeLabel: code, - }).Inc() - } -} - // Keeps track of created and reused connections to adapter bidders and the time from the // connection request, to the connection creation, or reuse from the pool across all engines func (m *Metrics) RecordAdapterConnections(adapterName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) { diff --git a/metrics/prometheus/prometheus_ow.go b/metrics/prometheus/prometheus_ow.go index ad80c8f5b6f..be9977626b6 100644 --- a/metrics/prometheus/prometheus_ow.go +++ b/metrics/prometheus/prometheus_ow.go @@ -10,13 +10,44 @@ import ( ) const ( - pubIDLabel = "pubid" - bidderLabel = "bidder" - codeLabel = "code" - profileLabel = "profileid" - dealLabel = "deal" + pubIDLabel = "pubid" + bidderLabel = "bidder" + codeLabel = "code" + profileLabel = "profileid" + dealLabel = "deal" + vastTagTypeLabel = "type" ) +type OWMetrics struct { + vastTagType *prometheus.CounterVec + // Rejected Bids + rejectedBids *prometheus.CounterVec + bids *prometheus.CounterVec + vastVersion *prometheus.CounterVec + //rejectedBids *prometheus.CounterVec + accountRejectedBid *prometheus.CounterVec + accountFloorsRequest *prometheus.CounterVec + + //Dynamic Fetch Failure + dynamicFetchFailure *prometheus.CounterVec + adapterDuplicateBidIDCounter *prometheus.CounterVec + requestsDuplicateBidIDCounter prometheus.Counter // total request having duplicate bid.id for given bidder + adapterVideoBidDuration *prometheus.HistogramVec + + // podImpGenTimer indicates time taken by impression generator + // algorithm to generate impressions for given ad pod request + podImpGenTimer *prometheus.HistogramVec + + // podImpGenTimer indicates time taken by combination generator + // algorithm to generate combination based on bid response and ad pod request + podCombGenTimer *prometheus.HistogramVec + + // podCompExclTimer indicates time taken by compititve exclusion + // algorithm to generate final pod response based on bid response and ad pod request + podCompExclTimer *prometheus.HistogramVec + httpCounter prometheus.Counter +} + func newHttpCounter(cfg config.PrometheusMetrics, registry *prometheus.Registry) prometheus.Counter { httpCounter := prometheus.NewCounter(prometheus.CounterOpts{ Name: "http_requests_total", @@ -30,7 +61,7 @@ func newHttpCounter(cfg config.PrometheusMetrics, registry *prometheus.Registry) // gives the bid response with multiple bids containing same bid.ID // ensure collisions value is greater than 1. This function will not give any error // if collisions = 1 is passed -func (m *Metrics) RecordAdapterDuplicateBidID(adaptor string, collisions int) { +func (m *OWMetrics) RecordAdapterDuplicateBidID(adaptor string, collisions int) { m.adapterDuplicateBidIDCounter.With(prometheus.Labels{ adapterLabel: adaptor, }).Add(float64(collisions)) @@ -38,7 +69,7 @@ func (m *Metrics) RecordAdapterDuplicateBidID(adaptor string, collisions int) { // RecordRequestHavingDuplicateBidID keeps count of request when duplicate bid.id is // detected in partner's response -func (m *Metrics) RecordRequestHavingDuplicateBidID() { +func (m *OWMetrics) RecordRequestHavingDuplicateBidID() { m.requestsDuplicateBidIDCounter.Inc() } @@ -66,32 +97,32 @@ func recordAlgoTime(timer *prometheus.HistogramVec, labels metrics.PodLabels, el // RecordPodImpGenTime records number of impressions generated and time taken // by underneath algorithm to generate them -func (m *Metrics) RecordPodImpGenTime(labels metrics.PodLabels, start time.Time) { +func (m *OWMetrics) RecordPodImpGenTime(labels metrics.PodLabels, start time.Time) { elapsedTime := time.Since(start) recordAlgoTime(m.podImpGenTimer, labels, elapsedTime) } // RecordPodCombGenTime records number of combinations generated and time taken // by underneath algorithm to generate them -func (m *Metrics) RecordPodCombGenTime(labels metrics.PodLabels, elapsedTime time.Duration) { +func (m *OWMetrics) RecordPodCombGenTime(labels metrics.PodLabels, elapsedTime time.Duration) { recordAlgoTime(m.podCombGenTimer, labels, elapsedTime) } // RecordPodCompititveExclusionTime records number of combinations comsumed for forming // final ad pod response and time taken by underneath algorithm to generate them -func (m *Metrics) RecordPodCompititveExclusionTime(labels metrics.PodLabels, elapsedTime time.Duration) { +func (m *OWMetrics) RecordPodCompititveExclusionTime(labels metrics.PodLabels, elapsedTime time.Duration) { recordAlgoTime(m.podCompExclTimer, labels, elapsedTime) } // RecordAdapterVideoBidDuration records actual ad duration (>0) returned by the bidder -func (m *Metrics) RecordAdapterVideoBidDuration(labels metrics.AdapterLabels, videoBidDuration int) { +func (m *OWMetrics) RecordAdapterVideoBidDuration(labels metrics.AdapterLabels, videoBidDuration int) { if videoBidDuration > 0 { m.adapterVideoBidDuration.With(prometheus.Labels{adapterLabel: string(labels.Adapter)}).Observe(float64(videoBidDuration)) } } // RecordRejectedBids records rejected bids labeled by pubid, bidder and reason code -func (m *Metrics) RecordRejectedBids(pubid, biddder, code string) { +func (m *OWMetrics) RecordRejectedBids(pubid, biddder, code string) { m.rejectedBids.With(prometheus.Labels{ pubIDLabel: pubid, bidderLabel: biddder, @@ -100,7 +131,7 @@ func (m *Metrics) RecordRejectedBids(pubid, biddder, code string) { } // RecordBids records bids labeled by pubid, profileid, bidder and deal -func (m *Metrics) RecordBids(pubid, profileid, biddder, deal string) { +func (m *OWMetrics) RecordBids(pubid, profileid, biddder, deal string) { m.bids.With(prometheus.Labels{ pubIDLabel: pubid, profileLabel: profileid, @@ -110,13 +141,120 @@ func (m *Metrics) RecordBids(pubid, profileid, biddder, deal string) { } // RecordVastVersion record the count of vast version labelled by bidder and vast version -func (m *Metrics) RecordVastVersion(coreBiddder, vastVersion string) { +func (m *OWMetrics) RecordVastVersion(coreBiddder, vastVersion string) { m.vastVersion.With(prometheus.Labels{ adapterLabel: coreBiddder, versionLabel: vastVersion, }).Inc() } +// RecordVASTTagType record the count of vast tags labeled by bidder and vast tag +func (m *OWMetrics) RecordVASTTagType(bidder, vastTagType string) { + m.vastTagType.With(prometheus.Labels{ + bidderLabel: bidder, + vastTagTypeLabel: vastTagType, + }).Inc() +} +func (m *Metrics) RecordRejectedBidsForAccount(pubId string) { + if pubId != metrics.PublisherUnknown { + m.accountRejectedBid.With(prometheus.Labels{ + accountLabel: pubId, + }).Inc() + } +} + +func (m *Metrics) RecordFloorsRequestForAccount(pubId string) { + if pubId != metrics.PublisherUnknown { + m.accountFloorsRequest.With(prometheus.Labels{ + accountLabel: pubId, + }).Inc() + } +} +func (m *Metrics) RecordDynamicFetchFailure(pubId, code string) { + if pubId != metrics.PublisherUnknown { + m.dynamicFetchFailure.With(prometheus.Labels{ + accountLabel: pubId, + codeLabel: code, + }).Inc() + } +} + func (m *Metrics) RecordHttpCounter() { m.httpCounter.Inc() } + +func (m *OWMetrics) init(cfg config.PrometheusMetrics, reg *prometheus.Registry) { + m.httpCounter = newHttpCounter(cfg, reg) + m.rejectedBids = newCounter(cfg, reg, + "rejected_bids", + "Count of rejected bids by publisher id, bidder and rejection reason code", + []string{pubIDLabel, bidderLabel, codeLabel}) + + m.vastVersion = newCounter(cfg, reg, + "vast_version", + "Count of vast version by bidder and vast version", + []string{adapterLabel, versionLabel}) + + m.vastTagType = newCounter(cfg, reg, + "vast_tag_type", + "Count of vast tag by bidder and vast tag type (Wrapper, InLine, URL, Unknown)", + []string{bidderLabel, vastTagTypeLabel}) + + m.dynamicFetchFailure = newCounter(cfg, reg, + "floors_account_fetch_err", + "Count of failures in case of dynamic fetch labeled by account", + []string{codeLabel, accountLabel}) + + m.adapterDuplicateBidIDCounter = newCounter(cfg, reg, + "duplicate_bid_ids", + "Number of collisions observed for given adaptor", + []string{adapterLabel}) + + m.requestsDuplicateBidIDCounter = newCounterWithoutLabels(cfg, reg, + "requests_having_duplicate_bid_ids", + "Count of number of request where bid collision is detected.") + + m.adapterVideoBidDuration = newHistogramVec(cfg, reg, + "adapter_vidbid_dur", + "Video Ad durations returned by the bidder", []string{adapterLabel}, + []float64{4, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 120}) + + m.bids = newCounter(cfg, reg, + "bids", + "Count of no of bids by publisher id, profile, bidder and deal", + []string{pubIDLabel, profileLabel, bidderLabel, dealLabel}) + + m.accountRejectedBid = newCounter(cfg, reg, + "floors_account_rejected_bid_requests", + "Count of total requests to Prebid Server that have rejected bids due to floors enfocement labled by account", + []string{accountLabel}) + + m.accountFloorsRequest = newCounter(cfg, reg, + "floors_account_requests", + "Count of total requests to Prebid Server that have non-zero imp.bidfloor labled by account", + []string{accountLabel}) + + // adpod specific metrics + m.podImpGenTimer = newHistogramVec(cfg, reg, + "impr_gen", + "Time taken by Ad Pod Impression Generator in seconds", []string{podAlgorithm, podNoOfImpressions}, + // 200 µS, 250 µS, 275 µS, 300 µS + //[]float64{0.000200000, 0.000250000, 0.000275000, 0.000300000}) + // 100 µS, 200 µS, 300 µS, 400 µS, 500 µS, 600 µS, + []float64{0.000100000, 0.000200000, 0.000300000, 0.000400000, 0.000500000, 0.000600000}) + + m.podCombGenTimer = newHistogramVec(cfg, reg, + "comb_gen", + "Time taken by Ad Pod Combination Generator in seconds", []string{podAlgorithm, podTotalCombinations}, + // 200 µS, 250 µS, 275 µS, 300 µS + //[]float64{0.000200000, 0.000250000, 0.000275000, 0.000300000}) + []float64{0.000100000, 0.000200000, 0.000300000, 0.000400000, 0.000500000, 0.000600000}) + + m.podCompExclTimer = newHistogramVec(cfg, reg, + "comp_excl", + "Time taken by Ad Pod Compititve Exclusion in seconds", []string{podAlgorithm, podNoOfResponseBids}, + // 200 µS, 250 µS, 275 µS, 300 µS + //[]float64{0.000200000, 0.000250000, 0.000275000, 0.000300000}) + []float64{0.000100000, 0.000200000, 0.000300000, 0.000400000, 0.000500000, 0.000600000}) + +} diff --git a/metrics/prometheus/prometheus_ow_test.go b/metrics/prometheus/prometheus_ow_test.go index 838a9c7c301..35a8c9d05b8 100644 --- a/metrics/prometheus/prometheus_ow_test.go +++ b/metrics/prometheus/prometheus_ow_test.go @@ -14,12 +14,12 @@ func TestRecordRejectedBids(t *testing.T) { expCount int } testCases := []struct { - description string - in testIn - out testOut + name string + in testIn + out testOut }{ { - description: "record rejected bids", + name: "record rejected bids", in: testIn{ pubid: "1010", bidder: "bidder", @@ -55,12 +55,12 @@ func TestRecordBids(t *testing.T) { expCount int } testCases := []struct { - description string - in testIn - out testOut + name string + in testIn + out testOut }{ { - description: "record bids", + name: "record bids", in: testIn{ pubid: "1010", bidder: "bidder", @@ -98,12 +98,12 @@ func TestRecordVastVersion(t *testing.T) { expCount int } testCases := []struct { - description string - in testIn - out testOut + name string + in testIn + out testOut }{ { - description: "record vast version", + name: "record vast version", in: testIn{ coreBidder: "bidder", vastVersion: "2.0", @@ -127,3 +127,44 @@ func TestRecordVastVersion(t *testing.T) { }) } } + +func TestRecordVASTTagType(t *testing.T) { + type args struct { + bidder, vastTagType string + } + type want struct { + expCount int + } + tests := []struct { + name string + args args + want want + }{ + { + name: "record_vast_tag", + args: args{ + bidder: "bidder", + vastTagType: "Wrapper", + }, + want: want{ + expCount: 1, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + pm := createMetricsForTesting() + pm.RecordVASTTagType(tt.args.bidder, tt.args.vastTagType) + assertCounterVecValue(t, + "", + "record vastTag", + pm.vastTagType, + float64(tt.want.expCount), + prometheus.Labels{ + bidderLabel: tt.args.bidder, + vastTagTypeLabel: tt.args.vastTagType, + }) + }) + } +} From 0aa7a35614b5a6ba7b728d6732c3f853cdfb9e86 Mon Sep 17 00:00:00 2001 From: Ankit-Pinge <128145896+Ankit-Pinge@users.noreply.github.com> Date: Thu, 26 Oct 2023 14:31:21 +0530 Subject: [PATCH 03/30] OTT-1327: Support for passing custom key-values in VAST Bidder using kv and kvm macros (#578) --- adapters/vastbidder/bidder_macro.go | 52 +++-- adapters/vastbidder/bidder_macro_test.go | 248 +++++++++++++++++++---- adapters/vastbidder/constant.go | 5 +- adapters/vastbidder/ibidder_macro.go | 2 +- adapters/vastbidder/macro_processor.go | 6 +- adapters/vastbidder/util.go | 59 ++++++ adapters/vastbidder/util_test.go | 195 ++++++++++++++++++ 7 files changed, 503 insertions(+), 64 deletions(-) create mode 100644 adapters/vastbidder/util_test.go diff --git a/adapters/vastbidder/bidder_macro.go b/adapters/vastbidder/bidder_macro.go index 2fbba3c28a4..7c95e45bbb4 100644 --- a/adapters/vastbidder/bidder_macro.go +++ b/adapters/vastbidder/bidder_macro.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "net/http" - "net/url" "strconv" "strings" "time" @@ -177,16 +176,35 @@ func (tag *BidderMacro) GetHeaders() http.Header { return http.Header{} } -// GetValueFromKV returns the value from KV map wrt key -func (tag *BidderMacro) GetValueFromKV(key string) string { - if tag.KV == nil { - return "" - } - key = strings.TrimPrefix(key, kvPrefix) - if value, found := tag.KV[key]; found { - return fmt.Sprintf("%v", value) +// GetValue returns the value for given key +// isKeyFound will check the key is present or not +func (tag *BidderMacro) GetValue(key string) (string, bool) { + macroKeys := strings.Split(key, ".") + isKeyFound := false + + // This will check if key has prefix kv/kvm + // if prefix present it will always return isKeyFound as true as it will help to replace the key with empty string in VAST TAG + if (macroKeys[0] == MacroKV || macroKeys[0] == MacroKVM) && len(macroKeys) > 1 { + isKeyFound = true + if tag.KV == nil { + return "", isKeyFound + } + switch macroKeys[0] { + case MacroKV: + val := getValueFromMap(macroKeys[1:], tag.KV) + if dataMap, ok := val.(map[string]interface{}); ok { + return mapToQuery(dataMap), isKeyFound + } + return fmt.Sprintf("%v", val), isKeyFound + case MacroKVM: + val := getValueFromMap(macroKeys[1:], tag.KV) + if isMap(val) { + return getJSONString(val), isKeyFound + } + return fmt.Sprintf("%v", val), isKeyFound + } } - return "" + return "", isKeyFound } /********************* Request *********************/ @@ -1209,13 +1227,7 @@ func (tag *BidderMacro) MacroKV(key string) string { if tag.KV == nil { return "" } - - values := url.Values{} - for key, val := range tag.KV { - values.Add(key, fmt.Sprintf("%v", val)) - } - return values.Encode() - + return mapToQuery(tag.KV) } // MacroKVM replace the kvm macro @@ -1223,11 +1235,7 @@ func (tag *BidderMacro) MacroKVM(key string) string { if tag.KV == nil { return "" } - jsonBytes, err := json.Marshal(tag.KV) - if err != nil { - return "" - } - return string(jsonBytes) + return getJSONString(tag.KV) } /********************* Request Headers *********************/ diff --git a/adapters/vastbidder/bidder_macro_test.go b/adapters/vastbidder/bidder_macro_test.go index 92daedff663..a5db950800f 100644 --- a/adapters/vastbidder/bidder_macro_test.go +++ b/adapters/vastbidder/bidder_macro_test.go @@ -1264,7 +1264,7 @@ func TestBidderMacro_MacroTest(t *testing.T) { } } -func TestBidderGetValueFromKV(t *testing.T) { +func TestBidderGetValue(t *testing.T) { type fields struct { KV map[string]interface{} } @@ -1272,41 +1272,157 @@ func TestBidderGetValueFromKV(t *testing.T) { key string } tests := []struct { - name string - fields fields - args args - want string - found bool + name string + fields fields + args args + want string + isKeyFound bool // if key has the prefix kv/kvm then it should return thr isKeyFound true }{ { - name: "Valid_Key", + name: "valid_Key", fields: fields{KV: map[string]interface{}{ "name": "test", "age": 22, }}, - args: args{key: "kv.name"}, - want: "test", + args: args{key: "kv.name"}, + want: "test", + isKeyFound: true, }, { - name: "Invalid_Key", + name: "invalid_Key", fields: fields{KV: map[string]interface{}{ "name": "test", "age": 22, }}, - args: args{key: "kv.anykey"}, - want: "", + args: args{key: "kv.anykey"}, + want: "", + isKeyFound: true, }, { - name: "Empty_KV_Map", - fields: fields{KV: nil}, - args: args{key: "kv.anykey"}, - want: "", + name: "empty_kv_map", + fields: fields{KV: nil}, + args: args{key: "kv.anykey"}, + want: "", + isKeyFound: true, }, { - name: "KV_map_with_no_key_val_pair", - fields: fields{KV: map[string]interface{}{}}, - args: args{key: "kv.anykey"}, - want: "", + name: "kv_map_with_no_key_val_pair", + fields: fields{KV: map[string]interface{}{}}, + args: args{key: "kv.anykey"}, + want: "", + isKeyFound: true, + }, + { + name: "key_with_value_as_url", + fields: fields{KV: map[string]interface{}{ + "name": "test", + "country": map[string]interface{}{ + "state": "MH", + "pincode": 411041, + "url": "http://example.com?k1=v1&k2=v2", + }, + }}, + args: args{key: "kvm.country.url"}, + want: "http://example.com?k1=v1&k2=v2", + isKeyFound: true, + }, + { + name: "kvm_prefix_key_with_value_as_nested_map", + fields: fields{KV: map[string]interface{}{ + "name": "test", + "country": map[string]interface{}{ + "state": "MH", + "pincode": 411041, + "url": "http//example.com?k1=v1&k2=v2", + "metadata": map[string]interface{}{ + "k1": "v1", + "k2": "v2", + }, + }, + }}, + args: args{key: "kvm.country"}, + want: "{\"metadata\":{\"k1\":\"v1\",\"k2\":\"v2\"},\"pincode\":411041,\"state\":\"MH\",\"url\":\"http//example.com?k1=v1&k2=v2\"}", + isKeyFound: true, + }, + { + name: "kv_prefix_key_with_value_as_nested_map", + fields: fields{KV: map[string]interface{}{ + "name": "test", + "country": map[string]interface{}{ + "state": "MH", + "pincode": 411041, + "url": "http://example.com?k1=v1&k2=v2", + "metadata": map[string]interface{}{ + "k1": "v1", + "k2": "v2", + }, + }, + }}, + args: args{key: "kv.country"}, + want: "metadata=k1%3Dv1%26k2%3Dv2&pincode=411041&state=MH&url=http%3A%2F%2Fexample.com%3Fk1%3Dv1%26k2%3Dv2", + isKeyFound: true, + }, + { + name: "key_without_kv_kvm_prefix", + fields: fields{KV: map[string]interface{}{ + "name": "test", + "country": map[string]interface{}{ + "state": "MH", + "pincode": 411041, + "url": "http//example.com?k1=v1&k2=v2", + "metadata": map[string]interface{}{ + "k1": "v1", + "k2": "v2", + }, + }, + }}, + args: args{key: "someprefix.kv"}, + want: "", + isKeyFound: false, // hence this key is not starting with kv/kvm prefix we return isKeyFound as false + }, + { + name: "multi-level_key", + fields: fields{KV: map[string]interface{}{ + "k1": map[string]interface{}{ + "k2": map[string]interface{}{ + "k3": map[string]interface{}{ + "k4": map[string]interface{}{ + "name": "test", + }, + }, + }, + }, + }}, + args: args{key: "kv.k1.k2.k3.k4.name"}, + want: "test", + isKeyFound: true, + }, + { + name: "key_not_matched", + fields: fields{KV: map[string]interface{}{ + "k1": map[string]interface{}{ + "k2": map[string]interface{}{ + "k3": map[string]interface{}{ + "k4": map[string]interface{}{ + "name": "test", + }, + }, + }, + }, + }}, + args: args{key: "kv.k1.k2.k3.name"}, + want: "", + isKeyFound: true, + }, + { + name: "key_wihtout_any_prefix", + fields: fields{KV: map[string]interface{}{ + "name": "test", + "age": 22, + }}, + args: args{key: "kv"}, + want: "", + isKeyFound: false, }, } for _, tt := range tests { @@ -1314,8 +1430,9 @@ func TestBidderGetValueFromKV(t *testing.T) { tag := &BidderMacro{ KV: tt.fields.KV, } - value := tag.GetValueFromKV(tt.args.key) + value, isKeyFound := tag.GetValue(tt.args.key) assert.Equal(t, tt.want, value, tt.name) + assert.Equal(t, tt.isKeyFound, isKeyFound) }) } } @@ -1334,7 +1451,7 @@ func TestBidderMacroKV(t *testing.T) { want string }{ { - name: "Valid_test", + name: "valid_test", fields: fields{KV: map[string]interface{}{ "name": "test", "age": "22", @@ -1343,27 +1460,63 @@ func TestBidderMacroKV(t *testing.T) { want: "age=22&name=test", }, { - name: "Valid_test_with_url", + name: "valid_test_with_url", fields: fields{KV: map[string]interface{}{ - "name": "test", - "age": "22", - "url": "http://example.com?k1=v1&k2=v2", + "age": "22", + "url": "http://example.com?k1=v1&k2=v2", + }}, + args: args{key: "kv"}, + want: "age=22&url=http%3A%2F%2Fexample.com%3Fk1%3Dv1%26k2%3Dv2", + }, + { + name: "valid_test_with_encoded_url", + fields: fields{KV: map[string]interface{}{ + "age": "22", + "url": "http%3A%2F%2Fexample.com%3Fk1%3Dv1%26k2%3Dv2", }}, args: args{key: "kv"}, - want: "age=22&name=test&url=http%3A%2F%2Fexample.com%3Fk1%3Dv1%26k2%3Dv2", + want: "age=22&url=http%253A%252F%252Fexample.com%253Fk1%253Dv1%2526k2%253Dv2", }, { - name: "Empty_KV_map", + name: "empty_KV_map", fields: fields{KV: nil}, args: args{key: "kv"}, want: "", }, { - name: "KV_map_with_no_key_val_pair", + name: "kv_map_with_no_key_val_pair", fields: fields{KV: map[string]interface{}{}}, args: args{key: "kv"}, want: "", }, + { + name: "key_with_value_as_map", + fields: fields{KV: map[string]interface{}{ + "age": 22, + "country": map[string]interface{}{ + "state": "MH", + "pincode": 411041, + }, + }}, + args: args{key: "kv"}, + want: "age=22&country=pincode%3D411041%26state%3DMH", + }, + { + name: "key_with_value_as_nested_map", + fields: fields{KV: map[string]interface{}{ + "age": 22, + "country": map[string]interface{}{ + "state": "MH", + "pincode": 411041, + "metadata": map[string]interface{}{ + "k1": 223, + "k2": "v2", + }, + }, + }}, + args: args{key: "kv"}, + want: "age=22&country=metadata%3Dk1%253D223%2526k2%253Dv2%26pincode%3D411041%26state%3DMH", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1371,6 +1524,7 @@ func TestBidderMacroKV(t *testing.T) { KV: tt.fields.KV, } got := tag.MacroKV(tt.args.key) + assert.Equal(t, tt.want, got, tt.name) }) } @@ -1390,7 +1544,7 @@ func TestBidderMacroKVM(t *testing.T) { want string }{ { - name: "Valid_test", + name: "valid_test", fields: fields{KV: map[string]interface{}{ "name": "test", "age": "22", @@ -1399,13 +1553,13 @@ func TestBidderMacroKVM(t *testing.T) { want: "{\"age\":\"22\",\"name\":\"test\"}", }, { - name: "Empty_KV_map", + name: "empty_kv_map", fields: fields{KV: nil}, args: args{key: "kvm"}, want: "", }, { - name: "Value_as_int_data_type", + name: "value_as_int_data_type", fields: fields{KV: map[string]interface{}{ "name": "test", "age": 22, @@ -1414,13 +1568,13 @@ func TestBidderMacroKVM(t *testing.T) { want: "{\"age\":22,\"name\":\"test\"}", }, { - name: "KV_map_with_no_key_val_pair", + name: "kv_map_with_no_key_val_pair", fields: fields{KV: map[string]interface{}{}}, args: args{key: "kvm"}, want: "{}", }, { - name: "Marshal_error", + name: "marshal_error", fields: fields{KV: map[string]interface{}{ "name": "test", "age": make(chan int), @@ -1428,6 +1582,32 @@ func TestBidderMacroKVM(t *testing.T) { args: args{key: "kvm"}, want: "", }, + { + name: "test_with_url", + fields: fields{KV: map[string]interface{}{ + "name": "test", + "url": "http://example.com?k1=v1&k2=v2", + }}, + args: args{key: "kvm"}, + want: "{\"name\":\"test\",\"url\":\"http://example.com?k1=v1&k2=v2\"}", + }, + { + name: "key_with_value_as_nested_map", + fields: fields{KV: map[string]interface{}{ + "name": "test", + "age": 22, + "country": map[string]interface{}{ + "state": "MH", + "pincode": 411041, + "metadata": map[string]interface{}{ + "k1": "v1", + "k2": "v2", + }, + }, + }}, + args: args{key: "kvm"}, + want: "{\"age\":22,\"country\":{\"metadata\":{\"k1\":\"v1\",\"k2\":\"v2\"},\"pincode\":411041,\"state\":\"MH\"},\"name\":\"test\"}", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/adapters/vastbidder/constant.go b/adapters/vastbidder/constant.go index 6a0d707fab5..1ba81122f78 100644 --- a/adapters/vastbidder/constant.go +++ b/adapters/vastbidder/constant.go @@ -169,9 +169,8 @@ const ( ) const ( - prebid = "prebid" - keyval = "keyval" - kvPrefix = "kv." + prebid = "prebid" + keyval = "keyval" ) var ParamKeys = []string{"param1", "param2", "param3", "param4", "param5"} diff --git a/adapters/vastbidder/ibidder_macro.go b/adapters/vastbidder/ibidder_macro.go index 6d7e555408c..a503c667e52 100644 --- a/adapters/vastbidder/ibidder_macro.go +++ b/adapters/vastbidder/ibidder_macro.go @@ -18,7 +18,7 @@ type IBidderMacro interface { SetAdapterConfig(*config.Adapter) GetURI() string GetHeaders() http.Header - GetValueFromKV(string) string + GetValue(string) (string, bool) //getAllHeaders returns default and custom heades getAllHeaders() http.Header diff --git a/adapters/vastbidder/macro_processor.go b/adapters/vastbidder/macro_processor.go index 354d922e1db..c6f2f3d871e 100644 --- a/adapters/vastbidder/macro_processor.go +++ b/adapters/vastbidder/macro_processor.go @@ -88,10 +88,8 @@ func (mp *MacroProcessor) processKey(key string) (string, bool) { tmpKey = tmpKey[0 : len(tmpKey)-macroEscapeSuffixLen] nEscaping++ continue - } else if strings.HasPrefix(tmpKey, kvPrefix) { - value = mp.bidderMacro.GetValueFromKV(tmpKey) - found = true - break + } else { + value, found = mp.bidderMacro.GetValue(tmpKey) } break } diff --git a/adapters/vastbidder/util.go b/adapters/vastbidder/util.go index 8ad02535ec6..c1ddb53fb4a 100644 --- a/adapters/vastbidder/util.go +++ b/adapters/vastbidder/util.go @@ -5,7 +5,10 @@ import ( "encoding/json" "fmt" "math/rand" + "net/url" + "reflect" "strconv" + "strings" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/openrtb_ext" @@ -68,3 +71,59 @@ func NormalizeJSON(obj map[string]interface{}) map[string]string { var GetRandomID = func() string { return strconv.FormatInt(rand.Int63(), intBase) } + +func getJSONString(kvmap any) string { + + var buf bytes.Buffer + encoder := json.NewEncoder(&buf) + + // Disable HTML escaping for special characters + encoder.SetEscapeHTML(false) + + if err := encoder.Encode(kvmap); err != nil { + return "" + } + return strings.TrimRight(buf.String(), "\n") + +} + +func isMap(data any) bool { + return reflect.TypeOf(data).Kind() == reflect.Map +} + +// extractDataFromMap help to get value from nested map +func getValueFromMap(lookUpOrder []string, m map[string]any) any { + if len(lookUpOrder) == 0 { + return "" + } + + for _, key := range lookUpOrder { + value, keyExists := m[key] + if !keyExists { + return "" + } + if nestedMap, isMap := value.(map[string]any); isMap { + m = nestedMap + } else { + return value + } + } + return m +} + +// mapToQuery convert the map data into & seperated string +func mapToQuery(m map[string]any) string { + values := url.Values{} + for key, value := range m { + switch reflect.TypeOf(value).Kind() { + case reflect.Map: + mvalue, ok := value.(map[string]any) + if ok { + values.Add(key, mapToQuery(mvalue)) + } + default: + values.Add(key, fmt.Sprintf("%v", value)) + } + } + return values.Encode() +} diff --git a/adapters/vastbidder/util_test.go b/adapters/vastbidder/util_test.go new file mode 100644 index 00000000000..e8c8681c19f --- /dev/null +++ b/adapters/vastbidder/util_test.go @@ -0,0 +1,195 @@ +package vastbidder + +import ( + "testing" + + "github.com/magiconair/properties/assert" +) + +func Test_getJSONString(t *testing.T) { + type args struct { + kvmap any + } + tests := []struct { + name string + args args + want string + }{ + { + name: "empty_map", + args: args{kvmap: map[string]any{}}, + want: "{}", + }, + { + name: "map_without_nesting", + args: args{kvmap: map[string]any{ + "k1": "v1", + "k2": "v2", + }}, + want: "{\"k1\":\"v1\",\"k2\":\"v2\"}", + }, + { + name: "map_with_nesting", + args: args{kvmap: map[string]any{ + "k1": "v1", + "metadata": map[string]any{ + "k2": "v2", + "k3": "v3", + }, + }}, + want: "{\"k1\":\"v1\",\"metadata\":{\"k2\":\"v2\",\"k3\":\"v3\"}}", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getJSONString(tt.args.kvmap) + assert.Equal(t, got, tt.want, tt.name) + }) + } +} + +func Test_getValueFromMap(t *testing.T) { + type args struct { + lookUpOrder []string + m map[string]any + } + tests := []struct { + name string + args args + want any + }{ + { + name: "map_without_nesting", + args: args{lookUpOrder: []string{"k1"}, + m: map[string]any{ + "k1": "v1", + "k2": "v2", + }, + }, + want: "v1", + }, + { + name: "map_with_nesting", + args: args{lookUpOrder: []string{"country", "state"}, + m: map[string]any{ + "name": "test", + "country": map[string]any{ + "state": "MH", + "pin": 12345, + }, + }, + }, + want: "MH", + }, + { + name: "key_not_exists", + args: args{lookUpOrder: []string{"country", "name"}, + m: map[string]any{ + "name": "test", + "country": map[string]any{ + "state": "MH", + "pin": 12345, + }, + }, + }, + want: "", + }, + { + name: "empty_m", + args: args{lookUpOrder: []string{"country", "name"}, + m: map[string]any{}, + }, + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getValueFromMap(tt.args.lookUpOrder, tt.args.m) + assert.Equal(t, got, tt.want) + }) + } +} + +func Test_mapToQuery(t *testing.T) { + type args struct { + m map[string]any + } + tests := []struct { + name string + args args + want string + }{ + { + name: "map_without_nesting", + args: args{ + m: map[string]any{ + "k1": "v1", + "k2": "v2", + }, + }, + want: "k1=v1&k2=v2", + }, + { + name: "map_with_nesting", + args: args{ + m: map[string]any{ + "name": "test", + "country": map[string]any{ + "state": "MH", + "pin": 12345, + }, + }, + }, + want: "country=pin%3D12345%26state%3DMH&name=test", + }, + { + name: "empty_map", + args: args{ + m: map[string]any{}, + }, + want: "", + }, + { + name: "nil_map", + args: args{ + m: nil, + }, + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := mapToQuery(tt.args.m); got != tt.want { + t.Errorf("mapToQuery() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_isMap(t *testing.T) { + type args struct { + data any + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "map_data_type", + args: args{data: map[string]any{}}, + want: true, + }, + { + name: "string_data_type", + args: args{data: "data type is string"}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := isMap(tt.args.data) + assert.Equal(t, got, tt.want) + }) + } +} From fd2a55ecf40293fe7314be32e2dd3065be7cf933 Mon Sep 17 00:00:00 2001 From: Pubmatic-Supriya-Patil <131644110+Pubmatic-Supriya-Patil@users.noreply.github.com> Date: Fri, 27 Oct 2023 11:49:27 +0530 Subject: [PATCH 04/30] OTT-1410: TEMPORARY: removed warning message when module is not enabled (#615) --- hooks/plan.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hooks/plan.go b/hooks/plan.go index d83db2f77c1..042c60708d5 100644 --- a/hooks/plan.go +++ b/hooks/plan.go @@ -3,7 +3,6 @@ package hooks import ( "time" - "github.com/golang/glog" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/hooks/hookstage" ) @@ -209,9 +208,10 @@ func getGroup[T any](getHookFn hookFn[T], cfg config.HookExecutionGroup) Group[T for _, hookCfg := range cfg.HookSequence { if h, ok := getHookFn(hookCfg.ModuleCode); ok { group.Hooks = append(group.Hooks, HookWrapper[T]{Module: hookCfg.ModuleCode, Code: hookCfg.HookImplCode, Hook: h}) - } else { - glog.Warningf("Not found hook while building hook execution plan: %s %s", hookCfg.ModuleCode, hookCfg.HookImplCode) } + // else { + // glog.Warningf("Not found hook while building hook execution plan: %s %s", hookCfg.ModuleCode, hookCfg.HookImplCode) + // } } return group From 4f0f731e31d00046a4b6ea8e4b6046245c89b642 Mon Sep 17 00:00:00 2001 From: Nikhil Vaidya <102963966+pm-nikhil-vaidya@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:14:51 +0530 Subject: [PATCH 05/30] OTT-1402 :: Make BidId unique for auction in auctionresponsehook (#611) --- .../openwrap/adunitconfig/common_test.go | 4 +- .../openwrap/allprocessedbidresponsehook.go | 43 +++++++ .../allprocessedbidresponsehook_test.go | 69 +++++++++++ .../pubmatic/openwrap/auctionresponsehook.go | 12 +- .../openwrap/auctionresponsehook_test.go | 69 +++++++++++ modules/pubmatic/openwrap/models/constants.go | 6 +- modules/pubmatic/openwrap/module.go | 19 +++ modules/pubmatic/openwrap/utils/bid.go | 17 +++ modules/pubmatic/openwrap/utils/bid_test.go | 111 ++++++++++++++++++ 9 files changed, 346 insertions(+), 4 deletions(-) create mode 100644 modules/pubmatic/openwrap/allprocessedbidresponsehook.go create mode 100644 modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go create mode 100644 modules/pubmatic/openwrap/utils/bid.go create mode 100644 modules/pubmatic/openwrap/utils/bid_test.go diff --git a/modules/pubmatic/openwrap/adunitconfig/common_test.go b/modules/pubmatic/openwrap/adunitconfig/common_test.go index f76f91538cc..e6b0f0648ce 100644 --- a/modules/pubmatic/openwrap/adunitconfig/common_test.go +++ b/modules/pubmatic/openwrap/adunitconfig/common_test.go @@ -83,7 +83,7 @@ func TestSelectSlot(t *testing.T) { }, h: 300, w: 200, - tagid: "/15671365/Test_AdUnit12349", + tagid: "/15671365/Test_AdUnit92349", div: "Div1", source: "test.com", }, @@ -113,7 +113,7 @@ func TestSelectSlot(t *testing.T) { }, }, }, - slotName: "/15671365/Test_AdUnit12349", + slotName: "/15671365/Test_AdUnit92349", isRegex: true, matchedRegex: "^/15671365/test_adunit[0-9]*$", }, diff --git a/modules/pubmatic/openwrap/allprocessedbidresponsehook.go b/modules/pubmatic/openwrap/allprocessedbidresponsehook.go new file mode 100644 index 00000000000..6ebaed0ae85 --- /dev/null +++ b/modules/pubmatic/openwrap/allprocessedbidresponsehook.go @@ -0,0 +1,43 @@ +package openwrap + +import ( + "context" + + "github.com/prebid/prebid-server/exchange/entities" + "github.com/prebid/prebid-server/hooks/hookstage" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/utils" + "github.com/prebid/prebid-server/openrtb_ext" +) + +// handleAllProcessedBidResponsesHook will create unique id for each bid in bid Response. This hook is introduced +// because bidresponse should be updated in mutations and we need modified bidID at the start of auction response hook. +func (m OpenWrap) handleAllProcessedBidResponsesHook( + ctx context.Context, + moduleCtx hookstage.ModuleInvocationContext, + payload hookstage.AllProcessedBidResponsesPayload, +) (hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload], error) { + result := hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload]{ + ChangeSet: hookstage.ChangeSet[hookstage.AllProcessedBidResponsesPayload]{}, + } + + // 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 handleAllProcessedBidResponsesHook()") + return result, nil + } + + result.ChangeSet.AddMutation(func(apbrp hookstage.AllProcessedBidResponsesPayload) (hookstage.AllProcessedBidResponsesPayload, error) { + updateBidIds(apbrp.Responses) + return apbrp, nil + }, hookstage.MutationUpdate, "update-bid-id") + + return result, nil +} + +func updateBidIds(bidderResponses map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid) { + for _, seatBid := range bidderResponses { + for i := range seatBid.Bids { + seatBid.Bids[i].Bid.ID = utils.SetUniqueBidID(seatBid.Bids[i].Bid.ID, seatBid.Bids[i].GeneratedBidID) + } + } +} diff --git a/modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go b/modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go new file mode 100644 index 00000000000..102e722f4af --- /dev/null +++ b/modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go @@ -0,0 +1,69 @@ +package openwrap + +import ( + "testing" + + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/exchange/entities" + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestUpdateBidIds(t *testing.T) { + type args struct { + bidderResponses map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid + } + tests := []struct { + name string + args args + want map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid + }{ + { + name: "All bidIds are updated", + args: args{ + bidderResponses: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{ + "pubmatic": { + Bids: []*entities.PbsOrtbBid{ + { + Bid: &openrtb2.Bid{ + ID: "bid-1", + }, + GeneratedBidID: "gen-1", + }, + { + Bid: &openrtb2.Bid{ + ID: "bid-2", + }, + GeneratedBidID: "gen-2", + }, + }, + }, + }, + }, + want: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{ + "pubmatic": { + Bids: []*entities.PbsOrtbBid{ + { + Bid: &openrtb2.Bid{ + ID: "bid-1::gen-1", + }, + GeneratedBidID: "gen-1", + }, + { + Bid: &openrtb2.Bid{ + ID: "bid-2::gen-2", + }, + GeneratedBidID: "gen-2", + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + updateBidIds(tt.args.bidderResponses) + assert.Equal(t, tt.want, tt.args.bidderResponses, "Bid Id should be equal") + }) + } +} diff --git a/modules/pubmatic/openwrap/auctionresponsehook.go b/modules/pubmatic/openwrap/auctionresponsehook.go index 4d74ea8f812..0d4b2d72d16 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook.go +++ b/modules/pubmatic/openwrap/auctionresponsehook.go @@ -12,6 +12,7 @@ import ( "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adunitconfig" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/tracker" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/utils" "github.com/prebid/prebid-server/openrtb_ext" ) @@ -251,8 +252,9 @@ func (m OpenWrap) handleAuctionResponseHook( } ap.BidResponse, err = m.applyDefaultBids(rctx, ap.BidResponse) - ap.BidResponse.Ext = rctx.ResponseExt + + resetBidIdtoOriginal(ap.BidResponse) return ap, err }, hookstage.MutationUpdate, "response-body-with-sshb-format") @@ -345,3 +347,11 @@ func getPlatformName(platform string) string { } return platform } + +func resetBidIdtoOriginal(bidResponse *openrtb2.BidResponse) { + for i, seatBid := range bidResponse.SeatBid { + for j, bid := range seatBid.Bid { + bidResponse.SeatBid[i].Bid[j].ID = utils.GetOriginalBidId(bid.ID) + } + } +} diff --git a/modules/pubmatic/openwrap/auctionresponsehook_test.go b/modules/pubmatic/openwrap/auctionresponsehook_test.go index f32be0519e2..a5ec0cf9bae 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook_test.go +++ b/modules/pubmatic/openwrap/auctionresponsehook_test.go @@ -124,3 +124,72 @@ func TestSeatNonBidsInHandleAuctionResponseHook(t *testing.T) { }) } } + +func TestResetBidIdtoOriginal(t *testing.T) { + type args struct { + bidResponse *openrtb2.BidResponse + } + tests := []struct { + name string + args args + want *openrtb2.BidResponse + }{ + { + name: "Reset Bid Id to original", + args: args{ + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "original::generated", + }, + { + ID: "original-1::generated-1", + }, + }, + Seat: "pubmatic", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "original-2::generated-2", + }, + }, + Seat: "index", + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "original", + }, + { + ID: "original-1", + }, + }, + Seat: "pubmatic", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "original-2", + }, + }, + Seat: "index", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resetBidIdtoOriginal(tt.args.bidResponse) + assert.Equal(t, tt.want, tt.args.bidResponse, "Bid Id should reset to original") + }) + } +} diff --git a/modules/pubmatic/openwrap/models/constants.go b/modules/pubmatic/openwrap/models/constants.go index 4805bb015a1..42ad7d36661 100755 --- a/modules/pubmatic/openwrap/models/constants.go +++ b/modules/pubmatic/openwrap/models/constants.go @@ -396,7 +396,11 @@ const ( ContextOWLoggerKey contextKey = "owlogger" ) -const Pipe = "|" +const ( + Pipe = "|" + BidIdSeparator = "::" +) + const ( EndpointV25 = "v25" EndpointAMP = "amp" diff --git a/modules/pubmatic/openwrap/module.go b/modules/pubmatic/openwrap/module.go index e569ffb035a..4c2cfcbd9e4 100644 --- a/modules/pubmatic/openwrap/module.go +++ b/modules/pubmatic/openwrap/module.go @@ -53,6 +53,25 @@ func (m OpenWrap) HandleBeforeValidationHook( return m.handleBeforeValidationHook(ctx, miCtx, payload) } +func (m OpenWrap) HandleAllProcessedBidResponsesHook( + ctx context.Context, + miCtx hookstage.ModuleInvocationContext, + payload hookstage.AllProcessedBidResponsesPayload, +) (hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload], error) { + defer func() { + if r := recover(); r != nil { + m.metricEngine.RecordOpenWrapServerPanicStats(m.cfg.Server.HostName, "HandleAllProcessedBidResponsesHook") + request, err := json.Marshal(payload) + if err != nil { + glog.Error("request:" + string(request) + ". err: " + err.Error() + ". stacktrace:" + string(debug.Stack())) + } + glog.Error("request:" + string(request) + ". stacktrace:" + string(debug.Stack())) + } + }() + + return m.handleAllProcessedBidResponsesHook(ctx, miCtx, payload) +} + func (m OpenWrap) HandleAuctionResponseHook( ctx context.Context, miCtx hookstage.ModuleInvocationContext, diff --git a/modules/pubmatic/openwrap/utils/bid.go b/modules/pubmatic/openwrap/utils/bid.go new file mode 100644 index 00000000000..f1146d4b4ee --- /dev/null +++ b/modules/pubmatic/openwrap/utils/bid.go @@ -0,0 +1,17 @@ +package utils + +import ( + "regexp" + + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" +) + +var bidIDRegx = regexp.MustCompile("[" + models.BidIdSeparator + "]") + +func GetOriginalBidId(bidID string) string { + return bidIDRegx.Split(bidID, -1)[0] +} + +func SetUniqueBidID(originalBidID, generatedBidID string) string { + return originalBidID + models.BidIdSeparator + generatedBidID +} diff --git a/modules/pubmatic/openwrap/utils/bid_test.go b/modules/pubmatic/openwrap/utils/bid_test.go new file mode 100644 index 00000000000..5b78f0ec3c8 --- /dev/null +++ b/modules/pubmatic/openwrap/utils/bid_test.go @@ -0,0 +1,111 @@ +package utils + +import ( + "testing" +) + +func TestGetOriginalBidId(t *testing.T) { + type args struct { + bidId string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Split the bid Id when get valid bidId", + args: args{ + bidId: "original-id::gen-id", + }, + want: "original-id", + }, + { + name: "Empty BidId", + args: args{ + bidId: "", + }, + want: "", + }, + { + name: "Partial BidId", + args: args{ + bidId: "::gen-id", + }, + want: "", + }, + { + name: "Partial BidId without generated and separator", + args: args{ + bidId: "original-bid-1", + }, + want: "original-bid-1", + }, + { + name: "Partial BidId without generated", + args: args{ + bidId: "original-bid::", + }, + want: "original-bid", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetOriginalBidId(tt.args.bidId); got != tt.want { + t.Errorf("GetOriginalBidId() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSetUniqueBidID(t *testing.T) { + type args struct { + originalBidID string + generatedBidID string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Unique bid id will be generated", + args: args{ + originalBidID: "orig-bid", + generatedBidID: "gen-bid", + }, + want: "orig-bid::gen-bid", + }, + { + name: "Original Bid Id empty", + args: args{ + originalBidID: "", + generatedBidID: "gen-bid", + }, + want: "::gen-bid", + }, + { + name: "generated BidId empty", + args: args{ + originalBidID: "orig-bid", + generatedBidID: "", + }, + want: "orig-bid::", + }, + { + name: "Both Id empty", + args: args{ + originalBidID: "", + generatedBidID: "", + }, + want: "::", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := SetUniqueBidID(tt.args.originalBidID, tt.args.generatedBidID); got != tt.want { + t.Errorf("SetUniqueBidId() = %v, want %v", got, tt.want) + } + }) + } +} From c82ca4945a26961bbd0e50d4dca650203cf9e52c Mon Sep 17 00:00:00 2001 From: pm-avinash-kapre <112699665+AvinashKapre@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:19:39 +0530 Subject: [PATCH 06/30] make case insensitive comparison in auction request (#3113) (#614) Co-authored-by: Ashish Garg --- endpoints/openrtb2/auction.go | 21 +-- endpoints/openrtb2/auction_test.go | 78 ++++++++-- .../exemplary/all-ext-case-insensitive.json | 140 ++++++++++++++++++ .../valid-whole/exemplary/all-ext.json | 86 ++++++----- .../exemplary/simple-case-insensitive.json | 61 ++++++++ .../valid-whole/exemplary/simple.json | 40 ++--- exchange/exchange_test.go | 6 +- exchange/utils.go | 3 +- 8 files changed, 355 insertions(+), 80 deletions(-) create mode 100644 endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext-case-insensitive.json create mode 100644 endpoints/openrtb2/sample-requests/valid-whole/exemplary/simple-case-insensitive.json diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 5055dd40029..aaa8c64a011 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -1524,12 +1524,12 @@ func (deps *endpointDeps) validateImpExt(imp *openrtb_ext.ImpWrapper, aliases ma errL := []error{} for bidder, ext := range prebid.Bidder { - coreBidder := bidder + coreBidder, _ := openrtb_ext.NormalizeBidderName(bidder) if tmp, isAlias := aliases[bidder]; isAlias { - coreBidder = tmp + coreBidder = openrtb_ext.BidderName(tmp) } - if coreBidderNormalized, isValid := deps.bidderMap[coreBidder]; isValid { + if coreBidderNormalized, isValid := deps.bidderMap[coreBidder.String()]; isValid { if err := deps.paramsValidator.Validate(coreBidderNormalized, ext); err != nil { msg := fmt.Sprintf("request.imp[%d].ext.prebid.bidder.%s failed validation.\n%v", impIndex, bidder, err) @@ -1593,18 +1593,21 @@ func (deps *endpointDeps) parseBidExt(req *openrtb_ext.RequestWrapper) error { } func (deps *endpointDeps) validateAliases(aliases map[string]string) error { - for alias, coreBidder := range aliases { - if _, isCoreBidderDisabled := deps.disabledBidders[coreBidder]; isCoreBidderDisabled { - return fmt.Errorf("request.ext.prebid.aliases.%s refers to disabled bidder: %s", alias, coreBidder) + for alias, bidderName := range aliases { + normalisedBidderName, _ := openrtb_ext.NormalizeBidderName(bidderName) + coreBidderName := normalisedBidderName.String() + if _, isCoreBidderDisabled := deps.disabledBidders[coreBidderName]; isCoreBidderDisabled { + return fmt.Errorf("request.ext.prebid.aliases.%s refers to disabled bidder: %s", alias, bidderName) } - if _, isCoreBidder := deps.bidderMap[coreBidder]; !isCoreBidder { - return fmt.Errorf("request.ext.prebid.aliases.%s refers to unknown bidder: %s", alias, coreBidder) + if _, isCoreBidder := deps.bidderMap[coreBidderName]; !isCoreBidder { + return fmt.Errorf("request.ext.prebid.aliases.%s refers to unknown bidder: %s", alias, bidderName) } - if alias == coreBidder { + if alias == coreBidderName { return fmt.Errorf("request.ext.prebid.aliases.%s defines a no-op alias. Choose a different alias, or remove this entry.", alias) } + aliases[alias] = coreBidderName } return nil } diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 350b3057185..6206ac02ad4 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -2833,19 +2833,21 @@ func TestValidateImpExt(t *testing.T) { for _, group := range testGroups { for _, test := range group.testCases { - imp := &openrtb2.Imp{Ext: test.impExt} - impWrapper := &openrtb_ext.ImpWrapper{Imp: imp} + t.Run(test.description, func(t *testing.T) { + imp := &openrtb2.Imp{Ext: test.impExt} + impWrapper := &openrtb_ext.ImpWrapper{Imp: imp} - errs := deps.validateImpExt(impWrapper, nil, 0, false, nil) + errs := deps.validateImpExt(impWrapper, nil, 0, false, nil) - assert.NoError(t, impWrapper.RebuildImpressionExt(), test.description+":rebuild_imp") + assert.NoError(t, impWrapper.RebuildImpressionExt(), test.description+":rebuild_imp") - if len(test.expectedImpExt) > 0 { - assert.JSONEq(t, test.expectedImpExt, string(imp.Ext), "imp.ext JSON does not match expected. Test: %s. %s\n", group.description, test.description) - } else { - assert.Empty(t, imp.Ext, "imp.ext expected to be empty but was: %s. Test: %s. %s\n", string(imp.Ext), group.description, test.description) - } - assert.ElementsMatch(t, test.expectedErrs, errs, "errs slice does not match expected. Test: %s. %s\n", group.description, test.description) + if len(test.expectedImpExt) > 0 { + assert.JSONEq(t, test.expectedImpExt, string(imp.Ext), "imp.ext JSON does not match expected. Test: %s. %s\n", group.description, test.description) + } else { + assert.Empty(t, imp.Ext, "imp.ext expected to be empty but was: %s. Test: %s. %s\n", string(imp.Ext), group.description, test.description) + } + assert.Equal(t, test.expectedErrs, errs, "errs slice does not match expected. Test: %s. %s\n", group.description, test.description) + }) } } } @@ -5771,3 +5773,59 @@ func TestSetSeatNonBidRaw(t *testing.T) { }) } } + +func TestValidateAliases(t *testing.T) { + deps := &endpointDeps{ + disabledBidders: map[string]string{"rubicon": "rubicon"}, + bidderMap: map[string]openrtb_ext.BidderName{"appnexus": openrtb_ext.BidderName("appnexus")}, + } + + testCases := []struct { + description string + aliases map[string]string + expectedAliases map[string]string + expectedError error + }{ + { + description: "valid case", + aliases: map[string]string{"test": "appnexus"}, + expectedAliases: map[string]string{"test": "appnexus"}, + expectedError: nil, + }, + { + description: "valid case - case insensitive", + aliases: map[string]string{"test": "Appnexus"}, + expectedAliases: map[string]string{"test": "appnexus"}, + expectedError: nil, + }, + { + description: "disabled bidder", + aliases: map[string]string{"test": "rubicon"}, + expectedAliases: nil, + expectedError: errors.New("request.ext.prebid.aliases.test refers to disabled bidder: rubicon"), + }, + { + description: "coreBidderName not found", + aliases: map[string]string{"test": "anyBidder"}, + expectedAliases: nil, + expectedError: errors.New("request.ext.prebid.aliases.test refers to unknown bidder: anyBidder"), + }, + { + description: "alias name is coreBidder name", + aliases: map[string]string{"appnexus": "appnexus"}, + expectedAliases: nil, + expectedError: errors.New("request.ext.prebid.aliases.appnexus defines a no-op alias. Choose a different alias, or remove this entry."), + }, + } + + for _, testCase := range testCases { + t.Run(testCase.description, func(t *testing.T) { + err := deps.validateAliases(testCase.aliases) + if err != nil { + assert.Equal(t, testCase.expectedError, err) + } else { + assert.ObjectsAreEqualValues(testCase.expectedAliases, map[string]string{"test": "appnexus"}) + } + }) + } +} diff --git a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext-case-insensitive.json b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext-case-insensitive.json new file mode 100644 index 00000000000..20997076af2 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext-case-insensitive.json @@ -0,0 +1,140 @@ +{ + "description": "This demonstrates all extension in case insensitive.", + "config": { + "mockBidders": [ + { + "bidderName": "appnexus", + "currency": "USD", + "price": 1.00 + }, + { + "bidderName": "rubicon", + "currency": "USD", + "price": 1.00 + } + ] + }, + "mockBidRequest": { + "id": "some-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "ext": { + "consent": "gdpr-consent-string", + "prebid": { + "buyeruids": { + "appnexus": "override-appnexus-id-in-cookie" + } + } + } + }, + "regs": { + "ext": { + "gdpr": 1, + "us_privacy": "1NYN" + } + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + }, + "districtm": { + "placementId": 105 + }, + "rubicon": { + "accountId": 1001, + "siteId": 113932, + "zoneId": 535510 + } + } + } + ], + "tmax": 500, + "ext": { + "prebid": { + "aliases": { + "districtm": "Appnexus" + }, + "bidadjustmentfactors": { + "appnexus": 1.01, + "districtm": 0.98, + "rubicon": 0.99 + }, + "cache": { + "bids": {} + }, + "channel": { + "name": "video", + "version": "1.0" + }, + "targeting": { + "includewinners": false, + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.10 + } + ] + } + } + } + } + }, + "expectedBidResponse": { + "id": "some-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "appnexus-bid", + "impid": "some-impression-id", + "price": 1.01 + } + ], + "seat": "appnexus" + }, + { + "bid": [ + { + "id": "appnexus-bid", + "impid": "some-impression-id", + "price": 0.98 + } + ], + "seat": "districtm" + }, + { + "bid": [ + { + "id": "rubicon-bid", + "impid": "some-impression-id", + "price": 0.99 + } + ], + "seat": "rubicon" + } + ], + "bidid": "test bid id", + "cur": "USD", + "nbr": 0 + }, + "expectedReturnCode": 200 +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext.json b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext.json index 8f64fd2a5fd..02dc6160d49 100644 --- a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext.json +++ b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext.json @@ -2,8 +2,16 @@ "description": "This demonstrates all of the OpenRTB extensions supported by Prebid Server. Very few requests will need all of these at once.", "config": { "mockBidders": [ - {"bidderName": "appnexus", "currency": "USD", "price": 1.00}, - {"bidderName": "rubicon", "currency": "USD", "price": 1.00} + { + "bidderName": "appnexus", + "currency": "USD", + "price": 1.00 + }, + { + "bidderName": "rubicon", + "currency": "USD", + "price": 1.00 + } ] }, "mockBidRequest": { @@ -91,42 +99,42 @@ } }, "expectedBidResponse": { - "id":"some-request-id", - "seatbid": [ - { - "bid": [ - { - "id": "appnexus-bid", - "impid": "some-impression-id", - "price": 1.01 - } - ], - "seat": "appnexus" - }, - { - "bid": [ - { - "id": "appnexus-bid", - "impid": "some-impression-id", - "price": 0.98 - } - ], - "seat": "districtm" - }, - { - "bid": [ - { - "id": "rubicon-bid", - "impid": "some-impression-id", - "price": 0.99 - } - ], - "seat": "rubicon" - } - ], - "bidid":"test bid id", - "cur":"USD", - "nbr":0 + "id": "some-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "appnexus-bid", + "impid": "some-impression-id", + "price": 1.01 + } + ], + "seat": "appnexus" + }, + { + "bid": [ + { + "id": "appnexus-bid", + "impid": "some-impression-id", + "price": 0.98 + } + ], + "seat": "districtm" + }, + { + "bid": [ + { + "id": "rubicon-bid", + "impid": "some-impression-id", + "price": 0.99 + } + ], + "seat": "rubicon" + } + ], + "bidid": "test bid id", + "cur": "USD", + "nbr": 0 }, "expectedReturnCode": 200 -} +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/simple-case-insensitive.json b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/simple-case-insensitive.json new file mode 100644 index 00000000000..9b66a59d0a5 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/simple-case-insensitive.json @@ -0,0 +1,61 @@ +{ + "description": "Simple request - bidder case insensitive", + "config": { + "mockBidders": [ + { + "bidderName": "appnexus", + "currency": "USD", + "price": 0.00 + } + ] + }, + "mockBidRequest": { + "id": "some-request-id", + "site": { + "page": "prebid.org" + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "Appnexus": { + "placementId": 12883451 + } + } + } + ], + "tmax": 500, + "ext": {} + }, + "expectedBidResponse": { + "id": "some-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "appnexus-bid", + "impid": "some-impression-id", + "price": 0 + } + ], + "seat": "Appnexus" + } + ], + "bidid": "test bid id", + "cur": "USD", + "nbr": 0 + }, + "expectedReturnCode": 200 +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/simple.json b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/simple.json index ba9079d4675..5a7dbd20747 100644 --- a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/simple.json +++ b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/simple.json @@ -2,7 +2,11 @@ "description": "Simple request", "config": { "mockBidders": [ - {"bidderName": "appnexus", "currency": "USD", "price": 0.00} + { + "bidderName": "appnexus", + "currency": "USD", + "price": 0.00 + } ] }, "mockBidRequest": { @@ -36,22 +40,22 @@ "ext": {} }, "expectedBidResponse": { - "id":"some-request-id", - "seatbid": [ - { - "bid": [ - { - "id": "appnexus-bid", - "impid": "some-impression-id", - "price": 0 - } - ], - "seat": "appnexus" - } - ], - "bidid":"test bid id", - "cur":"USD", - "nbr":0 + "id": "some-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "appnexus-bid", + "impid": "some-impression-id", + "price": 0 + } + ], + "seat": "appnexus" + } + ], + "bidid": "test bid id", + "cur": "USD", + "nbr": 0 }, "expectedReturnCode": 200 -} +} \ No newline at end of file diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index fa9c2770f55..4dee6bbbfc5 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -772,7 +772,7 @@ func TestAdapterCurrency(t *testing.T) { categoriesFetcher: nilCategoryFetcher{}, bidIDGenerator: &mockBidIDGenerator{false, false}, adapterMap: map[openrtb_ext.BidderName]AdaptedBidder{ - openrtb_ext.BidderName("foo"): AdaptBidder(mockBidder, nil, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderName("foo"), nil, ""), + openrtb_ext.BidderName("appnexus"): AdaptBidder(mockBidder, nil, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderName("appnexus"), nil, ""), }, } e.requestSplitter = requestSplitter{ @@ -786,7 +786,7 @@ func TestAdapterCurrency(t *testing.T) { Imp: []openrtb2.Imp{{ ID: "some-impression-id", Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}, {W: 300, H: 600}}}, - Ext: json.RawMessage(`{"prebid":{"bidder":{"foo":{"placementId":1}}}}`), + Ext: json.RawMessage(`{"prebid":{"bidder":{"appnexus":{"placementId":1}}}}`), }}, Site: &openrtb2.Site{ Page: "prebid.org", @@ -809,7 +809,7 @@ func TestAdapterCurrency(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "some-request-id", response.ID, "Response ID") assert.Empty(t, response.SeatBid, "Response Bids") - assert.Contains(t, string(response.Ext), `"errors":{"foo":[{"code":5,"message":"The adapter failed to generate any bid requests, but also failed to generate an error explaining why"}]}`, "Response Ext") + assert.Contains(t, string(response.Ext), `"errors":{"appnexus":[{"code":5,"message":"The adapter failed to generate any bid requests, but also failed to generate an error explaining why"}]}`, "Response Ext") // Test Currency Converter Properly Passed To Adapter if assert.NotNil(t, mockBidder.lastExtraRequestInfo, "Currency Conversion Argument") { diff --git a/exchange/utils.go b/exchange/utils.go index 7cb71e69e58..1df406528ce 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -769,10 +769,11 @@ func setUserExtWithCopy(request *openrtb2.BidRequest, userExtJSON json.RawMessag // resolveBidder returns the known BidderName associated with bidder, if bidder is an alias. If it's not an alias, the bidder is returned. func resolveBidder(bidder string, aliases map[string]string) openrtb_ext.BidderName { + normalisedBidderName, _ := openrtb_ext.NormalizeBidderName(bidder) if coreBidder, ok := aliases[bidder]; ok { return openrtb_ext.BidderName(coreBidder) } - return openrtb_ext.BidderName(bidder) + return normalisedBidderName } // parseAliases parses the aliases from the BidRequest From a5dbd5efb8f7214db1f69d4c4120c0ec8c5a3c79 Mon Sep 17 00:00:00 2001 From: Nikhil Vaidya <102963966+pm-nikhil-vaidya@users.noreply.github.com> Date: Mon, 30 Oct 2023 14:36:08 +0530 Subject: [PATCH 07/30] OTT-1402: refactor apply OpenWrap targeting (#617) --- modules/pubmatic/openwrap/targeting.go | 50 ++++++++++++--------- modules/pubmatic/openwrap/tracker/create.go | 5 ++- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/modules/pubmatic/openwrap/targeting.go b/modules/pubmatic/openwrap/targeting.go index c749c6c53e3..60558e708a3 100644 --- a/modules/pubmatic/openwrap/targeting.go +++ b/modules/pubmatic/openwrap/targeting.go @@ -7,6 +7,7 @@ import ( "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/fullscreenclickability" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/utils" "github.com/prebid/prebid-server/openrtb_ext" ) @@ -27,11 +28,31 @@ func allowTargetingKey(key string) bool { return strings.HasPrefix(key, models.HbBuyIdPrefix) } -func addPWTTargetingForBid(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) (droppedBids map[string][]openrtb2.Bid, warnings []string) { - if rctx.Platform != models.PLATFORM_APP { - return +func addInAppTargettingKeys(targeting map[string]string, seat string, ecpm float64, bid *openrtb2.Bid, isWinningBid bool) { + if isWinningBid { + targeting[models.CreatePartnerKey(seat, models.PWT_SLOTID)] = utils.GetOriginalBidId(bid.ID) + targeting[models.CreatePartnerKey(seat, models.PWT_SZ)] = models.GetSize(bid.W, bid.H) + targeting[models.CreatePartnerKey(seat, models.PWT_PARTNERID)] = seat + targeting[models.CreatePartnerKey(seat, models.PWT_ECPM)] = fmt.Sprintf("%.2f", ecpm) + targeting[models.CreatePartnerKey(seat, models.PWT_PLATFORM)] = getPlatformName(models.PLATFORM_APP) + targeting[models.CreatePartnerKey(seat, models.PWT_BIDSTATUS)] = "1" + if len(bid.DealID) != 0 { + targeting[models.CreatePartnerKey(seat, models.PWT_DEALID)] = bid.DealID + } } + targeting[models.PWT_SLOTID] = utils.GetOriginalBidId(bid.ID) + targeting[models.PWT_BIDSTATUS] = "1" + targeting[models.PWT_SZ] = models.GetSize(bid.W, bid.H) + targeting[models.PWT_PARTNERID] = seat + targeting[models.PWT_ECPM] = fmt.Sprintf("%.2f", ecpm) + targeting[models.PWT_PLATFORM] = getPlatformName(models.PLATFORM_APP) + if len(bid.DealID) != 0 { + targeting[models.PWT_DEALID] = bid.DealID + } +} + +func addPWTTargetingForBid(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) (droppedBids map[string][]openrtb2.Bid, warnings []string) { if !rctx.SendAllBids { droppedBids = make(map[string][]openrtb2.Bid) } @@ -70,16 +91,10 @@ func addPWTTargetingForBid(rctx models.RequestCtx, bidResponse *openrtb2.BidResp delete(bidCtx.Prebid.Targeting, key) } - bidCtx.Prebid.Targeting = newTargeting - bidCtx.Prebid.Targeting[models.CreatePartnerKey(seatBid.Seat, models.PWT_SLOTID)] = bid.ID - bidCtx.Prebid.Targeting[models.CreatePartnerKey(seatBid.Seat, models.PWT_SZ)] = models.GetSize(bid.W, bid.H) - bidCtx.Prebid.Targeting[models.CreatePartnerKey(seatBid.Seat, models.PWT_PARTNERID)] = seatBid.Seat - bidCtx.Prebid.Targeting[models.CreatePartnerKey(seatBid.Seat, models.PWT_ECPM)] = fmt.Sprintf("%.2f", bidCtx.NetECPM) - bidCtx.Prebid.Targeting[models.CreatePartnerKey(seatBid.Seat, models.PWT_PLATFORM)] = getPlatformName(rctx.Platform) - bidCtx.Prebid.Targeting[models.CreatePartnerKey(seatBid.Seat, models.PWT_BIDSTATUS)] = "1" - if len(bid.DealID) != 0 { - bidCtx.Prebid.Targeting[models.CreatePartnerKey(seatBid.Seat, models.PWT_DEALID)] = bid.DealID + if rctx.Platform == models.PLATFORM_APP { + addInAppTargettingKeys(newTargeting, seatBid.Seat, bidCtx.NetECPM, &bid, isWinningBid) } + bidCtx.Prebid.Targeting = newTargeting if isWinningBid { if rctx.SendAllBids { @@ -88,17 +103,8 @@ func addPWTTargetingForBid(rctx models.RequestCtx, bidResponse *openrtb2.BidResp if fullscreenclickability.IsFscApplicable(rctx.PubID, seatBid.Seat, bidCtx.DspId) { bidCtx.Fsc = 1 } - bidCtx.Prebid.Targeting[models.PWT_SLOTID] = bid.ID - bidCtx.Prebid.Targeting[models.PWT_BIDSTATUS] = "1" - bidCtx.Prebid.Targeting[models.PWT_SZ] = models.GetSize(bid.W, bid.H) - bidCtx.Prebid.Targeting[models.PWT_PARTNERID] = seatBid.Seat - bidCtx.Prebid.Targeting[models.PWT_ECPM] = fmt.Sprintf("%.2f", bidCtx.NetECPM) - bidCtx.Prebid.Targeting[models.PWT_PLATFORM] = getPlatformName(rctx.Platform) - if len(bid.DealID) != 0 { - bidCtx.Prebid.Targeting[models.PWT_DEALID] = bid.DealID - } } else if !rctx.SendAllBids { - warnings = append(warnings, "dropping bid "+bid.ID+" as sendAllBids is disabled") + warnings = append(warnings, "dropping bid "+utils.GetOriginalBidId(bid.ID)+" as sendAllBids is disabled") } // cache for bid details for logger and tracker diff --git a/modules/pubmatic/openwrap/tracker/create.go b/modules/pubmatic/openwrap/tracker/create.go index 35303c3a1e3..c20eeb70b49 100644 --- a/modules/pubmatic/openwrap/tracker/create.go +++ b/modules/pubmatic/openwrap/tracker/create.go @@ -9,6 +9,7 @@ import ( "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/bidderparams" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/utils" ) func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) map[string]models.OWTracker { @@ -132,8 +133,8 @@ func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) m tracker.PartnerInfo = models.Partner{ PartnerID: partnerID, BidderCode: seatBid.Seat, - BidID: bid.ID, - OrigBidID: bid.ID, + BidID: utils.GetOriginalBidId(bid.ID), + OrigBidID: utils.GetOriginalBidId(bid.ID), KGPV: kgpv, NetECPM: float64(netECPM), GrossECPM: models.GetGrossEcpm(price), From 9b80acba1e2883777c05165256ff8bc8054b9664 Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Mon, 30 Oct 2023 15:30:31 +0530 Subject: [PATCH 08/30] UOE-9673: OpenWrap module minor improvements (#579) --- .../openwrap/adunitconfig/common_test.go | 9 ++++- .../pubmatic/openwrap/adunitconfig/device.go | 12 +++--- .../pubmatic/openwrap/beforevalidationhook.go | 37 ++++++++++++++---- .../pubmatic/openwrap/bidderparams/vast.go | 20 +++------- .../cache/gocache/adunit_config_test.go | 18 ++++----- .../gocache/fullscreenclickability_test.go | 12 +++--- .../openwrap/cache/gocache/gocache.go | 4 +- .../openwrap/cache/gocache/gocache_test.go | 12 +++--- .../cache/gocache/partner_config_test.go | 20 +++++----- .../cache/gocache/slot_mappings_test.go | 30 +++++++-------- .../openwrap/cache/gocache/sync_test.go | 4 +- .../openwrap/cache/gocache/vast_tags_test.go | 14 +++---- modules/pubmatic/openwrap/config/config.go | 4 +- modules/pubmatic/openwrap/entrypointhook.go | 28 +++++++++++--- modules/pubmatic/openwrap/models/amp.go | 19 ++++++++++ modules/pubmatic/openwrap/models/constants.go | 1 + modules/pubmatic/openwrap/models/openwrap.go | 7 ++++ modules/pubmatic/openwrap/openwrap.go | 11 ++++-- modules/pubmatic/openwrap/openwrap_sshb.go | 38 +++++++++++++++++++ 19 files changed, 204 insertions(+), 96 deletions(-) create mode 100644 modules/pubmatic/openwrap/models/amp.go create mode 100644 modules/pubmatic/openwrap/openwrap_sshb.go diff --git a/modules/pubmatic/openwrap/adunitconfig/common_test.go b/modules/pubmatic/openwrap/adunitconfig/common_test.go index e6b0f0648ce..db2c242dc02 100644 --- a/modules/pubmatic/openwrap/adunitconfig/common_test.go +++ b/modules/pubmatic/openwrap/adunitconfig/common_test.go @@ -79,7 +79,14 @@ func TestSelectSlot(t *testing.T) { name: "Matching_Slot_config_when_regex_is_present_and_slotconfig_is_absent", args: args{ rCtx: models.RequestCtx{ - AdUnitConfig: getAdunitConfigWithRx(), + AdUnitConfig: func() *adunitconfig.AdUnitConfig { + auc := getAdunitConfigWithRx() + + // Temporary fix to make UT execution consistent. + // TODO: make getRegexMatch()'s loop consistent. + delete(auc.Config, "/15671365/test_adunit1") + return auc + }(), }, h: 300, w: 200, diff --git a/modules/pubmatic/openwrap/adunitconfig/device.go b/modules/pubmatic/openwrap/adunitconfig/device.go index 57b3517a5cb..b52644afd1f 100644 --- a/modules/pubmatic/openwrap/adunitconfig/device.go +++ b/modules/pubmatic/openwrap/adunitconfig/device.go @@ -7,8 +7,10 @@ import ( "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" ) -func ReplaceDeviceTypeFromAdUnitConfig(rCtx models.RequestCtx, device *openrtb2.Device) { - if device != nil || device.DeviceType != 0 { +func ReplaceDeviceTypeFromAdUnitConfig(rCtx models.RequestCtx, device **openrtb2.Device) { + if *device == nil { + *device = &openrtb2.Device{} + } else if (*device).DeviceType != 0 { return } @@ -28,9 +30,5 @@ func ReplaceDeviceTypeFromAdUnitConfig(rCtx models.RequestCtx, device *openrtb2. return } - if device == nil { - device = &openrtb2.Device{} - } - - device.DeviceType = adcom1.DeviceType(adUnitCfg.Device.DeviceType) + (*device).DeviceType = adcom1.DeviceType(adUnitCfg.Device.DeviceType) } diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go index c8624df4916..37ee37eca71 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook.go +++ b/modules/pubmatic/openwrap/beforevalidationhook.go @@ -8,6 +8,7 @@ import ( "net/url" "strconv" + "github.com/buger/jsonparser" "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/hooks/hookstage" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" @@ -142,12 +143,15 @@ func (m OpenWrap) handleBeforeValidationHook( IncludeWinners: boolutil.BoolPtr(true), } + isAdPodRequest := false disabledSlots := 0 serviceSideBidderPresent := false aliasgvlids := make(map[string]uint16) for i := 0; i < len(payload.BidRequest.Imp); i++ { + slotType := "banner" var adpodExt *models.AdPod + var isAdPodImpression bool imp := payload.BidRequest.Imp[i] if imp.TagID == "" { @@ -158,6 +162,8 @@ func (m OpenWrap) handleBeforeValidationHook( } if imp.Video != nil { + slotType = "video" + //add stats for video instl impressions if imp.Instl == 1 { m.metricEngine.RecordVideoInstlImpsStats(rCtx.PubIDStr, rCtx.ProfileIDStr) @@ -166,6 +172,16 @@ func (m OpenWrap) handleBeforeValidationHook( // provide custom macros for video event trackers requestExt.Prebid.Macros = getVASTEventMacros(rCtx) } + + if rCtx.IsCTVRequest && imp.Video.Ext != nil { + if _, _, _, err := jsonparser.Get(imp.Video.Ext, "adpod"); err == nil { + isAdPodImpression = true + if !isAdPodRequest { + isAdPodRequest = true + rCtx.MetricsEngine.RecordCTVReqCountWithAdPod(rCtx.PubIDStr, rCtx.ProfileIDStr) + } + } + } } impExt := &models.ImpExtension{} @@ -210,11 +226,6 @@ func (m OpenWrap) handleBeforeValidationHook( continue } - slotType := "banner" - if imp.Video != nil { - slotType = "video" - } - bidderMeta := make(map[string]models.PartnerData) nonMapped := make(map[string]struct{}) for _, partnerConfig := range rCtx.PartnerConfigMap { @@ -246,11 +257,12 @@ func (m OpenWrap) handleBeforeValidationHook( var isRegex bool var slot, kgpv string var bidderParams json.RawMessage + var matchedSlotKeysVAST []string switch prebidBidderCode { case string(openrtb_ext.BidderPubmatic), models.BidderPubMaticSecondaryAlias: slot, kgpv, isRegex, bidderParams, err = bidderparams.PreparePubMaticParamsV25(rCtx, m.cache, *payload.BidRequest, imp, *impExt, partnerID) case models.BidderVASTBidder: - slot, bidderParams, err = bidderparams.PrepareVASTBidderParams(rCtx, m.cache, *payload.BidRequest, imp, *impExt, partnerID, adpodExt) + slot, bidderParams, matchedSlotKeysVAST, err = bidderparams.PrepareVASTBidderParams(rCtx, m.cache, *payload.BidRequest, imp, *impExt, partnerID, adpodExt) default: slot, kgpv, isRegex, bidderParams, err = bidderparams.PrepareAdapterParamsV25(rCtx, m.cache, *payload.BidRequest, imp, *impExt, partnerID) } @@ -274,6 +286,10 @@ func (m OpenWrap) handleBeforeValidationHook( IsRegex: isRegex, // regex pattern } + for _, bidder := range matchedSlotKeysVAST { + bidderMeta[bidder].VASTTagFlags[bidder] = false + } + if alias, ok := partnerConfig[models.IsAlias]; ok && alias == "1" { if prebidPartnerName, ok := partnerConfig[models.PREBID_PARTNER_NAME]; ok { rCtx.Aliases[bidderCode] = adapters.ResolveOWBidder(prebidPartnerName) @@ -316,19 +332,26 @@ func (m OpenWrap) handleBeforeValidationHook( // cache the details for further processing if _, ok := rCtx.ImpBidCtx[imp.ID]; !ok { rCtx.ImpBidCtx[imp.ID] = models.ImpCtx{ + ImpID: imp.ID, TagID: imp.TagID, Div: div, IsRewardInventory: reward, Type: slotType, Banner: imp.Banner != nil, Video: imp.Video, + Native: imp.Native, IncomingSlots: incomingSlots, Bidders: make(map[string]models.PartnerData), BidCtx: make(map[string]models.BidCtx), NewExt: json.RawMessage(newImpExt), + IsAdPodRequest: isAdPodRequest, } } + if isAdPodImpression { + bidderMeta[string(openrtb_ext.BidderOWPrebidCTV)] = models.PartnerData{} + } + impCtx := rCtx.ImpBidCtx[imp.ID] impCtx.Bidders = bidderMeta impCtx.NonMapped = nonMapped @@ -448,7 +471,7 @@ func (m *OpenWrap) applyProfileChanges(rctx models.RequestCtx, bidRequest *openr } adunitconfig.ReplaceAppObjectFromAdUnitConfig(rctx, bidRequest.App) - adunitconfig.ReplaceDeviceTypeFromAdUnitConfig(rctx, bidRequest.Device) + adunitconfig.ReplaceDeviceTypeFromAdUnitConfig(rctx, &bidRequest.Device) bidRequest.Device.IP = rctx.IP bidRequest.Device.Language = getValidLanguage(bidRequest.Device.Language) diff --git a/modules/pubmatic/openwrap/bidderparams/vast.go b/modules/pubmatic/openwrap/bidderparams/vast.go index 33bfa4d8c2d..0cb6225e30c 100644 --- a/modules/pubmatic/openwrap/bidderparams/vast.go +++ b/modules/pubmatic/openwrap/bidderparams/vast.go @@ -13,34 +13,26 @@ import ( "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" ) -func PrepareVASTBidderParams(rctx models.RequestCtx, cache cache.Cache, bidRequest openrtb2.BidRequest, imp openrtb2.Imp, impExt models.ImpExtension, partnerID int, adpodExt *models.AdPod) (string, json.RawMessage, error) { +func PrepareVASTBidderParams(rctx models.RequestCtx, cache cache.Cache, bidRequest openrtb2.BidRequest, imp openrtb2.Imp, impExt models.ImpExtension, partnerID int, adpodExt *models.AdPod) (string, json.RawMessage, []string, error) { if imp.Video == nil { - return "", nil, nil + return "", nil, nil, nil } slots, slotMap, _, _ := getSlotMeta(rctx, cache, bidRequest, imp, impExt, partnerID) if len(slots) == 0 { - return "", nil, nil + return "", nil, nil, nil } pubVASTTags := cache.GetPublisherVASTTagsFromCache(rctx.PubID) if len(pubVASTTags) == 0 { - return "", nil, nil + return "", nil, nil, nil } matchedSlotKeys, err := getVASTBidderSlotKeys(&imp, slots[0], slotMap, pubVASTTags, adpodExt) if len(matchedSlotKeys) == 0 { - return "", nil, err + return "", nil, nil, err } - // NYC_TODO: - //setting flagmap - // bidderWrapper := &BidderWrapper{VASTagFlags: make(map[string]bool)} - // for _, key := range matchedSlotKeys { - // bidderWrapper.VASTagFlags[key] = false - // } - // impWrapper.Bidder[bidderCode] = bidderWrapper - bidParams := adapters.PrepareVASTBidderParamJSON(&bidRequest, &imp, pubVASTTags, matchedSlotKeys, slotMap, adpodExt) /* @@ -50,7 +42,7 @@ func PrepareVASTBidderParams(rctx models.RequestCtx, cache cache.Cache, bidReque //slotMap:map[/15671365/DMDemo1@com.pubmatic.openbid.app@101:map[param1:6005 param2:test param3:example]] //Ext:{"tags":[{"tagid":"101","url":"sample_url_1","dur":15,"price":"15","params":{"param1":"6005","param2":"test","param3":"example"}}]} */ - return slots[0], bidParams, nil + return slots[0], bidParams, matchedSlotKeys, nil } // getVASTBidderSlotKeys returns all slot keys which are matching to vast tag slot key diff --git a/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go b/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go index 1fba4efbf99..b5cc97656d5 100644 --- a/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go @@ -152,7 +152,7 @@ func Test_cache_populateCacheWithAdunitConfig(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -177,7 +177,7 @@ func Test_cache_populateCacheWithAdunitConfig(t *testing.T) { fields: fields{ cache: gocache.New(10, 10), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -200,7 +200,7 @@ func Test_cache_populateCacheWithAdunitConfig(t *testing.T) { fields: fields{ cache: gocache.New(10, 10), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -223,7 +223,7 @@ func Test_cache_populateCacheWithAdunitConfig(t *testing.T) { fields: fields{ cache: gocache.New(10, 10), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -277,7 +277,7 @@ func Test_cache_GetAdunitConfigFromCache(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -300,7 +300,7 @@ func Test_cache_GetAdunitConfigFromCache(t *testing.T) { name: "test_request", fields: fields{ db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -321,7 +321,7 @@ func Test_cache_GetAdunitConfigFromCache(t *testing.T) { name: "successfully_get_value_from_cache", fields: fields{ db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -345,7 +345,7 @@ func Test_cache_GetAdunitConfigFromCache(t *testing.T) { name: "got_empty_adunitconfig_from_cache", fields: fields{ db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -370,7 +370,7 @@ func Test_cache_GetAdunitConfigFromCache(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, diff --git a/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go b/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go index 2ad4f577830..d0efe3fdb1c 100644 --- a/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go @@ -24,7 +24,7 @@ func TestGetFSCDisabledPublishers(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } tests := []struct { @@ -49,7 +49,7 @@ func TestGetFSCDisabledPublishers(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -65,7 +65,7 @@ func TestGetFSCDisabledPublishers(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -104,7 +104,7 @@ func TestGetFSCThresholdPerDSP(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } tests := []struct { @@ -129,7 +129,7 @@ func TestGetFSCThresholdPerDSP(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -145,7 +145,7 @@ func TestGetFSCThresholdPerDSP(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, diff --git a/modules/pubmatic/openwrap/cache/gocache/gocache.go b/modules/pubmatic/openwrap/cache/gocache/gocache.go index 05f4522b5c9..8952408c09b 100644 --- a/modules/pubmatic/openwrap/cache/gocache/gocache.go +++ b/modules/pubmatic/openwrap/cache/gocache/gocache.go @@ -32,7 +32,7 @@ func key(format string, v ...interface{}) string { type cache struct { sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database metricEngine metrics.MetricsEngine } @@ -40,7 +40,7 @@ type cache struct { var c *cache var cOnce sync.Once -func New(goCache *gocache.Cache, database database.Database, cfg config.Cache, metricEngine metrics.MetricsEngine) *cache { +func New(goCache *gocache.Cache, database database.Database, cfg config.DBCache, metricEngine metrics.MetricsEngine) *cache { cOnce.Do( func() { c = &cache{ diff --git a/modules/pubmatic/openwrap/cache/gocache/gocache_test.go b/modules/pubmatic/openwrap/cache/gocache/gocache_test.go index 02a5c09b22c..c86d6755efb 100644 --- a/modules/pubmatic/openwrap/cache/gocache/gocache_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/gocache_test.go @@ -52,7 +52,7 @@ func TestNew(t *testing.T) { type args struct { goCache *gocache.Cache database database.Database - cfg config.Cache + cfg config.DBCache metricsEngine metrics.MetricsEngine } tests := []struct { @@ -64,7 +64,7 @@ func TestNew(t *testing.T) { args: args{ goCache: gocache.New(100, 100), database: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, metricsEngine: mockEngine, @@ -113,7 +113,7 @@ func Test_cache_Set(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -131,7 +131,7 @@ func Test_cache_Set(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -172,7 +172,7 @@ func Test_cache_Get(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -190,7 +190,7 @@ func Test_cache_Get(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, diff --git a/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go b/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go index 75780e39c4b..42ce547fb3c 100644 --- a/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go @@ -20,7 +20,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache } type args struct { pubID int @@ -40,7 +40,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { name: "get_valid_partnerConfig_map", fields: fields{ cache: gocache.New(100, 100), - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, VASTTagCacheExpiry: 1000, }, @@ -89,7 +89,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { name: "db_queries_failed_getting_partnerConfig_map", fields: fields{ cache: gocache.New(100, 100), - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, VASTTagCacheExpiry: 1000, }, @@ -119,7 +119,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { name: "db_queries_failed_getting_adunitconfig_and_wrapper_slotmappings", fields: fields{ cache: gocache.New(100, 100), - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, VASTTagCacheExpiry: 1000, }, @@ -183,7 +183,7 @@ func Test_cache_GetPartnerConfigMap_LockandLoad(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache } type args struct { pubID int @@ -203,7 +203,7 @@ func Test_cache_GetPartnerConfigMap_LockandLoad(t *testing.T) { name: "get_partnerConfig_map", fields: fields{ cache: gocache.New(100, 100), - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, VASTTagCacheExpiry: 1000, }, @@ -282,7 +282,7 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -306,7 +306,7 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { name: "error_returning_Active_partner_configuration_from_DB", fields: fields{ cache: gocache.New(100, 100), - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -330,7 +330,7 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { name: "non_nil_partnerConfigMap_from_DB", fields: fields{ cache: gocache.New(100, 100), - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -375,7 +375,7 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { name: "empty_partnerConfigMap_from_DB", fields: fields{ cache: gocache.New(100, 100), - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 100, }, db: mockDatabase, diff --git a/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go b/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go index bbafdd4a01e..6854ce508dc 100644 --- a/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go @@ -34,7 +34,7 @@ func Test_cache_populateCacheWithPubSlotNameHash(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -59,7 +59,7 @@ func Test_cache_populateCacheWithPubSlotNameHash(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 100, }, }, @@ -79,7 +79,7 @@ func Test_cache_populateCacheWithPubSlotNameHash(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 100, }, }, @@ -103,7 +103,7 @@ func Test_cache_populateCacheWithPubSlotNameHash(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 100, }, }, @@ -147,7 +147,7 @@ func Test_cache_populateCacheWithWrapperSlotMappings(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -171,7 +171,7 @@ func Test_cache_populateCacheWithWrapperSlotMappings(t *testing.T) { name: "Error from the DB", fields: fields{ cache: newCache, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -194,7 +194,7 @@ func Test_cache_populateCacheWithWrapperSlotMappings(t *testing.T) { name: "empty_mappings", fields: fields{ cache: newCache, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -217,7 +217,7 @@ func Test_cache_populateCacheWithWrapperSlotMappings(t *testing.T) { name: "valid_mappings", fields: fields{ cache: newCache, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -268,7 +268,7 @@ func Test_cache_populateCacheWithWrapperSlotMappings(t *testing.T) { name: "HashValues", fields: fields{ cache: newCache, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -348,7 +348,7 @@ func Test_cache_GetMappingsFromCacheV25(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -370,7 +370,7 @@ func Test_cache_GetMappingsFromCacheV25(t *testing.T) { fields: fields{ cache: newCache, db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -425,7 +425,7 @@ func Test_cache_GetMappingsFromCacheV25(t *testing.T) { fields: fields{ cache: newCache, db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -469,7 +469,7 @@ func Test_cache_GetSlotToHashValueMapFromCacheV25(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -491,7 +491,7 @@ func Test_cache_GetSlotToHashValueMapFromCacheV25(t *testing.T) { fields: fields{ cache: newCache, db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, @@ -526,7 +526,7 @@ func Test_cache_GetSlotToHashValueMapFromCacheV25(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, }, diff --git a/modules/pubmatic/openwrap/cache/gocache/sync_test.go b/modules/pubmatic/openwrap/cache/gocache/sync_test.go index 51179643e8b..a1d563856f4 100644 --- a/modules/pubmatic/openwrap/cache/gocache/sync_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/sync_test.go @@ -20,7 +20,7 @@ func Test_cache_LockAndLoad(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -38,7 +38,7 @@ func Test_cache_LockAndLoad(t *testing.T) { name: "test", fields: fields{ cache: gocache.New(100, 100), - cfg: config.Cache{ + cfg: config.DBCache{ CacheDefaultExpiry: 1000, }, db: mockDatabase, diff --git a/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go b/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go index e44540632be..859c1e1106d 100644 --- a/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go @@ -24,7 +24,7 @@ func Test_cache_populatePublisherVASTTags(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -47,7 +47,7 @@ func Test_cache_populatePublisherVASTTags(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ VASTTagCacheExpiry: 100000, }, }, @@ -69,7 +69,7 @@ func Test_cache_populatePublisherVASTTags(t *testing.T) { Map: sync.Map{}, cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ VASTTagCacheExpiry: 100000, }, }, @@ -98,7 +98,7 @@ func Test_cache_populatePublisherVASTTags(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ VASTTagCacheExpiry: 100000, }, }, @@ -150,7 +150,7 @@ func Test_cache_GetPublisherVASTTagsFromCache(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.Cache + cfg config.DBCache db database.Database } type args struct { @@ -168,7 +168,7 @@ func Test_cache_GetPublisherVASTTagsFromCache(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ VASTTagCacheExpiry: 100000, }, }, @@ -193,7 +193,7 @@ func Test_cache_GetPublisherVASTTagsFromCache(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.Cache{ + cfg: config.DBCache{ VASTTagCacheExpiry: 100000, }, }, diff --git a/modules/pubmatic/openwrap/config/config.go b/modules/pubmatic/openwrap/config/config.go index b26d90fd2c5..cec7d59ebc4 100755 --- a/modules/pubmatic/openwrap/config/config.go +++ b/modules/pubmatic/openwrap/config/config.go @@ -10,7 +10,7 @@ import ( type Config struct { Server Server Database Database - Cache Cache + DBCache DBCache Timeout Timeout Tracker Tracker PixelView PixelView @@ -58,7 +58,7 @@ type Queries struct { GetTBFRateQuery string } -type Cache struct { +type DBCache struct { CacheConTimeout int // Connection timeout for cache CacheDefaultExpiry int // in seconds diff --git a/modules/pubmatic/openwrap/entrypointhook.go b/modules/pubmatic/openwrap/entrypointhook.go index d7cf0e04dea..14d0d38d4df 100644 --- a/modules/pubmatic/openwrap/entrypointhook.go +++ b/modules/pubmatic/openwrap/entrypointhook.go @@ -17,10 +17,13 @@ import ( ) const ( - OpenWrapAuction = "/pbs/openrtb2/auction" - OpenWrapV25 = "/openrtb/2.5" - OpenWrapV25Video = "/openrtb/2.5/video" - OpenWrapAmp = "/openrtb/amp" + OpenWrapAuction = "/pbs/openrtb2/auction" + OpenWrapV25 = "/openrtb/2.5" + OpenWrapV25Video = "/openrtb/2.5/video" + OpenWrapOpenRTBVideo = "/video/openrtb" + OpenWrapVAST = "/video/vast" + OpenWrapJSON = "/video/json" + OpenWrapAmp = "/amp" ) func (m OpenWrap) handleEntrypointHook( @@ -34,6 +37,7 @@ func (m OpenWrap) handleEntrypointHook( return result, nil } + var pubid int var endpoint string var err error var requestExtWrapper models.RequestExtWrapper @@ -53,8 +57,17 @@ func (m OpenWrap) handleEntrypointHook( requestExtWrapper, err = v25.ConvertVideoToAuctionRequest(payload, &result) endpoint = models.EndpointVideo case OpenWrapAmp: - // requestExtWrapper, err = models.GetQueryParamRequestExtWrapper(payload.Body) + requestExtWrapper, pubid, err = models.GetQueryParamRequestExtWrapper(payload.Request) endpoint = models.EndpointAMP + case OpenWrapOpenRTBVideo: + requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body, "ext", "wrapper") + endpoint = models.EndpointVideo + case OpenWrapVAST: + requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body, "ext", "wrapper") + endpoint = models.EndpointVAST + case OpenWrapJSON: + requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body, "ext", "wrapper") + endpoint = models.EndpointJson default: // we should return from here } @@ -120,6 +133,11 @@ func (m OpenWrap) handleEntrypointHook( rCtx.LoggerImpressionID = uuid.NewV4().String() } + // temp, for AMP, etc + if pubid != 0 { + rCtx.PubID = pubid + } + result.ModuleContext = make(hookstage.ModuleContext) result.ModuleContext["rctx"] = rCtx diff --git a/modules/pubmatic/openwrap/models/amp.go b/modules/pubmatic/openwrap/models/amp.go new file mode 100644 index 00000000000..1cd1a513b93 --- /dev/null +++ b/modules/pubmatic/openwrap/models/amp.go @@ -0,0 +1,19 @@ +package models + +import ( + "net/http" + "strconv" +) + +func GetQueryParamRequestExtWrapper(request *http.Request) (RequestExtWrapper, int, error) { + extWrapper := RequestExtWrapper{ + SSAuctionFlag: -1, + } + + values := request.URL.Query() + pubid, _ := strconv.Atoi(values.Get(PUBID_KEY)) + extWrapper.ProfileId, _ = strconv.Atoi(values.Get(PROFILEID_KEY)) + extWrapper.VersionId, _ = strconv.Atoi(values.Get(VERSION_KEY)) + + return extWrapper, pubid, nil +} diff --git a/modules/pubmatic/openwrap/models/constants.go b/modules/pubmatic/openwrap/models/constants.go index 42ad7d36661..9f2f962cc85 100755 --- a/modules/pubmatic/openwrap/models/constants.go +++ b/modules/pubmatic/openwrap/models/constants.go @@ -408,6 +408,7 @@ const ( EndpointJson = "json" EndpointORTB = "ortb" EndpointVAST = "vast" + EndPointCTV = "ctv" Openwrap = "openwrap" ImpTypeBanner = "banner" ImpTypeVideo = "video" diff --git a/modules/pubmatic/openwrap/models/openwrap.go b/modules/pubmatic/openwrap/models/openwrap.go index 5bddd629de6..d33107f5469 100644 --- a/modules/pubmatic/openwrap/models/openwrap.go +++ b/modules/pubmatic/openwrap/models/openwrap.go @@ -103,6 +103,7 @@ type ImpCtx struct { IsRewardInventory *int8 Banner bool Video *openrtb2.Video + Native *openrtb2.Native IncomingSlots []string Type string // banner, video, native, etc Bidders map[string]PartnerData @@ -113,6 +114,10 @@ type ImpCtx struct { BannerAdUnitCtx AdUnitCtx VideoAdUnitCtx AdUnitCtx + + //temp + BidderError string + IsAdPodRequest bool } type PartnerData struct { @@ -123,6 +128,8 @@ type PartnerData struct { KGPV string IsRegex bool Params json.RawMessage + VASTTagFlag bool + VASTTagFlags map[string]bool } type BidCtx struct { diff --git a/modules/pubmatic/openwrap/openwrap.go b/modules/pubmatic/openwrap/openwrap.go index 7e6d970c324..488749bed1b 100644 --- a/modules/pubmatic/openwrap/openwrap.go +++ b/modules/pubmatic/openwrap/openwrap.go @@ -20,6 +20,7 @@ import ( metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" metrics_cfg "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/config" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/tbf" ) const ( @@ -49,7 +50,7 @@ func initOpenWrap(rawCfg json.RawMessage, moduleDeps moduledeps.ModuleDeps) (Ope db := mysql.New(mysqlDriver, cfg.Database) // NYC_TODO: replace this with freecache and use concrete structure - cache := gocache.New(time.Duration(cfg.Cache.CacheDefaultExpiry)*time.Second, CACHE_EXPIRY_ROUTINE_RUN_INTERVAL) + cache := gocache.New(time.Duration(cfg.DBCache.CacheDefaultExpiry)*time.Second, CACHE_EXPIRY_ROUTINE_RUN_INTERVAL) if cache == nil { return OpenWrap{}, errors.New("error while initializing cache") } @@ -64,9 +65,13 @@ func initOpenWrap(rawCfg json.RawMessage, moduleDeps moduledeps.ModuleDeps) (Ope return OpenWrap{}, fmt.Errorf("error while initializing metrics-engine: %v", err) } - owCache := ow_gocache.New(cache, db, cfg.Cache, &metricEngine) + owCache := ow_gocache.New(cache, db, cfg.DBCache, &metricEngine) - fullscreenclickability.Init(owCache, cfg.Cache.CacheDefaultExpiry) + // Init FSC and related services + fullscreenclickability.Init(owCache, cfg.DBCache.CacheDefaultExpiry) + + // Init TBF (tracking-beacon-first) feature related services + tbf.Init(cfg.DBCache.CacheDefaultExpiry, owCache) return OpenWrap{ cfg: cfg, diff --git a/modules/pubmatic/openwrap/openwrap_sshb.go b/modules/pubmatic/openwrap/openwrap_sshb.go new file mode 100644 index 00000000000..057cc9d2bbb --- /dev/null +++ b/modules/pubmatic/openwrap/openwrap_sshb.go @@ -0,0 +1,38 @@ +package openwrap + +import ( + cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" + metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" +) + +// GetConfig Temporary function to expose config to SSHB +func (ow OpenWrap) GetConfig() config.Config { + return ow.cfg + +} + +// GetCache Temporary function to expose cache to SSHB +func (ow OpenWrap) GetCache() cache.Cache { + return ow.cache +} + +// GetMetricEngine Temporary function to expose mertics to SSHB +func (ow OpenWrap) GetMetricEngine() metrics.MetricsEngine { + return ow.metricEngine +} + +// SetConfig Temporary function to expose config to SSHB +func (ow *OpenWrap) SetConfig(c config.Config) { + ow.cfg = c +} + +// GetCache Temporary function to expose cache to SSHB +func (ow *OpenWrap) SetCache(c cache.Cache) { + ow.cache = c +} + +// GetMetricEngine Temporary function to expose mertics to SSHB +func (ow *OpenWrap) SetMetricEngine(m metrics.MetricsEngine) { + ow.metricEngine = m +} From 629553d34c444087e95252c1d39e90eb39697918 Mon Sep 17 00:00:00 2001 From: Ankit-Pinge <128145896+Ankit-Pinge@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:41:55 +0530 Subject: [PATCH 09/30] fix github actions: adapter code coverage (#622) --- .github/workflows/adapter-code-coverage.yml | 9 ++++++++- .github/workflows/cross-repo-issue.yml | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/adapter-code-coverage.yml b/.github/workflows/adapter-code-coverage.yml index 5b5dc9331e5..b74ecebb446 100644 --- a/.github/workflows/adapter-code-coverage.yml +++ b/.github/workflows/adapter-code-coverage.yml @@ -43,6 +43,8 @@ jobs: id: run_coverage if: ${{ steps.get_directories.outputs.result }} != "" run: | + git config --global url."https://${USERNAME}:${TOKEN}@git.pubmatic.com".insteadOf "https://git.pubmatic.com" + directories=$(echo '${{ steps.get_directories.outputs.result }}' | jq -r '.[]') go mod download @@ -65,13 +67,18 @@ jobs: # remove pull request branch files cd .. rm -f -r ./* + env: + GO111MODULE: "on" + GOPRIVATE: "git.pubmatic.com/PubMatic/*" + TOKEN: ${{ secrets.PM_OPENWRAP_CICD_PASSWORD }} + USERNAME: ${{ secrets.PM_OPENWRAP_CICD_USERNAME }} - name: Checkout coverage-preview branch uses: actions/checkout@v3 with: fetch-depth: 0 ref: coverage-preview - repository: prebid/prebid-server + repository: PubMatic-OpenWrap/prebid-server - name: Commit coverage files to coverage-preview branch if: ${{ steps.run_coverage.outputs.coverage_dir }} != "" diff --git a/.github/workflows/cross-repo-issue.yml b/.github/workflows/cross-repo-issue.yml index 2bea44a301c..fd479cd2ad9 100644 --- a/.github/workflows/cross-repo-issue.yml +++ b/.github/workflows/cross-repo-issue.yml @@ -4,7 +4,7 @@ on: pull_request_target: types: [closed] branches: - - "master" + - "master-disable" jobs: cross-repo: From 06f8d1b616613a08c6a079d26e9f27baef76c150 Mon Sep 17 00:00:00 2001 From: Nikhil Vaidya <102963966+pm-nikhil-vaidya@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:06:07 +0530 Subject: [PATCH 10/30] Fix: Single colon in BidId causing inconsistent split (#625) --- modules/pubmatic/openwrap/utils/bid.go | 2 +- modules/pubmatic/openwrap/utils/bid_test.go | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/pubmatic/openwrap/utils/bid.go b/modules/pubmatic/openwrap/utils/bid.go index f1146d4b4ee..166976179f3 100644 --- a/modules/pubmatic/openwrap/utils/bid.go +++ b/modules/pubmatic/openwrap/utils/bid.go @@ -6,7 +6,7 @@ import ( "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" ) -var bidIDRegx = regexp.MustCompile("[" + models.BidIdSeparator + "]") +var bidIDRegx = regexp.MustCompile("(" + models.BidIdSeparator + ")") func GetOriginalBidId(bidID string) string { return bidIDRegx.Split(bidID, -1)[0] diff --git a/modules/pubmatic/openwrap/utils/bid_test.go b/modules/pubmatic/openwrap/utils/bid_test.go index 5b78f0ec3c8..d115fe1d1be 100644 --- a/modules/pubmatic/openwrap/utils/bid_test.go +++ b/modules/pubmatic/openwrap/utils/bid_test.go @@ -48,6 +48,20 @@ func TestGetOriginalBidId(t *testing.T) { }, want: "original-bid", }, + { + name: "BidId with single colon in origin Id", + args: args{ + bidId: "original-bid:2::generated-bid", + }, + want: "original-bid:2", + }, + { + name: "BidId with single colon in generated Id", + args: args{ + bidId: "original-bid:2::generated-bid:3", + }, + want: "original-bid:2", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 78a7b57d0d4d5495b32e171e2d31c4e5ffe7c5f4 Mon Sep 17 00:00:00 2001 From: Pubmatic-Supriya-Patil <131644110+Pubmatic-Supriya-Patil@users.noreply.github.com> Date: Thu, 2 Nov 2023 15:23:20 +0530 Subject: [PATCH 11/30] OTT-1387: use OpenWrap.Stats.Endpoint for stats config in vast un-wrap service (#629) --- go.mod | 2 +- go.sum | 4 ++-- .../vastunwrap/hook_raw_bidder_response_test.go | 2 +- modules/pubmatic/vastunwrap/module_test.go | 12 ++++++------ modules/pubmatic/vastunwrap/unwrap_service_test.go | 10 +++++----- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 15653c53348..7a8abb2f726 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/PubMatic-OpenWrap/prebid-server go 1.20 -replace git.pubmatic.com/vastunwrap => git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20231012062530-95f4848c9fb7 +replace git.pubmatic.com/vastunwrap => git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20231102070946-3c5a3bc1dff5 require ( github.com/DATA-DOG/go-sqlmock v1.5.0 diff --git a/go.sum b/go.sum index e375fadc049..8086862d444 100644 --- a/go.sum +++ b/go.sum @@ -292,8 +292,8 @@ cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20231012062530-95f4848c9fb7 h1:YD51mgFU5YJX6ufDBO19QIx/vX38uk7bxNhxYfqEytA= -git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20231012062530-95f4848c9fb7/go.mod h1:dgTumQ6/KYeLbpWq3HVOaqkZos6Q0QGwZmQmEIhQ3To= +git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20231102070946-3c5a3bc1dff5 h1:nK2YP3aS8+5dwc5cMJ8IxI0Sh7yfd858LKmcvwfkOUo= +git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20231102070946-3c5a3bc1dff5/go.mod h1:dgTumQ6/KYeLbpWq3HVOaqkZos6Q0QGwZmQmEIhQ3To= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= diff --git a/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go b/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go index 533068b65d2..933c1123ec6 100644 --- a/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go +++ b/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go @@ -21,7 +21,7 @@ func TestHandleRawBidderResponseHook(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockMetricsEngine := mock_stats.NewMockMetricsEngine(ctrl) - VastUnWrapModule := VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1500}}, MetricsEngine: mockMetricsEngine} + VastUnWrapModule := VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1500}}, MetricsEngine: mockMetricsEngine} type args struct { module VastUnwrapModule payload hookstage.RawBidderResponsePayload diff --git a/modules/pubmatic/vastunwrap/module_test.go b/modules/pubmatic/vastunwrap/module_test.go index e29458f84fd..749cf4c4aaf 100644 --- a/modules/pubmatic/vastunwrap/module_test.go +++ b/modules/pubmatic/vastunwrap/module_test.go @@ -48,7 +48,7 @@ func TestVastUnwrapModuleHandleEntrypointHook(t *testing.T) { fields: fields{cfg: VastUnwrapModule{Enabled: true, Cfg: unWrapCfg.VastUnWrapCfg{ HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 100, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, }, TrafficPercentage: 2}}, @@ -69,7 +69,7 @@ func TestVastUnwrapModuleHandleEntrypointHook(t *testing.T) { cfg: VastUnwrapModule{Enabled: false, Cfg: unWrapCfg.VastUnWrapCfg{ HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 100, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, }, TrafficPercentage: 2}}, @@ -136,7 +136,7 @@ func TestVastUnwrapModuleHandleRawBidderResponseHook(t *testing.T) { MaxWrapperSupport: 5, HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 1000, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, }, TrafficPercentage: 2}}, @@ -179,7 +179,7 @@ func TestVastUnwrapModuleHandleRawBidderResponseHook(t *testing.T) { fields: fields{cfg: VastUnwrapModule{Enabled: false, Cfg: unWrapCfg.VastUnWrapCfg{ HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 100, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, }, TrafficPercentage: 2}}, @@ -260,7 +260,7 @@ func TestBuilder(t *testing.T) { MaxWrapperSupport: 5, HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 100, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, }, }, @@ -289,7 +289,7 @@ func TestBuilder(t *testing.T) { MaxWrapperSupport: 5, HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 100, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, }, }, diff --git a/modules/pubmatic/vastunwrap/unwrap_service_test.go b/modules/pubmatic/vastunwrap/unwrap_service_test.go index e9b4c2c3a94..453861b3700 100644 --- a/modules/pubmatic/vastunwrap/unwrap_service_test.go +++ b/modules/pubmatic/vastunwrap/unwrap_service_test.go @@ -36,7 +36,7 @@ func TestDoUnwrap(t *testing.T) { { name: "doUnwrap for adtype video with Empty Bid", args: args{ - module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1000}}, MetricsEngine: mockMetricsEngine}, + module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1000}}, MetricsEngine: mockMetricsEngine}, bid: &adapters.TypedBid{ Bid: &openrtb2.Bid{}, }, @@ -47,7 +47,7 @@ func TestDoUnwrap(t *testing.T) { { name: "doUnwrap for adtype video with Empty ADM", args: args{ - module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1000}}, MetricsEngine: mockMetricsEngine}, + module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1000}}, MetricsEngine: mockMetricsEngine}, bid: &adapters.TypedBid{ Bid: &openrtb2.Bid{ ID: "Bid-123", @@ -66,7 +66,7 @@ func TestDoUnwrap(t *testing.T) { { name: "doUnwrap for adtype video with invalid URL and timeout", args: args{ - module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 2}}, MetricsEngine: mockMetricsEngine}, + module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 2}}, MetricsEngine: mockMetricsEngine}, bid: &adapters.TypedBid{ Bid: &openrtb2.Bid{ ID: "Bid-123", @@ -95,7 +95,7 @@ func TestDoUnwrap(t *testing.T) { { name: "doUnwrap for adtype video", args: args{ - module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1500}}, MetricsEngine: mockMetricsEngine}, + module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1500}}, MetricsEngine: mockMetricsEngine}, bid: &adapters.TypedBid{ Bid: &openrtb2.Bid{ ID: "Bid-123", @@ -127,7 +127,7 @@ func TestDoUnwrap(t *testing.T) { { name: "doUnwrap for adtype video with invalid vast xml", args: args{ - module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Host: "10.172.141.13", Port: 8080, RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1000}}, MetricsEngine: mockMetricsEngine}, + module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", RefershIntervalInSec: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1000}}, MetricsEngine: mockMetricsEngine}, bid: &adapters.TypedBid{ Bid: &openrtb2.Bid{ ID: "Bid-123", From 9f38f2f650f4c2bf6e4c265d9e1f271b11de1759 Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:44:35 +0530 Subject: [PATCH 12/30] UOE-9673: revert dbcache rename (#630) --- .../cache/gocache/adunit_config_test.go | 18 +++++------ .../gocache/fullscreenclickability_test.go | 12 ++++---- .../openwrap/cache/gocache/gocache.go | 4 +-- .../openwrap/cache/gocache/gocache_test.go | 12 ++++---- .../cache/gocache/partner_config_test.go | 20 ++++++------- .../cache/gocache/slot_mappings_test.go | 30 +++++++++---------- .../openwrap/cache/gocache/sync_test.go | 4 +-- .../openwrap/cache/gocache/vast_tags_test.go | 14 ++++----- modules/pubmatic/openwrap/config/config.go | 4 +-- modules/pubmatic/openwrap/openwrap.go | 8 ++--- 10 files changed, 63 insertions(+), 63 deletions(-) diff --git a/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go b/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go index b5cc97656d5..1fba4efbf99 100644 --- a/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go @@ -152,7 +152,7 @@ func Test_cache_populateCacheWithAdunitConfig(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -177,7 +177,7 @@ func Test_cache_populateCacheWithAdunitConfig(t *testing.T) { fields: fields{ cache: gocache.New(10, 10), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -200,7 +200,7 @@ func Test_cache_populateCacheWithAdunitConfig(t *testing.T) { fields: fields{ cache: gocache.New(10, 10), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -223,7 +223,7 @@ func Test_cache_populateCacheWithAdunitConfig(t *testing.T) { fields: fields{ cache: gocache.New(10, 10), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -277,7 +277,7 @@ func Test_cache_GetAdunitConfigFromCache(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -300,7 +300,7 @@ func Test_cache_GetAdunitConfigFromCache(t *testing.T) { name: "test_request", fields: fields{ db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -321,7 +321,7 @@ func Test_cache_GetAdunitConfigFromCache(t *testing.T) { name: "successfully_get_value_from_cache", fields: fields{ db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -345,7 +345,7 @@ func Test_cache_GetAdunitConfigFromCache(t *testing.T) { name: "got_empty_adunitconfig_from_cache", fields: fields{ db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -370,7 +370,7 @@ func Test_cache_GetAdunitConfigFromCache(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, diff --git a/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go b/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go index d0efe3fdb1c..2ad4f577830 100644 --- a/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go @@ -24,7 +24,7 @@ func TestGetFSCDisabledPublishers(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } tests := []struct { @@ -49,7 +49,7 @@ func TestGetFSCDisabledPublishers(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -65,7 +65,7 @@ func TestGetFSCDisabledPublishers(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -104,7 +104,7 @@ func TestGetFSCThresholdPerDSP(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } tests := []struct { @@ -129,7 +129,7 @@ func TestGetFSCThresholdPerDSP(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -145,7 +145,7 @@ func TestGetFSCThresholdPerDSP(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, diff --git a/modules/pubmatic/openwrap/cache/gocache/gocache.go b/modules/pubmatic/openwrap/cache/gocache/gocache.go index 8952408c09b..05f4522b5c9 100644 --- a/modules/pubmatic/openwrap/cache/gocache/gocache.go +++ b/modules/pubmatic/openwrap/cache/gocache/gocache.go @@ -32,7 +32,7 @@ func key(format string, v ...interface{}) string { type cache struct { sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database metricEngine metrics.MetricsEngine } @@ -40,7 +40,7 @@ type cache struct { var c *cache var cOnce sync.Once -func New(goCache *gocache.Cache, database database.Database, cfg config.DBCache, metricEngine metrics.MetricsEngine) *cache { +func New(goCache *gocache.Cache, database database.Database, cfg config.Cache, metricEngine metrics.MetricsEngine) *cache { cOnce.Do( func() { c = &cache{ diff --git a/modules/pubmatic/openwrap/cache/gocache/gocache_test.go b/modules/pubmatic/openwrap/cache/gocache/gocache_test.go index c86d6755efb..02a5c09b22c 100644 --- a/modules/pubmatic/openwrap/cache/gocache/gocache_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/gocache_test.go @@ -52,7 +52,7 @@ func TestNew(t *testing.T) { type args struct { goCache *gocache.Cache database database.Database - cfg config.DBCache + cfg config.Cache metricsEngine metrics.MetricsEngine } tests := []struct { @@ -64,7 +64,7 @@ func TestNew(t *testing.T) { args: args{ goCache: gocache.New(100, 100), database: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, metricsEngine: mockEngine, @@ -113,7 +113,7 @@ func Test_cache_Set(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -131,7 +131,7 @@ func Test_cache_Set(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -172,7 +172,7 @@ func Test_cache_Get(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -190,7 +190,7 @@ func Test_cache_Get(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, diff --git a/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go b/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go index 42ce547fb3c..75780e39c4b 100644 --- a/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go @@ -20,7 +20,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache } type args struct { pubID int @@ -40,7 +40,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { name: "get_valid_partnerConfig_map", fields: fields{ cache: gocache.New(100, 100), - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, VASTTagCacheExpiry: 1000, }, @@ -89,7 +89,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { name: "db_queries_failed_getting_partnerConfig_map", fields: fields{ cache: gocache.New(100, 100), - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, VASTTagCacheExpiry: 1000, }, @@ -119,7 +119,7 @@ func Test_cache_GetPartnerConfigMap(t *testing.T) { name: "db_queries_failed_getting_adunitconfig_and_wrapper_slotmappings", fields: fields{ cache: gocache.New(100, 100), - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, VASTTagCacheExpiry: 1000, }, @@ -183,7 +183,7 @@ func Test_cache_GetPartnerConfigMap_LockandLoad(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache } type args struct { pubID int @@ -203,7 +203,7 @@ func Test_cache_GetPartnerConfigMap_LockandLoad(t *testing.T) { name: "get_partnerConfig_map", fields: fields{ cache: gocache.New(100, 100), - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, VASTTagCacheExpiry: 1000, }, @@ -282,7 +282,7 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -306,7 +306,7 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { name: "error_returning_Active_partner_configuration_from_DB", fields: fields{ cache: gocache.New(100, 100), - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -330,7 +330,7 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { name: "non_nil_partnerConfigMap_from_DB", fields: fields{ cache: gocache.New(100, 100), - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -375,7 +375,7 @@ func Test_cache_getActivePartnerConfigAndPopulateWrapperMappings(t *testing.T) { name: "empty_partnerConfigMap_from_DB", fields: fields{ cache: gocache.New(100, 100), - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 100, }, db: mockDatabase, diff --git a/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go b/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go index 6854ce508dc..bbafdd4a01e 100644 --- a/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go @@ -34,7 +34,7 @@ func Test_cache_populateCacheWithPubSlotNameHash(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -59,7 +59,7 @@ func Test_cache_populateCacheWithPubSlotNameHash(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 100, }, }, @@ -79,7 +79,7 @@ func Test_cache_populateCacheWithPubSlotNameHash(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 100, }, }, @@ -103,7 +103,7 @@ func Test_cache_populateCacheWithPubSlotNameHash(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 100, }, }, @@ -147,7 +147,7 @@ func Test_cache_populateCacheWithWrapperSlotMappings(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -171,7 +171,7 @@ func Test_cache_populateCacheWithWrapperSlotMappings(t *testing.T) { name: "Error from the DB", fields: fields{ cache: newCache, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -194,7 +194,7 @@ func Test_cache_populateCacheWithWrapperSlotMappings(t *testing.T) { name: "empty_mappings", fields: fields{ cache: newCache, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -217,7 +217,7 @@ func Test_cache_populateCacheWithWrapperSlotMappings(t *testing.T) { name: "valid_mappings", fields: fields{ cache: newCache, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -268,7 +268,7 @@ func Test_cache_populateCacheWithWrapperSlotMappings(t *testing.T) { name: "HashValues", fields: fields{ cache: newCache, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 100, }, db: mockDatabase, @@ -348,7 +348,7 @@ func Test_cache_GetMappingsFromCacheV25(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -370,7 +370,7 @@ func Test_cache_GetMappingsFromCacheV25(t *testing.T) { fields: fields{ cache: newCache, db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -425,7 +425,7 @@ func Test_cache_GetMappingsFromCacheV25(t *testing.T) { fields: fields{ cache: newCache, db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -469,7 +469,7 @@ func Test_cache_GetSlotToHashValueMapFromCacheV25(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -491,7 +491,7 @@ func Test_cache_GetSlotToHashValueMapFromCacheV25(t *testing.T) { fields: fields{ cache: newCache, db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, @@ -526,7 +526,7 @@ func Test_cache_GetSlotToHashValueMapFromCacheV25(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, }, diff --git a/modules/pubmatic/openwrap/cache/gocache/sync_test.go b/modules/pubmatic/openwrap/cache/gocache/sync_test.go index a1d563856f4..51179643e8b 100644 --- a/modules/pubmatic/openwrap/cache/gocache/sync_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/sync_test.go @@ -20,7 +20,7 @@ func Test_cache_LockAndLoad(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -38,7 +38,7 @@ func Test_cache_LockAndLoad(t *testing.T) { name: "test", fields: fields{ cache: gocache.New(100, 100), - cfg: config.DBCache{ + cfg: config.Cache{ CacheDefaultExpiry: 1000, }, db: mockDatabase, diff --git a/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go b/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go index 859c1e1106d..e44540632be 100644 --- a/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go @@ -24,7 +24,7 @@ func Test_cache_populatePublisherVASTTags(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -47,7 +47,7 @@ func Test_cache_populatePublisherVASTTags(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ VASTTagCacheExpiry: 100000, }, }, @@ -69,7 +69,7 @@ func Test_cache_populatePublisherVASTTags(t *testing.T) { Map: sync.Map{}, cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ VASTTagCacheExpiry: 100000, }, }, @@ -98,7 +98,7 @@ func Test_cache_populatePublisherVASTTags(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ VASTTagCacheExpiry: 100000, }, }, @@ -150,7 +150,7 @@ func Test_cache_GetPublisherVASTTagsFromCache(t *testing.T) { type fields struct { Map sync.Map cache *gocache.Cache - cfg config.DBCache + cfg config.Cache db database.Database } type args struct { @@ -168,7 +168,7 @@ func Test_cache_GetPublisherVASTTagsFromCache(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ VASTTagCacheExpiry: 100000, }, }, @@ -193,7 +193,7 @@ func Test_cache_GetPublisherVASTTagsFromCache(t *testing.T) { fields: fields{ cache: gocache.New(100, 100), db: mockDatabase, - cfg: config.DBCache{ + cfg: config.Cache{ VASTTagCacheExpiry: 100000, }, }, diff --git a/modules/pubmatic/openwrap/config/config.go b/modules/pubmatic/openwrap/config/config.go index cec7d59ebc4..b26d90fd2c5 100755 --- a/modules/pubmatic/openwrap/config/config.go +++ b/modules/pubmatic/openwrap/config/config.go @@ -10,7 +10,7 @@ import ( type Config struct { Server Server Database Database - DBCache DBCache + Cache Cache Timeout Timeout Tracker Tracker PixelView PixelView @@ -58,7 +58,7 @@ type Queries struct { GetTBFRateQuery string } -type DBCache struct { +type Cache struct { CacheConTimeout int // Connection timeout for cache CacheDefaultExpiry int // in seconds diff --git a/modules/pubmatic/openwrap/openwrap.go b/modules/pubmatic/openwrap/openwrap.go index 488749bed1b..8771338704f 100644 --- a/modules/pubmatic/openwrap/openwrap.go +++ b/modules/pubmatic/openwrap/openwrap.go @@ -50,7 +50,7 @@ func initOpenWrap(rawCfg json.RawMessage, moduleDeps moduledeps.ModuleDeps) (Ope db := mysql.New(mysqlDriver, cfg.Database) // NYC_TODO: replace this with freecache and use concrete structure - cache := gocache.New(time.Duration(cfg.DBCache.CacheDefaultExpiry)*time.Second, CACHE_EXPIRY_ROUTINE_RUN_INTERVAL) + cache := gocache.New(time.Duration(cfg.Cache.CacheDefaultExpiry)*time.Second, CACHE_EXPIRY_ROUTINE_RUN_INTERVAL) if cache == nil { return OpenWrap{}, errors.New("error while initializing cache") } @@ -65,13 +65,13 @@ func initOpenWrap(rawCfg json.RawMessage, moduleDeps moduledeps.ModuleDeps) (Ope return OpenWrap{}, fmt.Errorf("error while initializing metrics-engine: %v", err) } - owCache := ow_gocache.New(cache, db, cfg.DBCache, &metricEngine) + owCache := ow_gocache.New(cache, db, cfg.Cache, &metricEngine) // Init FSC and related services - fullscreenclickability.Init(owCache, cfg.DBCache.CacheDefaultExpiry) + fullscreenclickability.Init(owCache, cfg.Cache.CacheDefaultExpiry) // Init TBF (tracking-beacon-first) feature related services - tbf.Init(cfg.DBCache.CacheDefaultExpiry, owCache) + tbf.Init(cfg.Cache.CacheDefaultExpiry, owCache) return OpenWrap{ cfg: cfg, From dcaa2a6f89376e06cdaa3669de67c3585245a5cd Mon Sep 17 00:00:00 2001 From: Pubmatic-Supriya-Patil <131644110+Pubmatic-Supriya-Patil@users.noreply.github.com> Date: Thu, 2 Nov 2023 17:31:20 +0530 Subject: [PATCH 13/30] OTT-1277: remove Prometheus stats for req.Device.Geo.Country (#624) --- .../openwrap/metrics/config/metrics.go | 7 ----- .../openwrap/metrics/config/metrics_test.go | 2 -- modules/pubmatic/openwrap/metrics/metrics.go | 1 - .../pubmatic/openwrap/metrics/mock/mock.go | 12 -------- .../openwrap/metrics/prometheus/prometheus.go | 1 - .../metrics/prometheus/prometheus_sshb.go | 12 -------- .../prometheus/prometheus_sshb_test.go | 28 ------------------- .../openwrap/metrics/stats/tcp_stats.go | 1 - 8 files changed, 64 deletions(-) diff --git a/modules/pubmatic/openwrap/metrics/config/metrics.go b/modules/pubmatic/openwrap/metrics/config/metrics.go index e5f31ee72dd..cf67a7e652a 100644 --- a/modules/pubmatic/openwrap/metrics/config/metrics.go +++ b/modules/pubmatic/openwrap/metrics/config/metrics.go @@ -445,10 +445,3 @@ func (me *MultiMetricsEngine) RecordOWServerPanic(endpoint, methodName, nodeName thisME.RecordOWServerPanic(endpoint, methodName, nodeName, podName) } } - -// RecordCountry records count of requests received with req.device.geo.country -func (me *MultiMetricsEngine) RecordCountry(pubID string) { - for _, thisME := range *me { - thisME.RecordCountry(pubID) - } -} diff --git a/modules/pubmatic/openwrap/metrics/config/metrics_test.go b/modules/pubmatic/openwrap/metrics/config/metrics_test.go index 2523657934a..6f80fed117e 100644 --- a/modules/pubmatic/openwrap/metrics/config/metrics_test.go +++ b/modules/pubmatic/openwrap/metrics/config/metrics_test.go @@ -217,7 +217,6 @@ func TestRecordFunctionForMultiMetricsEngine(t *testing.T) { mockEngine.EXPECT().RecordSendLoggerDataTime("requestType", "profileid", time.Second) mockEngine.EXPECT().RecordRequestTime("requestType", time.Second) mockEngine.EXPECT().RecordOWServerPanic("endpoint", "methodName", "nodeName", "podName") - mockEngine.EXPECT().RecordCountry("pubID") // create the multi-metric engine multiMetricEngine := MultiMetricsEngine{} @@ -280,5 +279,4 @@ func TestRecordFunctionForMultiMetricsEngine(t *testing.T) { multiMetricEngine.RecordSendLoggerDataTime("requestType", "profileid", time.Second) multiMetricEngine.RecordRequestTime("requestType", time.Second) multiMetricEngine.RecordOWServerPanic("endpoint", "methodName", "nodeName", "podName") - multiMetricEngine.RecordCountry("pubID") } diff --git a/modules/pubmatic/openwrap/metrics/metrics.go b/modules/pubmatic/openwrap/metrics/metrics.go index 7848f993bc0..3d00bf73219 100644 --- a/modules/pubmatic/openwrap/metrics/metrics.go +++ b/modules/pubmatic/openwrap/metrics/metrics.go @@ -72,5 +72,4 @@ type MetricsEngine interface { RecordSendLoggerDataTime(requestType, profileid string, sendTime time.Duration) RecordRequestTime(requestType string, requestTime time.Duration) RecordOWServerPanic(endpoint, methodName, nodeName, podName string) - RecordCountry(pubID string) } diff --git a/modules/pubmatic/openwrap/metrics/mock/mock.go b/modules/pubmatic/openwrap/metrics/mock/mock.go index 392ed5ca864..03dc3b0ae19 100644 --- a/modules/pubmatic/openwrap/metrics/mock/mock.go +++ b/modules/pubmatic/openwrap/metrics/mock/mock.go @@ -323,18 +323,6 @@ func (mr *MockMetricsEngineMockRecorder) RecordOWServerPanic(arg0, arg1, arg2, a return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordOWServerPanic", reflect.TypeOf((*MockMetricsEngine)(nil).RecordOWServerPanic), arg0, arg1, arg2, arg3) } -// RecordCountry mocks base method. -func (m *MockMetricsEngine) RecordCountry(arg0 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordCountry", arg0) -} - -// RecordBids indicates an expected call of RecordCountry. -func (mr *MockMetricsEngineMockRecorder) RecordCountry(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordCountry", reflect.TypeOf((*MockMetricsEngine)(nil).RecordCountry), arg0) -} - // RecordOpenWrapServerPanicStats mocks base method. func (m *MockMetricsEngine) RecordOpenWrapServerPanicStats(arg0, arg1 string) { m.ctrl.T.Helper() diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go index b5d5bd2e631..94e439773bf 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go @@ -65,7 +65,6 @@ type Metrics struct { panicCounts *prometheus.CounterVec sendLoggerData *prometheus.HistogramVec owRequestTime *prometheus.HistogramVec - country *prometheus.CounterVec } const ( diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go index f47e27b0333..bad86d6ce13 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go @@ -107,11 +107,6 @@ func newSSHBMetrics(metrics *Metrics, cfg *config.PrometheusMetrics, promRegistr "Counts the header-bidding server panic.", []string{nodeNameLabel, podNameLabel, methodNameLabel, endpointLabel}) - metrics.country = newCounter(cfg, promRegistry, - "sshb_country", - "Count of requests received with publishers Country by publisher id.", - []string{pubIDLabel}) - preloadLabelValues(metrics) } @@ -199,13 +194,6 @@ func (m *Metrics) RecordOWServerPanic(endpoint, methodName, nodeName, podName st }).Inc() } -// RecordCountry records count of requests received with req.device.geo.country -func (m *Metrics) RecordCountry(pubID string) { - m.country.With(prometheus.Labels{ - pubIDLabel: pubID, - }).Inc() -} - func preloadLabelValues(m *Metrics) { var ( requestStatusValues = requestStatusesAsString() diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go index 7a6a7842017..fc3423529ff 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go @@ -387,31 +387,3 @@ func TestRegisterLabelPermutations(t *testing.T) { assert.ElementsMatch(t, test.expectedLabels, resultLabels) } } - -func TestMetricsRecordCountry(t *testing.T) { - m := createMetricsForTesting() - type args struct { - pubID string - } - tests := []struct { - name string - args args - want float64 - }{ - { - name: "call_record_country", - args: args{ - pubID: "1010", - }, - want: 1, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m.RecordCountry(tt.args.pubID) - assertCounterVecValue(t, "", "country", m.country, tt.want, prometheus.Labels{ - pubIDLabel: tt.args.pubID, - }) - }) - } -} diff --git a/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go b/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go index bb3b23d2c8c..86a1936e6f5 100644 --- a/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go +++ b/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go @@ -339,4 +339,3 @@ func (st *StatsTCP) RecordCtvUaAccuracy(pubId, status string) func (st *StatsTCP) RecordSendLoggerDataTime(requestType, profileid string, sendTime time.Duration) {} func (st *StatsTCP) RecordRequestTime(requestType string, requestTime time.Duration) {} func (st *StatsTCP) RecordOWServerPanic(endpoint, methodName, nodeName, podName string) {} -func (st *StatsTCP) RecordCountry(pubID string) {} From aa2cb85d54754dea1c63c3369658de3a4494afa5 Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Fri, 3 Nov 2023 14:51:11 +0530 Subject: [PATCH 14/30] OTT-1402: fix winner targeting (#635) --- modules/pubmatic/openwrap/targeting.go | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/modules/pubmatic/openwrap/targeting.go b/modules/pubmatic/openwrap/targeting.go index 60558e708a3..374b20ffe4a 100644 --- a/modules/pubmatic/openwrap/targeting.go +++ b/modules/pubmatic/openwrap/targeting.go @@ -29,27 +29,27 @@ func allowTargetingKey(key string) bool { } func addInAppTargettingKeys(targeting map[string]string, seat string, ecpm float64, bid *openrtb2.Bid, isWinningBid bool) { + targeting[models.CreatePartnerKey(seat, models.PWT_SLOTID)] = utils.GetOriginalBidId(bid.ID) + targeting[models.CreatePartnerKey(seat, models.PWT_SZ)] = models.GetSize(bid.W, bid.H) + targeting[models.CreatePartnerKey(seat, models.PWT_PARTNERID)] = seat + targeting[models.CreatePartnerKey(seat, models.PWT_ECPM)] = fmt.Sprintf("%.2f", ecpm) + targeting[models.CreatePartnerKey(seat, models.PWT_PLATFORM)] = getPlatformName(models.PLATFORM_APP) + targeting[models.CreatePartnerKey(seat, models.PWT_BIDSTATUS)] = "1" + if len(bid.DealID) != 0 { + targeting[models.CreatePartnerKey(seat, models.PWT_DEALID)] = bid.DealID + } + if isWinningBid { - targeting[models.CreatePartnerKey(seat, models.PWT_SLOTID)] = utils.GetOriginalBidId(bid.ID) - targeting[models.CreatePartnerKey(seat, models.PWT_SZ)] = models.GetSize(bid.W, bid.H) - targeting[models.CreatePartnerKey(seat, models.PWT_PARTNERID)] = seat - targeting[models.CreatePartnerKey(seat, models.PWT_ECPM)] = fmt.Sprintf("%.2f", ecpm) - targeting[models.CreatePartnerKey(seat, models.PWT_PLATFORM)] = getPlatformName(models.PLATFORM_APP) - targeting[models.CreatePartnerKey(seat, models.PWT_BIDSTATUS)] = "1" + targeting[models.PWT_SLOTID] = utils.GetOriginalBidId(bid.ID) + targeting[models.PWT_BIDSTATUS] = "1" + targeting[models.PWT_SZ] = models.GetSize(bid.W, bid.H) + targeting[models.PWT_PARTNERID] = seat + targeting[models.PWT_ECPM] = fmt.Sprintf("%.2f", ecpm) + targeting[models.PWT_PLATFORM] = getPlatformName(models.PLATFORM_APP) if len(bid.DealID) != 0 { - targeting[models.CreatePartnerKey(seat, models.PWT_DEALID)] = bid.DealID + targeting[models.PWT_DEALID] = bid.DealID } } - - targeting[models.PWT_SLOTID] = utils.GetOriginalBidId(bid.ID) - targeting[models.PWT_BIDSTATUS] = "1" - targeting[models.PWT_SZ] = models.GetSize(bid.W, bid.H) - targeting[models.PWT_PARTNERID] = seat - targeting[models.PWT_ECPM] = fmt.Sprintf("%.2f", ecpm) - targeting[models.PWT_PLATFORM] = getPlatformName(models.PLATFORM_APP) - if len(bid.DealID) != 0 { - targeting[models.PWT_DEALID] = bid.DealID - } } func addPWTTargetingForBid(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) (droppedBids map[string][]openrtb2.Bid, warnings []string) { From ac44bb5f6ca64d6ab088c2e1854d58098a3ee0f4 Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:48:33 +0530 Subject: [PATCH 15/30] OTT-1418: use 6xx error code for openwrap NBR (#636) --- modules/pubmatic/openwrap/models/nbr/codes.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/pubmatic/openwrap/models/nbr/codes.go b/modules/pubmatic/openwrap/models/nbr/codes.go index 984bf16aa1b..fc64dea733d 100644 --- a/modules/pubmatic/openwrap/models/nbr/codes.go +++ b/modules/pubmatic/openwrap/models/nbr/codes.go @@ -10,7 +10,8 @@ const ( InvalidRequest int = 2 // 500+ Vendor-specific codes. - InvalidRequestWrapperExtension int = 500 + iota + // 5xx already in use by seat non bid. https://github.com/PubMatic-OpenWrap/prebid-openrtb/blob/main/openrtb3/non_bid_status_code.go#L53 + InvalidRequestWrapperExtension int = 600 + iota InvalidPublisherID InvalidProfileID InvalidProfileConfiguration From 2a2d2db0c8067f6b3aeb13f3749a8e42062499b7 Mon Sep 17 00:00:00 2001 From: Jaydeep Mohite <30924180+pm-jaydeep-mohite@users.noreply.github.com> Date: Mon, 6 Nov 2023 09:27:07 +0530 Subject: [PATCH 16/30] OTT-1360: As OpenWrap user, disable dynamic fetch if floors module disabled from UI for all S2S profiles (#634) * OTT-1360: As OpenWrap user, disable dynamic fetch if floors module disabled from UI for all S2S profiles --- modules/pubmatic/openwrap/floors.go | 24 +++++++++--------------- modules/pubmatic/openwrap/floors_test.go | 17 ++++++++--------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/modules/pubmatic/openwrap/floors.go b/modules/pubmatic/openwrap/floors.go index 10d98b77dc4..cd1533e6a28 100644 --- a/modules/pubmatic/openwrap/floors.go +++ b/modules/pubmatic/openwrap/floors.go @@ -9,7 +9,6 @@ import ( ) func setFloorsExt(requestExt *models.RequestExt, configMap map[int]map[string]string) { - if configMap == nil || configMap[models.VersionLevelConfigID] == nil { return } @@ -24,14 +23,6 @@ func setFloorsExt(requestExt *models.RequestExt, configMap map[int]map[string]st if requestExt.Prebid.Floors.Enabled == nil { requestExt.Prebid.Floors.Enabled = ptrutil.ToPtr(true) - enable, enabledExists := configMap[models.VersionLevelConfigID][models.FloorModuleEnabled] - if enabledExists && enable != "1" { - *requestExt.Prebid.Floors.Enabled = false - } - } - - if !(*requestExt.Prebid.Floors.Enabled) { - return } if requestExt.Prebid.Floors.Enforcement == nil { @@ -48,12 +39,15 @@ func setFloorsExt(requestExt *models.RequestExt, configMap map[int]map[string]st } } - url, urlExists := configMap[models.VersionLevelConfigID][models.PriceFloorURL] - if urlExists { - if requestExt.Prebid.Floors.Location == nil { - requestExt.Prebid.Floors.Location = new(openrtb_ext.PriceFloorEndpoint) + // Based on floorPriceModuleEnabled flag, dynamic fetch would be enabled/disabled + enableFlag, isFlagPresent := configMap[models.VersionLevelConfigID][models.FloorModuleEnabled] + if isFlagPresent && enableFlag == "1" { + url, urlExists := configMap[models.VersionLevelConfigID][models.PriceFloorURL] + if urlExists { + if requestExt.Prebid.Floors.Location == nil { + requestExt.Prebid.Floors.Location = new(openrtb_ext.PriceFloorEndpoint) + } + requestExt.Prebid.Floors.Location.URL = url } - requestExt.Prebid.Floors.Location.URL = url } - } diff --git a/modules/pubmatic/openwrap/floors_test.go b/modules/pubmatic/openwrap/floors_test.go index d4e3bd2fbdf..c1b30081698 100644 --- a/modules/pubmatic/openwrap/floors_test.go +++ b/modules/pubmatic/openwrap/floors_test.go @@ -5,9 +5,10 @@ import ( "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" ) -func Test_setFloorsExt(t *testing.T) { +func TestSetFloorsExt(t *testing.T) { enable := true disable := false @@ -21,7 +22,7 @@ func Test_setFloorsExt(t *testing.T) { want *models.RequestExt }{ { - name: "JSON URL is present in db", + name: "Only JSON URL is present in db", args: args{ requestExt: &models.RequestExt{}, configMap: map[int]map[string]string{ @@ -34,9 +35,6 @@ func Test_setFloorsExt(t *testing.T) { ExtRequest: openrtb_ext.ExtRequest{ Prebid: openrtb_ext.ExtRequestPrebid{ Floors: &openrtb_ext.PriceFloorRules{ - Location: &openrtb_ext.PriceFloorEndpoint{ - URL: "http://test.com/floor", - }, Enabled: &enable, Enforcement: &openrtb_ext.PriceFloorEnforcement{ EnforcePBS: &enable, @@ -61,7 +59,10 @@ func Test_setFloorsExt(t *testing.T) { ExtRequest: openrtb_ext.ExtRequest{ Prebid: openrtb_ext.ExtRequestPrebid{ Floors: &openrtb_ext.PriceFloorRules{ - Enabled: &disable, + Enabled: &enable, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: &enable, + }, }, }, }, @@ -213,9 +214,6 @@ func Test_setFloorsExt(t *testing.T) { Prebid: openrtb_ext.ExtRequestPrebid{ Floors: &openrtb_ext.PriceFloorRules{ Enabled: &enable, - Location: &openrtb_ext.PriceFloorEndpoint{ - URL: "http://test.com/floor", - }, Enforcement: &openrtb_ext.PriceFloorEnforcement{ EnforcePBS: &enable, }, @@ -354,6 +352,7 @@ func Test_setFloorsExt(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { setFloorsExt(tt.args.requestExt, tt.args.configMap) + assert.Equal(t, tt.want, tt.args.requestExt) }) } } From 9540de8a17d917890872b4dc667f388473f41f15 Mon Sep 17 00:00:00 2001 From: Ankit-Pinge <128145896+Ankit-Pinge@users.noreply.github.com> Date: Tue, 7 Nov 2023 14:27:28 +0530 Subject: [PATCH 17/30] OTT-1349 : Pass schain object to VAST bidder via schain macro (#633) --- adapters/vastbidder/bidder_macro.go | 27 +++ adapters/vastbidder/bidder_macro_test.go | 142 +++++++++++++++ adapters/vastbidder/constant.go | 1 + adapters/vastbidder/ibidder_macro.go | 1 + adapters/vastbidder/mapper.go | 1 + openrtb_ext/supplyChain.go | 57 ++++++ openrtb_ext/supplyChain_test.go | 211 +++++++++++++++++++++++ 7 files changed, 440 insertions(+) diff --git a/adapters/vastbidder/bidder_macro.go b/adapters/vastbidder/bidder_macro.go index 7c95e45bbb4..3c585abd2b0 100644 --- a/adapters/vastbidder/bidder_macro.go +++ b/adapters/vastbidder/bidder_macro.go @@ -286,6 +286,33 @@ func (tag *BidderMacro) MacroPaymentIDChain(key string) string { return "" } +// MacroSchain contains definition for Schain Parameter +func (tag *BidderMacro) MacroSchain(key string) string { + if tag.Request.Source == nil { + return "" + } + + if tag.Request.Source.SChain != nil { + return openrtb_ext.SerializeSupplyChain(tag.Request.Source.SChain) + } + + if tag.Request.Source.Ext != nil { + schain, _, _, err := jsonparser.Get(tag.Request.Source.Ext, MacroSchain) + + if err != nil { + return "" + } + var schainObj openrtb2.SupplyChain + err = json.Unmarshal(schain, &schainObj) + + if err != nil { + return "" + } + return openrtb_ext.SerializeSupplyChain(&schainObj) + } + return "" +} + /********************* Regs *********************/ // MacroCoppa contains definition for Coppa Parameter diff --git a/adapters/vastbidder/bidder_macro_test.go b/adapters/vastbidder/bidder_macro_test.go index a5db950800f..d1d41050e5a 100644 --- a/adapters/vastbidder/bidder_macro_test.go +++ b/adapters/vastbidder/bidder_macro_test.go @@ -1619,3 +1619,145 @@ func TestBidderMacroKVM(t *testing.T) { }) } } + +func TestMacroSchain(t *testing.T) { + + type fields struct { + Request *openrtb2.BidRequest + } + type args struct { + key string + } + tests := []struct { + name string + fields fields + args args + want string + }{ + { + name: "source_object_with_both_source.schain_and_source.ext.schain", + fields: fields{&openrtb2.BidRequest{Source: &openrtb2.Source{ + SChain: &openrtb2.SupplyChain{}, + Ext: []byte(`{ + "schain":{ + "complete":1, + "nodes":[ + { + "asi":"exchange1.com", + "sid":"1234&abcd", + "hp":1, + "name":"publisher name" + } + ], + "ver":"1.0" + } + }`), + }}}, + args: args{key: "schain"}, + want: "", // here we have given priority to source.schain object hence source.schain is not nil it return empty string + }, + { + name: "nil_source.schain_object", + fields: fields{&openrtb2.BidRequest{ + Source: &openrtb2.Source{ + SChain: nil, + Ext: []byte(`{ + "schain":{ + "complete":0, + "nodes":[ + { + "asi":"exchange2.com", + "sid":"abcd", + "hp":1 + } + ], + "ver":"1.0" + } + }`), + }, + }}, + args: args{key: "schain"}, + want: "1.0,0!exchange2.com,abcd,1,,,", + }, + { + name: "missing_schain_object", + fields: fields{&openrtb2.BidRequest{Source: &openrtb2.Source{ + Ext: []byte(`{ + "somechain":{ + "complete":1, + "nodes":[ + { + "asi":"exchange1.com", + "sid":"1234&abcd", + "hp":1, + "ext":{"k1":"v1"} + } + ], + "ver":"1.0" + } + }`), + }}}, + args: args{key: "schain"}, + want: "", + }, + { + name: "missing_both_source.schain_and_source.ext", + fields: fields{&openrtb2.BidRequest{Source: nil}}, + args: args{key: "schain"}, + want: "", + }, + { + name: "source.schain_is_present", + fields: fields{&openrtb2.BidRequest{Source: &openrtb2.Source{ + SChain: &openrtb2.SupplyChain{ + Complete: 1, + Ver: "1.0", + Nodes: []openrtb2.SupplyChainNode{ + { + ASI: "asi", + SID: "sid", + RID: "rid", + Name: "name", + Domain: "domain", + HP: openrtb2.Int8Ptr(1), + }, + }}, + }}}, + args: args{key: "schain"}, + want: "1.0,1!asi,sid,1,rid,name,domain", + }, + { + name: "unmarshaling_error", + fields: fields{&openrtb2.BidRequest{Source: &openrtb2.Source{ + Ext: []byte(`{ + "schain":{ + "complete":"1", + "nodes":[ + { + "asi":"exchange1.com", + "sid":"1234&abcd", + "rid":"bid-request-1", + "name":"publisher%20name", + "domain":"publisher.com", + "hp":1 + } + ], + "ver":"1.0" + } + }`), + }}}, + args: args{key: "schain"}, + want: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tag := &BidderMacro{ + Request: tt.fields.Request, + } + got := tag.MacroSchain(tt.args.key) + assert.Equal(t, got, tt.want, tt.name) + }) + } +} diff --git a/adapters/vastbidder/constant.go b/adapters/vastbidder/constant.go index 1ba81122f78..ad84f225786 100644 --- a/adapters/vastbidder/constant.go +++ b/adapters/vastbidder/constant.go @@ -22,6 +22,7 @@ const ( MacroFD = `fd` MacroTransactionID = `tid` MacroPaymentIDChain = `pchain` + MacroSchain = `schain` //Regs MacroCoppa = `coppa` diff --git a/adapters/vastbidder/ibidder_macro.go b/adapters/vastbidder/ibidder_macro.go index a503c667e52..7ffbc721282 100644 --- a/adapters/vastbidder/ibidder_macro.go +++ b/adapters/vastbidder/ibidder_macro.go @@ -37,6 +37,7 @@ type IBidderMacro interface { MacroFD(string) string MacroTransactionID(string) string MacroPaymentIDChain(string) string + MacroSchain(string) string //Regs MacroCoppa(string) string diff --git a/adapters/vastbidder/mapper.go b/adapters/vastbidder/mapper.go index b01098f5c7a..c97ceb5e109 100644 --- a/adapters/vastbidder/mapper.go +++ b/adapters/vastbidder/mapper.go @@ -34,6 +34,7 @@ var _defaultMapper = Mapper{ MacroFD: ¯oCallBack{cached: true, escape: true, callback: IBidderMacro.MacroFD}, MacroTransactionID: ¯oCallBack{cached: true, escape: true, callback: IBidderMacro.MacroTransactionID}, MacroPaymentIDChain: ¯oCallBack{cached: true, escape: true, callback: IBidderMacro.MacroPaymentIDChain}, + MacroSchain: ¯oCallBack{cached: true, escape: false, callback: IBidderMacro.MacroSchain}, //Regs MacroCoppa: ¯oCallBack{cached: true, callback: IBidderMacro.MacroCoppa}, diff --git a/openrtb_ext/supplyChain.go b/openrtb_ext/supplyChain.go index 6f023542dfb..70299b50cc8 100644 --- a/openrtb_ext/supplyChain.go +++ b/openrtb_ext/supplyChain.go @@ -1,6 +1,10 @@ package openrtb_ext import ( + "fmt" + "net/url" + "strings" + "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/util/ptrutil" ) @@ -19,3 +23,56 @@ func cloneSupplyChain(schain *openrtb2.SupplyChain) *openrtb2.SupplyChain { return &clone } + +// SerializeSupplyChain convert schain object to serialized string +func SerializeSupplyChain(schain *openrtb2.SupplyChain) string { + + if len(schain.Nodes) < 1 { + return "" + } + var serializedSchain strings.Builder + serializedSchain.Grow(256) + + serializedSchain.WriteString(schain.Ver) + serializedSchain.WriteByte(',') + fmt.Fprintf(&serializedSchain, "%d", schain.Complete) + + for _, node := range schain.Nodes { + serializedSchain.WriteByte('!') + + if node.ASI != "" { + serializedSchain.WriteString(url.QueryEscape(node.ASI)) + } + serializedSchain.WriteByte(',') + + if node.SID != "" { + serializedSchain.WriteString(url.QueryEscape(node.SID)) + } + serializedSchain.WriteByte(',') + + if node.HP != nil { + // node.HP is integer pointer so 1st dereference it then convert it to string and push to serializedSchain + fmt.Fprintf(&serializedSchain, "%d", *node.HP) + } + serializedSchain.WriteByte(',') + + if node.RID != "" { + serializedSchain.WriteString(url.QueryEscape(node.RID)) + } + serializedSchain.WriteByte(',') + + if node.Name != "" { + serializedSchain.WriteString(url.QueryEscape(node.Name)) + } + serializedSchain.WriteByte(',') + + if node.Domain != "" { + serializedSchain.WriteString(url.QueryEscape(node.Domain)) + } + if node.Ext != nil { + serializedSchain.WriteByte(',') + serializedSchain.WriteString(url.QueryEscape(string(node.Ext))) + } + } + return serializedSchain.String() +} diff --git a/openrtb_ext/supplyChain_test.go b/openrtb_ext/supplyChain_test.go index 12fd5c337fb..6b45df1fb09 100644 --- a/openrtb_ext/supplyChain_test.go +++ b/openrtb_ext/supplyChain_test.go @@ -81,3 +81,214 @@ func TestCloneSupplyChain(t *testing.T) { }) } } + +func TestSerializeSupplyChain(t *testing.T) { + type args struct { + schain *openrtb2.SupplyChain + } + tests := []struct { + name string + args args + want string + }{ + { + name: "single hop - chain complete", + args: args{schain: &openrtb2.SupplyChain{ + Complete: 1, + Ver: "1.0", + Nodes: []openrtb2.SupplyChainNode{ + { + ASI: "exchange1.com", + SID: "1234", + RID: "bid-request-1", + Name: "publisher", + Domain: "publisher.com", + HP: openrtb2.Int8Ptr(1), + }, + }}}, + want: "1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com", + }, + { + name: "multiple hops - with all properties supplied", + args: args{schain: &openrtb2.SupplyChain{ + Complete: 1, + Ver: "1.0", + Nodes: []openrtb2.SupplyChainNode{ + { + ASI: "exchange1.com", + SID: "1234", + HP: openrtb2.Int8Ptr(1), + RID: "bid-request-1", + Name: "publisher", + Domain: "publisher.com", + }, + { + ASI: "exchange2.com", + SID: "abcd", + HP: openrtb2.Int8Ptr(1), + RID: "bid-request-2", + Name: "intermediary", + Domain: "intermediary.com", + }, + }}}, + want: "1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com!exchange2.com,abcd,1,bid-request-2,intermediary,intermediary.com", + }, + { + name: "single hop - chain incomplete", + args: args{schain: &openrtb2.SupplyChain{ + Complete: 0, + Ver: "1.0", + Nodes: []openrtb2.SupplyChainNode{ + { + ASI: "exchange1.com", + SID: "1234", + RID: "bid-request-1", + Name: "publisher", + HP: openrtb2.Int8Ptr(1), + }, + }}}, + want: "1.0,0!exchange1.com,1234,1,bid-request-1,publisher,", + }, + { + name: "single hop - chain complete, encoded values", + args: args{schain: &openrtb2.SupplyChain{ + Complete: 1, + Ver: "1.0", + Nodes: []openrtb2.SupplyChainNode{ + { + ASI: "exchange1.com", + SID: "1234!abcd", + HP: openrtb2.Int8Ptr(1), + RID: "bid-request-1", + Name: "publisher, Inc.", + Domain: "publisher.com", + }, + }}}, + want: "1.0,1!exchange1.com,1234%21abcd,1,bid-request-1,publisher%2C+Inc.,publisher.com", + }, + { + name: "multiple hops - with all properties supplied,encoded values", + args: args{schain: &openrtb2.SupplyChain{ + Complete: 1, + Ver: "1.0", + Nodes: []openrtb2.SupplyChainNode{ + { + ASI: "exchange1.com", + SID: "1234&abcd", + HP: openrtb2.Int8Ptr(1), + RID: "bid-request-1", + Name: "publisher?name", + Domain: "publisher.com", + }, + { + ASI: "exchange2.com", + SID: "abcd?12345", + HP: openrtb2.Int8Ptr(1), + RID: "bid-request-2", + Name: "intermediary", + Domain: "intermediary.com", + }, + }}}, + want: "1.0,1!exchange1.com,1234%26abcd,1,bid-request-1,publisher%3Fname,publisher.com!exchange2.com,abcd%3F12345,1,bid-request-2,intermediary,intermediary.com", + }, + { + name: "single hop - chain complete, missing optional field - encoded values", + args: args{schain: &openrtb2.SupplyChain{ + Complete: 1, + Ver: "1.0", + Nodes: []openrtb2.SupplyChainNode{ + { + ASI: "exchange1.com", + SID: "1234&&abcd", + HP: openrtb2.Int8Ptr(1), + RID: "bid-request-1", + Name: "publisher, Inc.", + }, + }}}, + want: "1.0,1!exchange1.com,1234%26%26abcd,1,bid-request-1,publisher%2C+Inc.,", + }, + { + name: "single hop - chain complete, missing domain field - encoded values", + args: args{schain: &openrtb2.SupplyChain{ + Complete: 1, + Ver: "1.0", + Nodes: []openrtb2.SupplyChainNode{ + { + ASI: "exchange1.com", + SID: "1234!abcd", + HP: openrtb2.Int8Ptr(1), + RID: "bid-request-1", + Name: "publisher, Inc.", + }, + }}}, + want: "1.0,1!exchange1.com,1234%21abcd,1,bid-request-1,publisher%2C+Inc.,", + }, + { + name: "single hop with extension - chain complete", + args: args{schain: &openrtb2.SupplyChain{ + Complete: 1, + Ver: "1.0", + Nodes: []openrtb2.SupplyChainNode{ + { + ASI: "exchange1.com", + SID: "1234", + RID: "bid-request-1", + Name: "publisher", + Domain: "publisher.com", + HP: openrtb2.Int8Ptr(1), + Ext: []byte(`{"test":1,"url":"https://testuser.com?k1=v1&k2=v2"}`), + }, + }}}, + want: "1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com,%7B%22test%22%3A1%2C%22url%22%3A%22https%3A%2F%2Ftestuser.com%3Fk1%3Dv1%26k2%3Dv2%22%7D", + }, + { + name: "single hop with encoded extension - chain complete", + args: args{schain: &openrtb2.SupplyChain{ + Complete: 1, + Ver: "1.0", + Nodes: []openrtb2.SupplyChainNode{ + { + ASI: "exchange1.com", + SID: "1234", + RID: "bid-request-1", + Name: "publisher", + Domain: "publisher.com", + HP: openrtb2.Int8Ptr(1), + Ext: []byte(`%7B%22test%22%3A1%7D`), + }, + }}}, + want: "1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com,%257B%2522test%2522%253A1%257D", + }, + { + name: "multiple hops with extension - some optional properties not supplied", + args: args{schain: &openrtb2.SupplyChain{ + Complete: 1, + Ver: "1.0", + Nodes: []openrtb2.SupplyChainNode{ + { + ASI: "exchange1.com", + SID: "1234", + HP: openrtb2.Int8Ptr(1), + RID: "bid-request-1", + Domain: "publisher.com", + Ext: []byte(`{"test1":1,"name":"test","age":22}`), + }, + { + ASI: "exchange2.com", + SID: "abcd", + HP: openrtb2.Int8Ptr(1), + RID: "bid-request-2", + Name: "intermediary", + Ext: []byte(`{"test"}`), + }, + }}}, + want: "1.0,1!exchange1.com,1234,1,bid-request-1,,publisher.com,%7B%22test1%22%3A1%2C%22name%22%3A%22test%22%2C%22age%22%3A22%7D!exchange2.com,abcd,1,bid-request-2,intermediary,,%7B%22test%22%7D", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := SerializeSupplyChain(tt.args.schain) + assert.Equal(t, tt.want, got, tt.name) + }) + } +} From f46d88f7e362b8f723c766693450a1f4d0034de0 Mon Sep 17 00:00:00 2001 From: Ankit-Pinge <128145896+Ankit-Pinge@users.noreply.github.com> Date: Tue, 7 Nov 2023 14:28:23 +0530 Subject: [PATCH 18/30] OTT-1412(Bug Fix) : Retain the encoded query parameter (#639) --- adapters/vastbidder/bidder_macro_test.go | 2 +- adapters/vastbidder/util.go | 7 ++++++- adapters/vastbidder/util_test.go | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/adapters/vastbidder/bidder_macro_test.go b/adapters/vastbidder/bidder_macro_test.go index d1d41050e5a..ddf2493e22e 100644 --- a/adapters/vastbidder/bidder_macro_test.go +++ b/adapters/vastbidder/bidder_macro_test.go @@ -1475,7 +1475,7 @@ func TestBidderMacroKV(t *testing.T) { "url": "http%3A%2F%2Fexample.com%3Fk1%3Dv1%26k2%3Dv2", }}, args: args{key: "kv"}, - want: "age=22&url=http%253A%252F%252Fexample.com%253Fk1%253Dv1%2526k2%253Dv2", + want: "age=22&url=http%3A%2F%2Fexample.com%3Fk1%3Dv1%26k2%3Dv2", }, { name: "empty_KV_map", diff --git a/adapters/vastbidder/util.go b/adapters/vastbidder/util.go index c1ddb53fb4a..59877913a96 100644 --- a/adapters/vastbidder/util.go +++ b/adapters/vastbidder/util.go @@ -122,7 +122,12 @@ func mapToQuery(m map[string]any) string { values.Add(key, mapToQuery(mvalue)) } default: - values.Add(key, fmt.Sprintf("%v", value)) + v := fmt.Sprintf("%v", value) + decodedString, err := url.QueryUnescape(v) + if err == nil { + v = decodedString + } + values.Add(key, v) } } return values.Encode() diff --git a/adapters/vastbidder/util_test.go b/adapters/vastbidder/util_test.go index e8c8681c19f..c011698d4a9 100644 --- a/adapters/vastbidder/util_test.go +++ b/adapters/vastbidder/util_test.go @@ -95,7 +95,7 @@ func Test_getValueFromMap(t *testing.T) { want: "", }, { - name: "empty_m", + name: "empty_map", args: args{lookUpOrder: []string{"country", "name"}, m: map[string]any{}, }, From f357d5fa2bb813560472a0d0797cc36d646284bd Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Tue, 7 Nov 2023 15:19:46 +0530 Subject: [PATCH 19/30] OTT-1418: fix NBR code sequence (#640) --- .../pubmatic/openwrap/beforevalidationhook.go | 2 +- .../openwrap/beforevalidationhook_test.go | 6 +++--- modules/pubmatic/openwrap/entrypointhook.go | 4 ++-- modules/pubmatic/openwrap/models/nbr/codes.go | 19 ++++++------------- modules/pubmatic/openwrap/util.go | 2 +- modules/pubmatic/openwrap/util_test.go | 2 +- 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go index 37ee37eca71..4776c372bea 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook.go +++ b/modules/pubmatic/openwrap/beforevalidationhook.go @@ -62,7 +62,7 @@ func (m OpenWrap) handleBeforeValidationHook( requestExt, err := models.GetRequestExt(payload.BidRequest.Ext) if err != nil { - result.NbrCode = nbr.InvalidRequest + result.NbrCode = nbr.InvalidRequestExt err = errors.New("failed to get request ext: " + err.Error()) result.Errors = append(result.Errors, err.Error()) return result, err diff --git a/modules/pubmatic/openwrap/beforevalidationhook_test.go b/modules/pubmatic/openwrap/beforevalidationhook_test.go index f45846f1979..62c056b8746 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook_test.go +++ b/modules/pubmatic/openwrap/beforevalidationhook_test.go @@ -1668,12 +1668,12 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { }, setup: func() { mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") - mockEngine.EXPECT().RecordBadRequests(rctx.Endpoint, getPubmaticErrorCode(nbr.InvalidRequest)) - mockEngine.EXPECT().RecordNobidErrPrebidServerRequests("5890", nbr.InvalidRequest) + mockEngine.EXPECT().RecordBadRequests(rctx.Endpoint, getPubmaticErrorCode(nbr.InvalidRequestExt)) + mockEngine.EXPECT().RecordNobidErrPrebidServerRequests("5890", nbr.InvalidRequestExt) }, want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ Reject: true, - NbrCode: nbr.InvalidRequest, + NbrCode: nbr.InvalidRequestExt, Errors: []string{"failed to get request ext: failed to decode request.ext : json: cannot unmarshal number into Go value of type models.RequestExt"}, }, wantErr: true, diff --git a/modules/pubmatic/openwrap/entrypointhook.go b/modules/pubmatic/openwrap/entrypointhook.go index 14d0d38d4df..87ed5813699 100644 --- a/modules/pubmatic/openwrap/entrypointhook.go +++ b/modules/pubmatic/openwrap/entrypointhook.go @@ -82,12 +82,12 @@ func (m OpenWrap) handleEntrypointHook( result.Reject = true if err != nil { - result.NbrCode = nbr.InvalidRequest + result.NbrCode = nbr.InvalidRequestWrapperExtension result.Errors = append(result.Errors, "InvalidRequest") return result, err } - if requestExtWrapper.ProfileId == 0 { + if requestExtWrapper.ProfileId <= 0 { result.NbrCode = nbr.InvalidProfileID result.Errors = append(result.Errors, "ErrMissingProfileID") return result, err diff --git a/modules/pubmatic/openwrap/models/nbr/codes.go b/modules/pubmatic/openwrap/models/nbr/codes.go index fc64dea733d..aeacbaea6d0 100644 --- a/modules/pubmatic/openwrap/models/nbr/codes.go +++ b/modules/pubmatic/openwrap/models/nbr/codes.go @@ -1,25 +1,18 @@ package nbr const ( - // Refer below link for standard codes. - // https://github.com/InteractiveAdvertisingBureau/openrtb/blob/2c3bf2bb2bc81ce0b5260f2e82c59938ea05b74a/extensions/community_extensions/seat-non-bid.md#list-non-bid-status-codes - - // Internal Technical Error - InternalError int = 1 - // Invalid Request - InvalidRequest int = 2 - // 500+ Vendor-specific codes. // 5xx already in use by seat non bid. https://github.com/PubMatic-OpenWrap/prebid-openrtb/blob/main/openrtb3/non_bid_status_code.go#L53 - InvalidRequestWrapperExtension int = 600 + iota - InvalidPublisherID + InvalidRequestWrapperExtension int = 601 + iota InvalidProfileID + InvalidPublisherID + InvalidRequestExt InvalidProfileConfiguration + InvalidPlatform AllPartnerThrottled InvalidPriceGranularityConfig InvalidImpressionTagID - ServerSidePartnerNotConfigured + InternalError AllSlotsDisabled - InvalidVideoRequest - InvalidPlatform + ServerSidePartnerNotConfigured ) diff --git a/modules/pubmatic/openwrap/util.go b/modules/pubmatic/openwrap/util.go index d4d81110e81..e2522ef6ca8 100644 --- a/modules/pubmatic/openwrap/util.go +++ b/modules/pubmatic/openwrap/util.go @@ -261,7 +261,7 @@ func getPubmaticErrorCode(standardNBR int) int { case nbr.InvalidPublisherID: return 604 // ErrMissingPublisherID - case nbr.InvalidRequest: + case nbr.InvalidRequestExt: return 18 // ErrBadRequest case nbr.InvalidProfileID: diff --git a/modules/pubmatic/openwrap/util_test.go b/modules/pubmatic/openwrap/util_test.go index 2e2ec196046..ef76716d3f5 100644 --- a/modules/pubmatic/openwrap/util_test.go +++ b/modules/pubmatic/openwrap/util_test.go @@ -898,7 +898,7 @@ func TestGetPubmaticErrorCode(t *testing.T) { { name: "ErrBadRequest", args: args{ - standardNBR: nbr.InvalidRequest, + standardNBR: nbr.InvalidRequestExt, }, want: 18, }, From 32c62e17bc473024dccd031b35019b1204beec71 Mon Sep 17 00:00:00 2001 From: pm-avinash-kapre <112699665+AvinashKapre@users.noreply.github.com> Date: Tue, 7 Nov 2023 15:44:20 +0530 Subject: [PATCH 20/30] UOE-9681,UOE-9670: Add OWS hosted PBS vanilla endpoint, for prebid.js DIY client (#583) --- .../pubmatic/openwrap/auctionresponsehook.go | 9 + .../pubmatic/openwrap/beforevalidationhook.go | 53 ++- .../openwrap/beforevalidationhook_test.go | 220 ++++++++++++- modules/pubmatic/openwrap/entrypointhook.go | 57 ++-- .../pubmatic/openwrap/entrypointhook_test.go | 302 +++++++++++++++++- modules/pubmatic/openwrap/models/constants.go | 2 + modules/pubmatic/openwrap/models/openwrap.go | 3 +- modules/pubmatic/openwrap/models/request.go | 3 +- 8 files changed, 598 insertions(+), 51 deletions(-) diff --git a/modules/pubmatic/openwrap/auctionresponsehook.go b/modules/pubmatic/openwrap/auctionresponsehook.go index 0d4b2d72d16..b678413ad9f 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook.go +++ b/modules/pubmatic/openwrap/auctionresponsehook.go @@ -34,6 +34,15 @@ func (m OpenWrap) handleAuctionResponseHook( result.DebugMessages = append(result.DebugMessages, "error: request-ctx not found in handleBeforeValidationHook()") return result, nil } + + //SSHB request should not execute module + 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 m.metricEngine.RecordPublisherResponseTimeStats(rctx.PubIDStr, int(time.Since(time.Unix(rctx.StartTime, 0)).Milliseconds())) diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go index 4776c372bea..3256cda3a29 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook.go +++ b/modules/pubmatic/openwrap/beforevalidationhook.go @@ -7,6 +7,7 @@ import ( "fmt" "net/url" "strconv" + "strings" "github.com/buger/jsonparser" "github.com/prebid/openrtb/v19/openrtb2" @@ -46,6 +47,18 @@ func (m OpenWrap) handleBeforeValidationHook( } }() + //Do not execute the module for requests processed in SSHB(8001) + if rCtx.Sshb == "1" { + result.Reject = false + return result, nil + } + + if rCtx.Endpoint == models.EndpointHybrid { + //TODO: Add bidder params fix + result.Reject = false + return result, nil + } + pubID, err := getPubID(*payload.BidRequest) if err != nil { result.NbrCode = nbr.InvalidPublisherID @@ -154,6 +167,19 @@ func (m OpenWrap) handleBeforeValidationHook( var isAdPodImpression bool imp := payload.BidRequest.Imp[i] + impExt := &models.ImpExtension{} + if len(imp.Ext) != 0 { + err := json.Unmarshal(imp.Ext, impExt) + if err != nil { + result.NbrCode = nbr.InternalError + err = errors.New("failed to parse imp.ext: " + imp.ID) + result.Errors = append(result.Errors, err.Error()) + return result, err + } + } + if rCtx.Endpoint == models.EndpointOWS2S { + imp.TagID = getTagID(imp, impExt) + } if imp.TagID == "" { result.NbrCode = nbr.InvalidImpressionTagID err = errors.New("tagid missing for imp: " + imp.ID) @@ -184,17 +210,6 @@ func (m OpenWrap) handleBeforeValidationHook( } } - impExt := &models.ImpExtension{} - if len(imp.Ext) != 0 { - err := json.Unmarshal(imp.Ext, impExt) - if err != nil { - result.NbrCode = nbr.InternalError - err = errors.New("failed to parse imp.ext: " + imp.ID) - result.Errors = append(result.Errors, err.Error()) - return result, err - } - } - div := "" if impExt.Wrapper != nil { div = impExt.Wrapper.Div @@ -439,7 +454,7 @@ func (m *OpenWrap) applyProfileChanges(rctx models.RequestCtx, bidRequest *openr } if cur, ok := rctx.PartnerConfigMap[models.VersionLevelConfigID][models.AdServerCurrency]; ok { - bidRequest.Cur = []string{cur} + bidRequest.Cur = append(bidRequest.Cur, cur) } if bidRequest.TMax == 0 { bidRequest.TMax = rctx.TMax @@ -797,7 +812,6 @@ func (m OpenWrap) setTimeout(rCtx models.RequestCtx) int64 { // 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 { - //if ssauction is set to 0 in the request if rctx.SSAuction == 0 { return true @@ -844,3 +858,16 @@ func getPubID(bidRequest openrtb2.BidRequest) (pubID int, err error) { } return pubID, err } + +func getTagID(imp openrtb2.Imp, impExt *models.ImpExtension) string { + //priority for tagId is imp.ext.gpid > imp.TagID > imp.ext.data.pbadslot + if impExt.Gpid != "" { + if idx := strings.Index(impExt.Gpid, "#"); idx != -1 { + return impExt.Gpid[:idx] + } + return impExt.Gpid + } else if imp.TagID != "" { + return imp.TagID + } + return impExt.Data.PbAdslot +} diff --git a/modules/pubmatic/openwrap/beforevalidationhook_test.go b/modules/pubmatic/openwrap/beforevalidationhook_test.go index 62c056b8746..e6922046ce0 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook_test.go +++ b/modules/pubmatic/openwrap/beforevalidationhook_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "errors" - "fmt" "net/http" "testing" @@ -95,7 +94,7 @@ func getTestBidRequest(isSite bool) *openrtb2.BidRequest { }, } } - testReq.Cur = []string{} + testReq.Cur = []string{"EUR"} testReq.WLang = []string{"english", "hindi"} testReq.Device = &openrtb2.Device{ DeviceType: 1, @@ -830,7 +829,7 @@ func TestOpenWrap_applyProfileChanges(t *testing.T) { want: &openrtb2.BidRequest{ ID: "testID", Test: 1, - Cur: []string{"USD"}, + Cur: []string{"EUR", "USD"}, TMax: 500, Source: &openrtb2.Source{ TID: "testID", @@ -893,7 +892,7 @@ func TestOpenWrap_applyProfileChanges(t *testing.T) { want: &openrtb2.BidRequest{ ID: "testID", Test: 1, - Cur: []string{"USD"}, + Cur: []string{"EUR", "USD"}, TMax: 500, Source: &openrtb2.Source{ TID: "testID", @@ -1589,6 +1588,22 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { isRequestNotRejected bool wantErr bool }{ + { + name: "request_with_sshb=1", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + Sshb: "1", + }, + }, + }, + }, + want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ + Reject: false, + }, + }, { name: "empty_module_context", args: args{ @@ -1625,6 +1640,23 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { }, wantErr: false, }, + { + name: "hybrid_request_module_should_not_reject_request_and_return_without_executing_module", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + Endpoint: models.EndpointHybrid, + }, + }, + }, + }, + want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ + Reject: false, + }, + wantErr: false, + }, { name: "Invalid_PubID_in_request", args: args{ @@ -1841,7 +1873,7 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { wantErr: false, }, { - name: "TagID_not_prsent_in_imp", + name: "TagID_not_present_in_imp", args: args{ ctx: context.Background(), moduleCtx: hookstage.ModuleInvocationContext{ @@ -1884,6 +1916,54 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { }, wantErr: true, }, + { + name: "TagID_not_present_in_imp_and_not_found_for_client_request", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": func() models.RequestCtx { + testRctx := rctx + testRctx.Endpoint = models.EndpointOWS2S + return testRctx + }(), + }, + }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"ext":{"wrapper":{"div":"div"},"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{}) + //prometheus metrics + mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") + mockEngine.EXPECT().RecordBadRequests(models.EndpointOWS2S, getPubmaticErrorCode(nbr.InvalidImpressionTagID)) + mockEngine.EXPECT().RecordNobidErrPrebidServerRequests("5890", nbr.InvalidImpressionTagID) + mockEngine.EXPECT().RecordPublisherRequests(models.EndpointOWS2S, "5890", rctx.Platform) + }, + want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ + Reject: true, + NbrCode: nbr.InvalidImpressionTagID, + Errors: []string{"tagid missing for imp: 123"}, + }, + wantErr: true, + }, { name: "invalid_impExt", args: args{ @@ -2198,7 +2278,6 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { assert.NotEmpty(t, tt.want.DebugMessages) return } - fmt.Println(got.DebugMessages) if (err != nil) != tt.wantErr { assert.Equal(t, tt.wantErr, err != nil) return @@ -2207,3 +2286,132 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { }) } } + +func TestGetTagID(t *testing.T) { + type args struct { + imp openrtb2.Imp + impExt *models.ImpExtension + } + tests := []struct { + name string + args args + want string + }{ + { + name: "tagId_not_found", + args: args{ + imp: openrtb2.Imp{}, + impExt: &models.ImpExtension{}, + }, + want: "", + }, + { + name: "tagId_present_in_gpid", + args: args{ + imp: openrtb2.Imp{}, + impExt: &models.ImpExtension{ + Gpid: "/7578294/adunit1", + }, + }, + want: "/7578294/adunit1", + }, + { + name: "tagId_set_by_publisher_on_page", + args: args{ + imp: openrtb2.Imp{ + TagID: "/7578294/adunit1", + }, + impExt: &models.ImpExtension{}, + }, + want: "/7578294/adunit1", + }, + { + name: "tagId_present_in_pbadslot", + args: args{ + imp: openrtb2.Imp{}, + impExt: &models.ImpExtension{ + Data: openrtb_ext.ExtImpData{ + PbAdslot: "/7578294/adunit1", + }, + }, + }, + want: "/7578294/adunit1", + }, + { + name: "tagId_present_in_pbadslot_and_gpid", + args: args{ + imp: openrtb2.Imp{}, + impExt: &models.ImpExtension{ + Gpid: "/7578294/adunit123", + Data: openrtb_ext.ExtImpData{ + PbAdslot: "/7578294/adunit", + }, + }, + }, + want: "/7578294/adunit123", + }, + { + name: "tagId_present_in_imp.TagId_and_gpid", + args: args{ + imp: openrtb2.Imp{ + TagID: "/7578294/adunit", + }, + impExt: &models.ImpExtension{ + Gpid: "/7578294/adunit123", + }, + }, + want: "/7578294/adunit123", + }, + { + name: "tagId_present_in_imp.TagId_and_pbadslot", + args: args{ + imp: openrtb2.Imp{ + TagID: "/7578294/adunit123", + }, + impExt: &models.ImpExtension{ + Data: openrtb_ext.ExtImpData{ + PbAdslot: "/7578294/adunit", + }, + }, + }, + want: "/7578294/adunit123", + }, + { + name: "tagId_present_in_imp.TagId_and_pbadslot_and_gpid", + args: args{ + imp: openrtb2.Imp{ + TagID: "/7578294/adunit", + }, + impExt: &models.ImpExtension{ + Gpid: "/7578294/adunit123", + Data: openrtb_ext.ExtImpData{ + PbAdslot: "/7578294/adunit12345", + }, + }, + }, + want: "/7578294/adunit123", + }, + { + name: "GpId_contains_'#'", + args: args{ + imp: openrtb2.Imp{ + TagID: "/7578294/adunit", + }, + impExt: &models.ImpExtension{ + Gpid: "/43743431/DMDemo#Div1", + Data: openrtb_ext.ExtImpData{ + PbAdslot: "/7578294/adunit12345", + }, + }, + }, + want: "/43743431/DMDemo", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getTagID(tt.args.imp, tt.args.impExt); got != tt.want { + t.Errorf("getTagID() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/modules/pubmatic/openwrap/entrypointhook.go b/modules/pubmatic/openwrap/entrypointhook.go index 87ed5813699..a4c25a460f6 100644 --- a/modules/pubmatic/openwrap/entrypointhook.go +++ b/modules/pubmatic/openwrap/entrypointhook.go @@ -5,6 +5,7 @@ import ( "strconv" "time" + "github.com/buger/jsonparser" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/hooks/hookexecution" "github.com/prebid/prebid-server/hooks/hookstage" @@ -30,25 +31,47 @@ func (m OpenWrap) handleEntrypointHook( _ context.Context, miCtx hookstage.ModuleInvocationContext, payload hookstage.EntrypointPayload, -) (hookstage.HookResult[hookstage.EntrypointPayload], error) { - result := hookstage.HookResult[hookstage.EntrypointPayload]{} +) (result hookstage.HookResult[hookstage.EntrypointPayload], err error) { queryParams := payload.Request.URL.Query() - if queryParams.Get("sshb") != "1" { - return result, nil - } + source := queryParams.Get("source") //source query param to identify /openrtb2/auction type - var pubid int + rCtx := models.RequestCtx{} var endpoint string - var err error + var pubid int var requestExtWrapper models.RequestExtWrapper + defer func() { + if result.Reject { + m.metricEngine.RecordBadRequests(endpoint, getPubmaticErrorCode(result.NbrCode)) + } else { + result.ModuleContext = make(hookstage.ModuleContext) + result.ModuleContext["rctx"] = rCtx + } + }() + + rCtx.Sshb = queryParams.Get("sshb") + //Do not execute the module for requests processed in SSHB(8001) + if queryParams.Get("sshb") == "1" { + return result, nil + } + switch payload.Request.URL.Path { + // Direct call to 8000 port case hookexecution.EndpointAuction: - if !models.IsHybrid(payload.Body) { // new hybrid api should not execute module + switch source { + case "pbjs": + endpoint = models.EndpointOWS2S + requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body) + case "inapp": + requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body, "ext", "wrapper") + endpoint = models.EndpointV25 + default: + rCtx.Endpoint = models.EndpointHybrid return result, nil } - requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body) + // call to 8001 port and here via reverse proxy case OpenWrapAuction: // legacy hybrid api should not execute module - m.metricEngine.RecordPBSAuctionRequestsStats() + // m.metricEngine.RecordPBSAuctionRequestsStats() //TODO: uncomment after hybrid call through module + rCtx.Endpoint = models.EndpointHybrid return result, nil case OpenWrapV25: requestExtWrapper, err = models.GetRequestExtWrapper(payload.Body, "ext", "wrapper") @@ -72,12 +95,6 @@ func (m OpenWrap) handleEntrypointHook( // we should return from here } - defer func() { - if result.Reject { - m.metricEngine.RecordBadRequests(endpoint, getPubmaticErrorCode(result.NbrCode)) - } - }() - // init default for all modules result.Reject = true @@ -93,9 +110,10 @@ func (m OpenWrap) handleEntrypointHook( return result, err } - rCtx := models.RequestCtx{ + requestDebug, _ := jsonparser.GetBoolean(payload.Body, "ext", "prebid", "debug") + rCtx = models.RequestCtx{ StartTime: time.Now().Unix(), - Debug: queryParams.Get(models.Debug) == "1", + Debug: queryParams.Get(models.Debug) == "1" || requestDebug, UA: payload.Request.Header.Get("User-Agent"), ProfileID: requestExtWrapper.ProfileId, DisplayID: requestExtWrapper.VersionId, @@ -138,9 +156,6 @@ func (m OpenWrap) handleEntrypointHook( rCtx.PubID = pubid } - result.ModuleContext = make(hookstage.ModuleContext) - result.ModuleContext["rctx"] = rCtx - result.Reject = false return result, nil } diff --git a/modules/pubmatic/openwrap/entrypointhook_test.go b/modules/pubmatic/openwrap/entrypointhook_test.go index 2f64e6a3639..a763e1f6618 100644 --- a/modules/pubmatic/openwrap/entrypointhook_test.go +++ b/modules/pubmatic/openwrap/entrypointhook_test.go @@ -39,13 +39,13 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { wantErr error }{ { - name: "sshb absent", + name: "request with sshb=1 should not execute entrypointhook", args: args{ in0: context.Background(), miCtx: hookstage.ModuleInvocationContext{}, payload: hookstage.EntrypointPayload{ Request: func() *http.Request { - r, err := http.NewRequest("POST", "http://localhost/openrtb/2.5?debug=1", nil) + r, err := http.NewRequest("POST", "http://localhost/openrtb/2.5?sshb=1", nil) if err != nil { panic(err) } @@ -58,10 +58,16 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { }, setup: func(mme *mock_metrics.MockMetricsEngine) {}, }, - want: hookstage.HookResult[hookstage.EntrypointPayload]{}, + want: hookstage.HookResult[hookstage.EntrypointPayload]{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + Sshb: "1", + }, + }, + }, }, { - name: "valid /openrtb/2.5 request", + name: "valid /openrtb2/2.5 request(reverseProxy through SSHB(8001->8000))", fields: fields{ cfg: config.Config{ Tracker: config.Tracker{ @@ -76,7 +82,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { miCtx: hookstage.ModuleInvocationContext{}, payload: hookstage.EntrypointPayload{ Request: func() *http.Request { - r, err := http.NewRequest("POST", "http://localhost/openrtb/2.5?debug=1&sshb=1", nil) + r, err := http.NewRequest("POST", "http://localhost/openrtb/2.5?debug=1&sshb=2", nil) if err != nil { panic(err) } @@ -124,7 +130,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { wantErr: nil, }, { - name: "valid /openrtb/2.5 request with wiid set and no cookies", + name: "valid /openrtb/2.5 request with wiid set and no cookies(reverseProxy through SSHB(8001->8000)", fields: fields{ cfg: config.Config{ Tracker: config.Tracker{ @@ -139,7 +145,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { miCtx: hookstage.ModuleInvocationContext{}, payload: hookstage.EntrypointPayload{ Request: func() *http.Request { - r, err := http.NewRequest("POST", "http://localhost/openrtb/2.5?debug=1&sshb=1", nil) + r, err := http.NewRequest("POST", "http://localhost/openrtb/2.5?debug=1&sshb=2", nil) if err != nil { panic(err) } @@ -178,7 +184,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { wantErr: nil, }, { - name: "/openrtb/2.5 request without profileid", + name: "/openrtb/2.5 request without profileid(reverseProxy through SSHB(8001->8000)", fields: fields{ cfg: config.Config{}, cache: nil, @@ -188,7 +194,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { miCtx: hookstage.ModuleInvocationContext{}, payload: hookstage.EntrypointPayload{ Request: func() *http.Request { - r, err := http.NewRequest("POST", "http://localhost/openrtb/2.5?&sshb=1", nil) + r, err := http.NewRequest("POST", "http://localhost/openrtb/2.5?&sshb=2", nil) if err != nil { panic(err) } @@ -207,6 +213,280 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { }, wantErr: nil, }, + { + name: "Valid /openrtb2/auction?source=pbjs request(ows2s)", + fields: fields{ + cfg: config.Config{}, + cache: nil, + }, + args: args{ + in0: context.Background(), + miCtx: hookstage.ModuleInvocationContext{}, + payload: hookstage.EntrypointPayload{ + Request: func() *http.Request { + r, err := http.NewRequest("POST", "http://localhost/openrtb2/auction?source=pbjs&debug=1", nil) + if err != nil { + panic(err) + } + r.Header.Add("User-Agent", "go-test") + r.Header.Add("SOURCE_IP", "127.0.0.1") + r.Header.Add("Cookie", `KADUSERCOOKIE=7D75D25F-FAC9-443D-B2D1-B17FEE11E027; DPSync3=1684886400%3A248%7C1685491200%3A245_226_201; KRTBCOOKIE_80=16514-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&22987-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23025-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23386-CAESEMih0bN7ISRdZT8xX8LXzEw; KRTBCOOKIE_377=6810-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&22918-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&23031-59dc50c9-d658-44ce-b442-5a1f344d97c0; uids=eyJ0ZW1wVUlEcyI6eyIzM2Fjcm9zcyI6eyJ1aWQiOiIxMTkxNzkxMDk5Nzc2NjEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTo0My4zODg4Nzk5NVoifSwiYWRmIjp7InVpZCI6IjgwNDQ2MDgzMzM3Nzg4MzkwNzgiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMS4wMzMwNTQ3MjdaIn0sImFka2VybmVsIjp7InVpZCI6IkE5MTYzNTAwNzE0OTkyOTMyOTkwIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuMzczMzg1NjYyWiJ9LCJhZGtlcm5lbEFkbiI6eyJ1aWQiOiJBOTE2MzUwMDcxNDk5MjkzMjk5MCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEzLjQzNDkyNTg5NloifSwiYWRtaXhlciI6eyJ1aWQiOiIzNjZhMTdiMTJmMjI0ZDMwOGYzZTNiOGRhOGMzYzhhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjU5MjkxNDgwMVoifSwiYWRueHMiOnsidWlkIjoiNDE5Mjg5ODUzMDE0NTExOTMiLCJleHBpcmVzIjoiMjAyMy0wMS0xOFQwOTo1MzowOC44MjU0NDI2NzZaIn0sImFqYSI6eyJ1aWQiOiJzMnN1aWQ2RGVmMFl0bjJveGQ1aG9zS1AxVmV3IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTMuMjM5MTc2MDU0WiJ9LCJlcGxhbm5pbmciOnsidWlkIjoiQUoxRjBTOE5qdTdTQ0xWOSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjkyOTk2MDQ3M1oifSwiZ2Ftb3NoaSI6eyJ1aWQiOiJndXNyXzM1NmFmOWIxZDhjNjQyYjQ4MmNiYWQyYjdhMjg4MTYxIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuNTI0MTU3MjI1WiJ9LCJncmlkIjp7InVpZCI6IjRmYzM2MjUwLWQ4NTItNDU5Yy04NzcyLTczNTZkZTE3YWI5NyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE0LjY5NjMxNjIyN1oifSwiZ3JvdXBtIjp7InVpZCI6IjdENzVEMjVGLUZBQzktNDQzRC1CMkQxLUIxN0ZFRTExRTAyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjM5LjIyNjIxMjUzMloifSwiaXgiOnsidWlkIjoiWW9ORlNENlc5QkphOEh6eEdtcXlCUUFBXHUwMDI2Mjk3IiwiZXhwaXJlcyI6IjIwMjMtMDUtMzFUMDc6NTM6MzguNTU1ODI3MzU0WiJ9LCJqaXhpZSI6eyJ1aWQiOiI3MzY3MTI1MC1lODgyLTExZWMtYjUzOC0xM2FjYjdhZjBkZTQiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi4xOTEwOTk3MzJaIn0sImxvZ2ljYWQiOnsidWlkIjoiQVZ4OVROQS11c25pa3M4QURzTHpWa3JvaDg4QUFBR0JUREh0UUEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS40NTUxNDk2MTZaIn0sIm1lZGlhbmV0Ijp7InVpZCI6IjI5Nzg0MjM0OTI4OTU0MTAwMDBWMTAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMy42NzIyMTUxMjhaIn0sIm1naWQiOnsidWlkIjoibTU5Z1hyN0xlX1htIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTcuMDk3MDAxNDcxWiJ9LCJuYW5vaW50ZXJhY3RpdmUiOnsidWlkIjoiNmFlYzhjMTAzNzlkY2I3ODQxMmJjODBiNmRkOWM5NzMxNzNhYjdkNzEyZTQzMWE1YTVlYTcwMzRlNTZhNThhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE2LjcxNDgwNzUwNVoifSwib25ldGFnIjp7InVpZCI6IjdPelZoVzFOeC1LOGFVak1HMG52NXVNYm5YNEFHUXZQbnVHcHFrZ3k0ckEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS4xNDE3NDEyNjJaIn0sIm9wZW54Ijp7InVpZCI6IjVkZWNlNjIyLTBhMjMtMGRhYi0zYTI0LTVhNzcwMTBlNDU4MiIsImV4cGlyZXMiOiIyMDIzLTA1LTMxVDA3OjUyOjQ3LjE0MDQxNzM2M1oifSwicHVibWF0aWMiOnsidWlkIjoiN0Q3NUQyNUYtRkFDOS00NDNELUIyRDEtQjE3RkVFMTFFMDI3IiwiZXhwaXJlcyI6IjIwMjItMTAtMzFUMDk6MTQ6MjUuNzM3MjU2ODk5WiJ9LCJyaWNoYXVkaWVuY2UiOnsidWlkIjoiY2I2YzYzMjAtMzNlMi00Nzc0LWIxNjAtMXp6MTY1NDg0MDc0OSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjUyNTA3NDE4WiJ9LCJzbWFydHlhZHMiOnsidWlkIjoiMTJhZjE1ZTQ0ZjAwZDA3NjMwZTc0YzQ5MTU0Y2JmYmE0Zjg0N2U4ZDRhMTU0YzhjM2Q1MWY1OGNmNzJhNDYyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjgyNTAzMTg4NFoifSwic21pbGV3YW50ZWQiOnsidWlkIjoiZGQ5YzNmZTE4N2VmOWIwOWNhYTViNzExNDA0YzI4MzAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNC4yNTU2MDkzNjNaIn0sInN5bmFjb3JtZWRpYSI6eyJ1aWQiOiJHRFBSIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuOTc5NTgzNDM4WiJ9LCJ0cmlwbGVsaWZ0Ijp7InVpZCI6IjcwMjE5NzUwNTQ4MDg4NjUxOTQ2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA4Ljk4OTY3MzU3NFoifSwidmFsdWVpbXByZXNzaW9uIjp7InVpZCI6IjlkMDgxNTVmLWQ5ZmUtNGI1OC04OThlLWUyYzU2MjgyYWIzZSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjA2NzgzOTE2NFoifSwidmlzeCI6eyJ1aWQiOiIyN2UwYWMzYy1iNDZlLTQxYjMtOTkyYy1mOGQyNzE0OTQ5NWUiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi45ODk1MjM1NzNaIn0sInlpZWxkbGFiIjp7InVpZCI6IjY5NzE0ZDlmLWZiMDAtNGE1Zi04MTljLTRiZTE5MTM2YTMyNSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjExLjMwMzAyNjYxNVoifSwieWllbGRtbyI6eyJ1aWQiOiJnOTZjMmY3MTlmMTU1MWIzMWY2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjExMDUyODYwOVoifSwieWllbGRvbmUiOnsidWlkIjoiMmE0MmZiZDMtMmM3MC00ZWI5LWIxYmQtMDQ2OTY2NTBkOTQ4IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuMzE4MzMzOTM5WiJ9LCJ6ZXJvY2xpY2tmcmF1ZCI6eyJ1aWQiOiJiOTk5NThmZS0yYTg3LTJkYTQtOWNjNC05NjFmZDExM2JlY2UiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNS43MTk1OTQ1NjZaIn19LCJiZGF5IjoiMjAyMi0wNS0xN1QwNjo0ODozOC4wMTc5ODgyMDZaIn0=; KRTBCOOKIE_153=1923-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&19420-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&22979-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&23462-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse; KRTBCOOKIE_57=22776-41928985301451193&KRTB&23339-41928985301451193; KRTBCOOKIE_27=16735-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&16736-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23019-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23114-uid:3cab6283-4546-4500-a7b6-40ef605fe745; KRTBCOOKIE_18=22947-1978557989514665832; KRTBCOOKIE_466=16530-4fc36250-d852-459c-8772-7356de17ab97; KRTBCOOKIE_391=22924-8044608333778839078&KRTB&23263-8044608333778839078&KRTB&23481-8044608333778839078; KRTBCOOKIE_1310=23431-b81c3g7dr67i&KRTB&23446-b81c3g7dr67i&KRTB&23465-b81c3g7dr67i; KRTBCOOKIE_1290=23368-vkf3yv9lbbl; KRTBCOOKIE_22=14911-4554572065121110164&KRTB&23150-4554572065121110164; KRTBCOOKIE_860=16335-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23334-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23417-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23426-YGAqDU1zUTdjyAFxCoe3kctlNPo; KRTBCOOKIE_904=16787-KwJwE7NkCZClNJRysN2iYg; KRTBCOOKIE_1159=23138-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23328-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23427-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23445-5545f53f3d6e4ec199d8ed627ff026f3; KRTBCOOKIE_32=11175-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22713-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22715-AQEI_1QecY2ESAIjEW6KAQEBAQE; SyncRTB3=1685577600%3A35%7C1685491200%3A107_21_71_56_204_247_165_231_233_179_22_209_54_254_238_96_99_220_7_214_13_3_8_234_176_46_5%7C1684886400%3A2_223_15%7C1689465600%3A69%7C1685145600%3A63; KRTBCOOKIE_107=1471-uid:EK38R0PM1NQR0H5&KRTB&23421-uid:EK38R0PM1NQR0H5; KRTBCOOKIE_594=17105-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004&KRTB&17107-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004; SPugT=1684310122; chkChromeAb67Sec=133; KRTBCOOKIE_699=22727-AAFy2k7FBosAAEasbJoXnw; PugT=1684310473; origin=go-test`) + return r + }(), + Body: []byte(`{"imp":[{"tagid":"/43743431/DMDemo","ext":{"prebid":{"bidder":{"pubmatic":{"publisherId":"5890"}},"adunitcode":"div-gpt-ad-1460505748561-0"}},"id":"div-gpt-ad-1460505748561-0","banner":{"topframe":1,"format":[{"w":300,"h":250}]}}],"site":{"domain":"localhost:9999","publisher":{"domain":"localhost:9999","id":"5890"},"page":"http://localhost:9999/integrationExamples/gpt/owServer_example.html"},"device":{"w":1792,"h":446,"dnt":0,"ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36","language":"en","sua":{"source":1,"platform":{"brand":"macOS"},"browsers":[{"brand":"Google Chrome","version":["117"]},{"brand":"Not;A=Brand","version":["8"]},{"brand":"Chromium","version":["117"]}],"mobile":0}},"ext":{"prebid":{"auctiontimestamp":1697191822565,"targeting":{"includewinners":true,"includebidderkeys":true},"bidderparams":{"pubmatic":{"publisherId":"5890","wrapper":{"profileid":43563,"versionid":1}}},"channel":{"name":"pbjs","version":"v8.7.0-pre"},"createtids":false}},"id":"5bdd7da5-1166-40fe-a9cb-3bf3c3164cd3","test":0,"tmax":3000}`), + }, + setup: func(mme *mock_metrics.MockMetricsEngine) {}, + }, + want: hookstage.HookResult[hookstage.EntrypointPayload]{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + ProfileID: 43563, + PubID: 0, + PubIDStr: "", + DisplayID: 1, + SSAuction: -1, + Debug: true, + UA: "go-test", + IP: "127.0.0.1", + IsCTVRequest: false, + 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: "43563", + Endpoint: models.EndpointOWS2S, + MetricsEngine: mockEngine, + SeatNonBids: make(map[string][]openrtb_ext.NonBid), + }, + }, + }, + wantErr: nil, + }, + { + name: "Valid /openrtb2/auction?source=pbjs request(ows2s) debug set from request body instead of queryparam", + fields: fields{ + cfg: config.Config{}, + cache: nil, + }, + args: args{ + in0: context.Background(), + miCtx: hookstage.ModuleInvocationContext{}, + payload: hookstage.EntrypointPayload{ + Request: func() *http.Request { + r, err := http.NewRequest("POST", "http://localhost/openrtb2/auction?source=pbjs", nil) + if err != nil { + panic(err) + } + r.Header.Add("User-Agent", "go-test") + r.Header.Add("SOURCE_IP", "127.0.0.1") + r.Header.Add("Cookie", `KADUSERCOOKIE=7D75D25F-FAC9-443D-B2D1-B17FEE11E027; DPSync3=1684886400%3A248%7C1685491200%3A245_226_201; KRTBCOOKIE_80=16514-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&22987-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23025-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23386-CAESEMih0bN7ISRdZT8xX8LXzEw; KRTBCOOKIE_377=6810-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&22918-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&23031-59dc50c9-d658-44ce-b442-5a1f344d97c0; uids=eyJ0ZW1wVUlEcyI6eyIzM2Fjcm9zcyI6eyJ1aWQiOiIxMTkxNzkxMDk5Nzc2NjEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTo0My4zODg4Nzk5NVoifSwiYWRmIjp7InVpZCI6IjgwNDQ2MDgzMzM3Nzg4MzkwNzgiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMS4wMzMwNTQ3MjdaIn0sImFka2VybmVsIjp7InVpZCI6IkE5MTYzNTAwNzE0OTkyOTMyOTkwIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuMzczMzg1NjYyWiJ9LCJhZGtlcm5lbEFkbiI6eyJ1aWQiOiJBOTE2MzUwMDcxNDk5MjkzMjk5MCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEzLjQzNDkyNTg5NloifSwiYWRtaXhlciI6eyJ1aWQiOiIzNjZhMTdiMTJmMjI0ZDMwOGYzZTNiOGRhOGMzYzhhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjU5MjkxNDgwMVoifSwiYWRueHMiOnsidWlkIjoiNDE5Mjg5ODUzMDE0NTExOTMiLCJleHBpcmVzIjoiMjAyMy0wMS0xOFQwOTo1MzowOC44MjU0NDI2NzZaIn0sImFqYSI6eyJ1aWQiOiJzMnN1aWQ2RGVmMFl0bjJveGQ1aG9zS1AxVmV3IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTMuMjM5MTc2MDU0WiJ9LCJlcGxhbm5pbmciOnsidWlkIjoiQUoxRjBTOE5qdTdTQ0xWOSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjkyOTk2MDQ3M1oifSwiZ2Ftb3NoaSI6eyJ1aWQiOiJndXNyXzM1NmFmOWIxZDhjNjQyYjQ4MmNiYWQyYjdhMjg4MTYxIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuNTI0MTU3MjI1WiJ9LCJncmlkIjp7InVpZCI6IjRmYzM2MjUwLWQ4NTItNDU5Yy04NzcyLTczNTZkZTE3YWI5NyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE0LjY5NjMxNjIyN1oifSwiZ3JvdXBtIjp7InVpZCI6IjdENzVEMjVGLUZBQzktNDQzRC1CMkQxLUIxN0ZFRTExRTAyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjM5LjIyNjIxMjUzMloifSwiaXgiOnsidWlkIjoiWW9ORlNENlc5QkphOEh6eEdtcXlCUUFBXHUwMDI2Mjk3IiwiZXhwaXJlcyI6IjIwMjMtMDUtMzFUMDc6NTM6MzguNTU1ODI3MzU0WiJ9LCJqaXhpZSI6eyJ1aWQiOiI3MzY3MTI1MC1lODgyLTExZWMtYjUzOC0xM2FjYjdhZjBkZTQiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi4xOTEwOTk3MzJaIn0sImxvZ2ljYWQiOnsidWlkIjoiQVZ4OVROQS11c25pa3M4QURzTHpWa3JvaDg4QUFBR0JUREh0UUEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS40NTUxNDk2MTZaIn0sIm1lZGlhbmV0Ijp7InVpZCI6IjI5Nzg0MjM0OTI4OTU0MTAwMDBWMTAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMy42NzIyMTUxMjhaIn0sIm1naWQiOnsidWlkIjoibTU5Z1hyN0xlX1htIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTcuMDk3MDAxNDcxWiJ9LCJuYW5vaW50ZXJhY3RpdmUiOnsidWlkIjoiNmFlYzhjMTAzNzlkY2I3ODQxMmJjODBiNmRkOWM5NzMxNzNhYjdkNzEyZTQzMWE1YTVlYTcwMzRlNTZhNThhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE2LjcxNDgwNzUwNVoifSwib25ldGFnIjp7InVpZCI6IjdPelZoVzFOeC1LOGFVak1HMG52NXVNYm5YNEFHUXZQbnVHcHFrZ3k0ckEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS4xNDE3NDEyNjJaIn0sIm9wZW54Ijp7InVpZCI6IjVkZWNlNjIyLTBhMjMtMGRhYi0zYTI0LTVhNzcwMTBlNDU4MiIsImV4cGlyZXMiOiIyMDIzLTA1LTMxVDA3OjUyOjQ3LjE0MDQxNzM2M1oifSwicHVibWF0aWMiOnsidWlkIjoiN0Q3NUQyNUYtRkFDOS00NDNELUIyRDEtQjE3RkVFMTFFMDI3IiwiZXhwaXJlcyI6IjIwMjItMTAtMzFUMDk6MTQ6MjUuNzM3MjU2ODk5WiJ9LCJyaWNoYXVkaWVuY2UiOnsidWlkIjoiY2I2YzYzMjAtMzNlMi00Nzc0LWIxNjAtMXp6MTY1NDg0MDc0OSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjUyNTA3NDE4WiJ9LCJzbWFydHlhZHMiOnsidWlkIjoiMTJhZjE1ZTQ0ZjAwZDA3NjMwZTc0YzQ5MTU0Y2JmYmE0Zjg0N2U4ZDRhMTU0YzhjM2Q1MWY1OGNmNzJhNDYyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjgyNTAzMTg4NFoifSwic21pbGV3YW50ZWQiOnsidWlkIjoiZGQ5YzNmZTE4N2VmOWIwOWNhYTViNzExNDA0YzI4MzAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNC4yNTU2MDkzNjNaIn0sInN5bmFjb3JtZWRpYSI6eyJ1aWQiOiJHRFBSIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuOTc5NTgzNDM4WiJ9LCJ0cmlwbGVsaWZ0Ijp7InVpZCI6IjcwMjE5NzUwNTQ4MDg4NjUxOTQ2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA4Ljk4OTY3MzU3NFoifSwidmFsdWVpbXByZXNzaW9uIjp7InVpZCI6IjlkMDgxNTVmLWQ5ZmUtNGI1OC04OThlLWUyYzU2MjgyYWIzZSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjA2NzgzOTE2NFoifSwidmlzeCI6eyJ1aWQiOiIyN2UwYWMzYy1iNDZlLTQxYjMtOTkyYy1mOGQyNzE0OTQ5NWUiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi45ODk1MjM1NzNaIn0sInlpZWxkbGFiIjp7InVpZCI6IjY5NzE0ZDlmLWZiMDAtNGE1Zi04MTljLTRiZTE5MTM2YTMyNSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjExLjMwMzAyNjYxNVoifSwieWllbGRtbyI6eyJ1aWQiOiJnOTZjMmY3MTlmMTU1MWIzMWY2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjExMDUyODYwOVoifSwieWllbGRvbmUiOnsidWlkIjoiMmE0MmZiZDMtMmM3MC00ZWI5LWIxYmQtMDQ2OTY2NTBkOTQ4IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuMzE4MzMzOTM5WiJ9LCJ6ZXJvY2xpY2tmcmF1ZCI6eyJ1aWQiOiJiOTk5NThmZS0yYTg3LTJkYTQtOWNjNC05NjFmZDExM2JlY2UiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNS43MTk1OTQ1NjZaIn19LCJiZGF5IjoiMjAyMi0wNS0xN1QwNjo0ODozOC4wMTc5ODgyMDZaIn0=; KRTBCOOKIE_153=1923-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&19420-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&22979-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&23462-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse; KRTBCOOKIE_57=22776-41928985301451193&KRTB&23339-41928985301451193; KRTBCOOKIE_27=16735-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&16736-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23019-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23114-uid:3cab6283-4546-4500-a7b6-40ef605fe745; KRTBCOOKIE_18=22947-1978557989514665832; KRTBCOOKIE_466=16530-4fc36250-d852-459c-8772-7356de17ab97; KRTBCOOKIE_391=22924-8044608333778839078&KRTB&23263-8044608333778839078&KRTB&23481-8044608333778839078; KRTBCOOKIE_1310=23431-b81c3g7dr67i&KRTB&23446-b81c3g7dr67i&KRTB&23465-b81c3g7dr67i; KRTBCOOKIE_1290=23368-vkf3yv9lbbl; KRTBCOOKIE_22=14911-4554572065121110164&KRTB&23150-4554572065121110164; KRTBCOOKIE_860=16335-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23334-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23417-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23426-YGAqDU1zUTdjyAFxCoe3kctlNPo; KRTBCOOKIE_904=16787-KwJwE7NkCZClNJRysN2iYg; KRTBCOOKIE_1159=23138-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23328-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23427-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23445-5545f53f3d6e4ec199d8ed627ff026f3; KRTBCOOKIE_32=11175-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22713-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22715-AQEI_1QecY2ESAIjEW6KAQEBAQE; SyncRTB3=1685577600%3A35%7C1685491200%3A107_21_71_56_204_247_165_231_233_179_22_209_54_254_238_96_99_220_7_214_13_3_8_234_176_46_5%7C1684886400%3A2_223_15%7C1689465600%3A69%7C1685145600%3A63; KRTBCOOKIE_107=1471-uid:EK38R0PM1NQR0H5&KRTB&23421-uid:EK38R0PM1NQR0H5; KRTBCOOKIE_594=17105-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004&KRTB&17107-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004; SPugT=1684310122; chkChromeAb67Sec=133; KRTBCOOKIE_699=22727-AAFy2k7FBosAAEasbJoXnw; PugT=1684310473; origin=go-test`) + return r + }(), + Body: []byte(`{"imp":[{"tagid":"/43743431/DMDemo","ext":{"prebid":{"bidder":{"pubmatic":{"publisherId":"5890"}},"adunitcode":"div-gpt-ad-1460505748561-0"}},"id":"div-gpt-ad-1460505748561-0","banner":{"topframe":1,"format":[{"w":300,"h":250}]}}],"site":{"domain":"localhost:9999","publisher":{"domain":"localhost:9999","id":"5890"},"page":"http://localhost:9999/integrationExamples/gpt/owServer_example.html"},"device":{"w":1792,"h":446,"dnt":0,"ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36","language":"en","sua":{"source":1,"platform":{"brand":"macOS"},"browsers":[{"brand":"Google Chrome","version":["117"]},{"brand":"Not;A=Brand","version":["8"]},{"brand":"Chromium","version":["117"]}],"mobile":0}},"ext":{"prebid":{"auctiontimestamp":1697191822565,"targeting":{"includewinners":true,"includebidderkeys":true},"bidderparams":{"pubmatic":{"publisherId":"5890","wrapper":{"profileid":43563,"versionid":1}}},"channel":{"name":"pbjs","version":"v8.7.0-pre"},"createtids":false,"debug":true}},"id":"5bdd7da5-1166-40fe-a9cb-3bf3c3164cd3","test":0,"tmax":3000}`), + }, + setup: func(mme *mock_metrics.MockMetricsEngine) {}, + }, + want: hookstage.HookResult[hookstage.EntrypointPayload]{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + ProfileID: 43563, + PubID: 0, + PubIDStr: "", + DisplayID: 1, + SSAuction: -1, + Debug: true, + UA: "go-test", + IP: "127.0.0.1", + IsCTVRequest: false, + 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: "43563", + Endpoint: models.EndpointOWS2S, + MetricsEngine: mockEngine, + SeatNonBids: make(map[string][]openrtb_ext.NonBid), + }, + }, + }, + wantErr: nil, + }, + { + name: "/openrtb2/auction?source=pbjs request(ows2s) without profileid", + fields: fields{ + cfg: config.Config{}, + cache: nil, + }, + args: args{ + in0: context.Background(), + miCtx: hookstage.ModuleInvocationContext{}, + payload: hookstage.EntrypointPayload{ + Request: func() *http.Request { + r, err := http.NewRequest("POST", "http://localhost/openrtb2/auction?source=pbjs&debug=1", nil) + if err != nil { + panic(err) + } + r.Header.Add("User-Agent", "go-test") + r.Header.Add("SOURCE_IP", "127.0.0.1") + r.Header.Add("Cookie", `KADUSERCOOKIE=7D75D25F-FAC9-443D-B2D1-B17FEE11E027; DPSync3=1684886400%3A248%7C1685491200%3A245_226_201; KRTBCOOKIE_80=16514-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&22987-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23025-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23386-CAESEMih0bN7ISRdZT8xX8LXzEw; KRTBCOOKIE_377=6810-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&22918-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&23031-59dc50c9-d658-44ce-b442-5a1f344d97c0; uids=eyJ0ZW1wVUlEcyI6eyIzM2Fjcm9zcyI6eyJ1aWQiOiIxMTkxNzkxMDk5Nzc2NjEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTo0My4zODg4Nzk5NVoifSwiYWRmIjp7InVpZCI6IjgwNDQ2MDgzMzM3Nzg4MzkwNzgiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMS4wMzMwNTQ3MjdaIn0sImFka2VybmVsIjp7InVpZCI6IkE5MTYzNTAwNzE0OTkyOTMyOTkwIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuMzczMzg1NjYyWiJ9LCJhZGtlcm5lbEFkbiI6eyJ1aWQiOiJBOTE2MzUwMDcxNDk5MjkzMjk5MCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEzLjQzNDkyNTg5NloifSwiYWRtaXhlciI6eyJ1aWQiOiIzNjZhMTdiMTJmMjI0ZDMwOGYzZTNiOGRhOGMzYzhhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjU5MjkxNDgwMVoifSwiYWRueHMiOnsidWlkIjoiNDE5Mjg5ODUzMDE0NTExOTMiLCJleHBpcmVzIjoiMjAyMy0wMS0xOFQwOTo1MzowOC44MjU0NDI2NzZaIn0sImFqYSI6eyJ1aWQiOiJzMnN1aWQ2RGVmMFl0bjJveGQ1aG9zS1AxVmV3IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTMuMjM5MTc2MDU0WiJ9LCJlcGxhbm5pbmciOnsidWlkIjoiQUoxRjBTOE5qdTdTQ0xWOSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjkyOTk2MDQ3M1oifSwiZ2Ftb3NoaSI6eyJ1aWQiOiJndXNyXzM1NmFmOWIxZDhjNjQyYjQ4MmNiYWQyYjdhMjg4MTYxIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuNTI0MTU3MjI1WiJ9LCJncmlkIjp7InVpZCI6IjRmYzM2MjUwLWQ4NTItNDU5Yy04NzcyLTczNTZkZTE3YWI5NyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE0LjY5NjMxNjIyN1oifSwiZ3JvdXBtIjp7InVpZCI6IjdENzVEMjVGLUZBQzktNDQzRC1CMkQxLUIxN0ZFRTExRTAyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjM5LjIyNjIxMjUzMloifSwiaXgiOnsidWlkIjoiWW9ORlNENlc5QkphOEh6eEdtcXlCUUFBXHUwMDI2Mjk3IiwiZXhwaXJlcyI6IjIwMjMtMDUtMzFUMDc6NTM6MzguNTU1ODI3MzU0WiJ9LCJqaXhpZSI6eyJ1aWQiOiI3MzY3MTI1MC1lODgyLTExZWMtYjUzOC0xM2FjYjdhZjBkZTQiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi4xOTEwOTk3MzJaIn0sImxvZ2ljYWQiOnsidWlkIjoiQVZ4OVROQS11c25pa3M4QURzTHpWa3JvaDg4QUFBR0JUREh0UUEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS40NTUxNDk2MTZaIn0sIm1lZGlhbmV0Ijp7InVpZCI6IjI5Nzg0MjM0OTI4OTU0MTAwMDBWMTAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMy42NzIyMTUxMjhaIn0sIm1naWQiOnsidWlkIjoibTU5Z1hyN0xlX1htIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTcuMDk3MDAxNDcxWiJ9LCJuYW5vaW50ZXJhY3RpdmUiOnsidWlkIjoiNmFlYzhjMTAzNzlkY2I3ODQxMmJjODBiNmRkOWM5NzMxNzNhYjdkNzEyZTQzMWE1YTVlYTcwMzRlNTZhNThhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE2LjcxNDgwNzUwNVoifSwib25ldGFnIjp7InVpZCI6IjdPelZoVzFOeC1LOGFVak1HMG52NXVNYm5YNEFHUXZQbnVHcHFrZ3k0ckEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS4xNDE3NDEyNjJaIn0sIm9wZW54Ijp7InVpZCI6IjVkZWNlNjIyLTBhMjMtMGRhYi0zYTI0LTVhNzcwMTBlNDU4MiIsImV4cGlyZXMiOiIyMDIzLTA1LTMxVDA3OjUyOjQ3LjE0MDQxNzM2M1oifSwicHVibWF0aWMiOnsidWlkIjoiN0Q3NUQyNUYtRkFDOS00NDNELUIyRDEtQjE3RkVFMTFFMDI3IiwiZXhwaXJlcyI6IjIwMjItMTAtMzFUMDk6MTQ6MjUuNzM3MjU2ODk5WiJ9LCJyaWNoYXVkaWVuY2UiOnsidWlkIjoiY2I2YzYzMjAtMzNlMi00Nzc0LWIxNjAtMXp6MTY1NDg0MDc0OSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjUyNTA3NDE4WiJ9LCJzbWFydHlhZHMiOnsidWlkIjoiMTJhZjE1ZTQ0ZjAwZDA3NjMwZTc0YzQ5MTU0Y2JmYmE0Zjg0N2U4ZDRhMTU0YzhjM2Q1MWY1OGNmNzJhNDYyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjgyNTAzMTg4NFoifSwic21pbGV3YW50ZWQiOnsidWlkIjoiZGQ5YzNmZTE4N2VmOWIwOWNhYTViNzExNDA0YzI4MzAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNC4yNTU2MDkzNjNaIn0sInN5bmFjb3JtZWRpYSI6eyJ1aWQiOiJHRFBSIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuOTc5NTgzNDM4WiJ9LCJ0cmlwbGVsaWZ0Ijp7InVpZCI6IjcwMjE5NzUwNTQ4MDg4NjUxOTQ2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA4Ljk4OTY3MzU3NFoifSwidmFsdWVpbXByZXNzaW9uIjp7InVpZCI6IjlkMDgxNTVmLWQ5ZmUtNGI1OC04OThlLWUyYzU2MjgyYWIzZSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjA2NzgzOTE2NFoifSwidmlzeCI6eyJ1aWQiOiIyN2UwYWMzYy1iNDZlLTQxYjMtOTkyYy1mOGQyNzE0OTQ5NWUiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi45ODk1MjM1NzNaIn0sInlpZWxkbGFiIjp7InVpZCI6IjY5NzE0ZDlmLWZiMDAtNGE1Zi04MTljLTRiZTE5MTM2YTMyNSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjExLjMwMzAyNjYxNVoifSwieWllbGRtbyI6eyJ1aWQiOiJnOTZjMmY3MTlmMTU1MWIzMWY2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjExMDUyODYwOVoifSwieWllbGRvbmUiOnsidWlkIjoiMmE0MmZiZDMtMmM3MC00ZWI5LWIxYmQtMDQ2OTY2NTBkOTQ4IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuMzE4MzMzOTM5WiJ9LCJ6ZXJvY2xpY2tmcmF1ZCI6eyJ1aWQiOiJiOTk5NThmZS0yYTg3LTJkYTQtOWNjNC05NjFmZDExM2JlY2UiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNS43MTk1OTQ1NjZaIn19LCJiZGF5IjoiMjAyMi0wNS0xN1QwNjo0ODozOC4wMTc5ODgyMDZaIn0=; KRTBCOOKIE_153=1923-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&19420-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&22979-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&23462-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse; KRTBCOOKIE_57=22776-41928985301451193&KRTB&23339-41928985301451193; KRTBCOOKIE_27=16735-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&16736-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23019-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23114-uid:3cab6283-4546-4500-a7b6-40ef605fe745; KRTBCOOKIE_18=22947-1978557989514665832; KRTBCOOKIE_466=16530-4fc36250-d852-459c-8772-7356de17ab97; KRTBCOOKIE_391=22924-8044608333778839078&KRTB&23263-8044608333778839078&KRTB&23481-8044608333778839078; KRTBCOOKIE_1310=23431-b81c3g7dr67i&KRTB&23446-b81c3g7dr67i&KRTB&23465-b81c3g7dr67i; KRTBCOOKIE_1290=23368-vkf3yv9lbbl; KRTBCOOKIE_22=14911-4554572065121110164&KRTB&23150-4554572065121110164; KRTBCOOKIE_860=16335-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23334-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23417-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23426-YGAqDU1zUTdjyAFxCoe3kctlNPo; KRTBCOOKIE_904=16787-KwJwE7NkCZClNJRysN2iYg; KRTBCOOKIE_1159=23138-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23328-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23427-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23445-5545f53f3d6e4ec199d8ed627ff026f3; KRTBCOOKIE_32=11175-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22713-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22715-AQEI_1QecY2ESAIjEW6KAQEBAQE; SyncRTB3=1685577600%3A35%7C1685491200%3A107_21_71_56_204_247_165_231_233_179_22_209_54_254_238_96_99_220_7_214_13_3_8_234_176_46_5%7C1684886400%3A2_223_15%7C1689465600%3A69%7C1685145600%3A63; KRTBCOOKIE_107=1471-uid:EK38R0PM1NQR0H5&KRTB&23421-uid:EK38R0PM1NQR0H5; KRTBCOOKIE_594=17105-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004&KRTB&17107-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004; SPugT=1684310122; chkChromeAb67Sec=133; KRTBCOOKIE_699=22727-AAFy2k7FBosAAEasbJoXnw; PugT=1684310473; origin=go-test`) + return r + }(), + Body: []byte(`{"imp":[{"tagid":"/43743431/DMDemo","ext":{"prebid":{"bidder":{"pubmatic":{"publisherId":"5890"}},"adunitcode":"div-gpt-ad-1460505748561-0"}},"id":"div-gpt-ad-1460505748561-0","banner":{"topframe":1,"format":[{"w":300,"h":250}]}}],"site":{"domain":"localhost:9999","publisher":{"domain":"localhost:9999","id":"5890"},"page":"http://localhost:9999/integrationExamples/gpt/owServer_example.html"},"device":{"w":1792,"h":446,"dnt":0,"ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36","language":"en","sua":{"source":1,"platform":{"brand":"macOS"},"browsers":[{"brand":"Google Chrome","version":["117"]},{"brand":"Not;A=Brand","version":["8"]},{"brand":"Chromium","version":["117"]}],"mobile":0}},"ext":{"prebid":{"auctiontimestamp":1697191822565,"targeting":{"includewinners":true,"includebidderkeys":true},"bidderparams":{"pubmatic":{"publisherId":"5890","wrapper":{}}},"channel":{"name":"pbjs","version":"v8.7.0-pre"},"createtids":false}},"id":"5bdd7da5-1166-40fe-a9cb-3bf3c3164cd3","test":0,"tmax":3000}`), + }, + setup: func(mme *mock_metrics.MockMetricsEngine) { + mme.EXPECT().RecordBadRequests(gomock.Any(), 700) + }, + }, + want: hookstage.HookResult[hookstage.EntrypointPayload]{ + Reject: true, + NbrCode: nbr.InvalidProfileID, + Errors: []string{"ErrMissingProfileID"}, + }, + wantErr: nil, + }, + { + name: "/openrtb2/auction without source=pbjs/inapp. new hybrid endpoint should not execute module", + fields: fields{ + cfg: config.Config{}, + cache: nil, + }, + args: args{ + in0: context.Background(), + miCtx: hookstage.ModuleInvocationContext{}, + payload: hookstage.EntrypointPayload{ + Request: func() *http.Request { + r, err := http.NewRequest("POST", "http://localhost/openrtb2/auction?debug=1", nil) + if err != nil { + panic(err) + } + r.Header.Add("User-Agent", "go-test") + r.Header.Add("SOURCE_IP", "127.0.0.1") + r.Header.Add("Cookie", `KADUSERCOOKIE=7D75D25F-FAC9-443D-B2D1-B17FEE11E027; DPSync3=1684886400%3A248%7C1685491200%3A245_226_201; KRTBCOOKIE_80=16514-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&22987-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23025-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23386-CAESEMih0bN7ISRdZT8xX8LXzEw; KRTBCOOKIE_377=6810-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&22918-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&23031-59dc50c9-d658-44ce-b442-5a1f344d97c0; uids=eyJ0ZW1wVUlEcyI6eyIzM2Fjcm9zcyI6eyJ1aWQiOiIxMTkxNzkxMDk5Nzc2NjEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTo0My4zODg4Nzk5NVoifSwiYWRmIjp7InVpZCI6IjgwNDQ2MDgzMzM3Nzg4MzkwNzgiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMS4wMzMwNTQ3MjdaIn0sImFka2VybmVsIjp7InVpZCI6IkE5MTYzNTAwNzE0OTkyOTMyOTkwIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuMzczMzg1NjYyWiJ9LCJhZGtlcm5lbEFkbiI6eyJ1aWQiOiJBOTE2MzUwMDcxNDk5MjkzMjk5MCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEzLjQzNDkyNTg5NloifSwiYWRtaXhlciI6eyJ1aWQiOiIzNjZhMTdiMTJmMjI0ZDMwOGYzZTNiOGRhOGMzYzhhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjU5MjkxNDgwMVoifSwiYWRueHMiOnsidWlkIjoiNDE5Mjg5ODUzMDE0NTExOTMiLCJleHBpcmVzIjoiMjAyMy0wMS0xOFQwOTo1MzowOC44MjU0NDI2NzZaIn0sImFqYSI6eyJ1aWQiOiJzMnN1aWQ2RGVmMFl0bjJveGQ1aG9zS1AxVmV3IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTMuMjM5MTc2MDU0WiJ9LCJlcGxhbm5pbmciOnsidWlkIjoiQUoxRjBTOE5qdTdTQ0xWOSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjkyOTk2MDQ3M1oifSwiZ2Ftb3NoaSI6eyJ1aWQiOiJndXNyXzM1NmFmOWIxZDhjNjQyYjQ4MmNiYWQyYjdhMjg4MTYxIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuNTI0MTU3MjI1WiJ9LCJncmlkIjp7InVpZCI6IjRmYzM2MjUwLWQ4NTItNDU5Yy04NzcyLTczNTZkZTE3YWI5NyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE0LjY5NjMxNjIyN1oifSwiZ3JvdXBtIjp7InVpZCI6IjdENzVEMjVGLUZBQzktNDQzRC1CMkQxLUIxN0ZFRTExRTAyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjM5LjIyNjIxMjUzMloifSwiaXgiOnsidWlkIjoiWW9ORlNENlc5QkphOEh6eEdtcXlCUUFBXHUwMDI2Mjk3IiwiZXhwaXJlcyI6IjIwMjMtMDUtMzFUMDc6NTM6MzguNTU1ODI3MzU0WiJ9LCJqaXhpZSI6eyJ1aWQiOiI3MzY3MTI1MC1lODgyLTExZWMtYjUzOC0xM2FjYjdhZjBkZTQiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi4xOTEwOTk3MzJaIn0sImxvZ2ljYWQiOnsidWlkIjoiQVZ4OVROQS11c25pa3M4QURzTHpWa3JvaDg4QUFBR0JUREh0UUEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS40NTUxNDk2MTZaIn0sIm1lZGlhbmV0Ijp7InVpZCI6IjI5Nzg0MjM0OTI4OTU0MTAwMDBWMTAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMy42NzIyMTUxMjhaIn0sIm1naWQiOnsidWlkIjoibTU5Z1hyN0xlX1htIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTcuMDk3MDAxNDcxWiJ9LCJuYW5vaW50ZXJhY3RpdmUiOnsidWlkIjoiNmFlYzhjMTAzNzlkY2I3ODQxMmJjODBiNmRkOWM5NzMxNzNhYjdkNzEyZTQzMWE1YTVlYTcwMzRlNTZhNThhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE2LjcxNDgwNzUwNVoifSwib25ldGFnIjp7InVpZCI6IjdPelZoVzFOeC1LOGFVak1HMG52NXVNYm5YNEFHUXZQbnVHcHFrZ3k0ckEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS4xNDE3NDEyNjJaIn0sIm9wZW54Ijp7InVpZCI6IjVkZWNlNjIyLTBhMjMtMGRhYi0zYTI0LTVhNzcwMTBlNDU4MiIsImV4cGlyZXMiOiIyMDIzLTA1LTMxVDA3OjUyOjQ3LjE0MDQxNzM2M1oifSwicHVibWF0aWMiOnsidWlkIjoiN0Q3NUQyNUYtRkFDOS00NDNELUIyRDEtQjE3RkVFMTFFMDI3IiwiZXhwaXJlcyI6IjIwMjItMTAtMzFUMDk6MTQ6MjUuNzM3MjU2ODk5WiJ9LCJyaWNoYXVkaWVuY2UiOnsidWlkIjoiY2I2YzYzMjAtMzNlMi00Nzc0LWIxNjAtMXp6MTY1NDg0MDc0OSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjUyNTA3NDE4WiJ9LCJzbWFydHlhZHMiOnsidWlkIjoiMTJhZjE1ZTQ0ZjAwZDA3NjMwZTc0YzQ5MTU0Y2JmYmE0Zjg0N2U4ZDRhMTU0YzhjM2Q1MWY1OGNmNzJhNDYyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjgyNTAzMTg4NFoifSwic21pbGV3YW50ZWQiOnsidWlkIjoiZGQ5YzNmZTE4N2VmOWIwOWNhYTViNzExNDA0YzI4MzAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNC4yNTU2MDkzNjNaIn0sInN5bmFjb3JtZWRpYSI6eyJ1aWQiOiJHRFBSIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuOTc5NTgzNDM4WiJ9LCJ0cmlwbGVsaWZ0Ijp7InVpZCI6IjcwMjE5NzUwNTQ4MDg4NjUxOTQ2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA4Ljk4OTY3MzU3NFoifSwidmFsdWVpbXByZXNzaW9uIjp7InVpZCI6IjlkMDgxNTVmLWQ5ZmUtNGI1OC04OThlLWUyYzU2MjgyYWIzZSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjA2NzgzOTE2NFoifSwidmlzeCI6eyJ1aWQiOiIyN2UwYWMzYy1iNDZlLTQxYjMtOTkyYy1mOGQyNzE0OTQ5NWUiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi45ODk1MjM1NzNaIn0sInlpZWxkbGFiIjp7InVpZCI6IjY5NzE0ZDlmLWZiMDAtNGE1Zi04MTljLTRiZTE5MTM2YTMyNSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjExLjMwMzAyNjYxNVoifSwieWllbGRtbyI6eyJ1aWQiOiJnOTZjMmY3MTlmMTU1MWIzMWY2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjExMDUyODYwOVoifSwieWllbGRvbmUiOnsidWlkIjoiMmE0MmZiZDMtMmM3MC00ZWI5LWIxYmQtMDQ2OTY2NTBkOTQ4IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuMzE4MzMzOTM5WiJ9LCJ6ZXJvY2xpY2tmcmF1ZCI6eyJ1aWQiOiJiOTk5NThmZS0yYTg3LTJkYTQtOWNjNC05NjFmZDExM2JlY2UiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNS43MTk1OTQ1NjZaIn19LCJiZGF5IjoiMjAyMi0wNS0xN1QwNjo0ODozOC4wMTc5ODgyMDZaIn0=; KRTBCOOKIE_153=1923-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&19420-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&22979-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&23462-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse; KRTBCOOKIE_57=22776-41928985301451193&KRTB&23339-41928985301451193; KRTBCOOKIE_27=16735-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&16736-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23019-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23114-uid:3cab6283-4546-4500-a7b6-40ef605fe745; KRTBCOOKIE_18=22947-1978557989514665832; KRTBCOOKIE_466=16530-4fc36250-d852-459c-8772-7356de17ab97; KRTBCOOKIE_391=22924-8044608333778839078&KRTB&23263-8044608333778839078&KRTB&23481-8044608333778839078; KRTBCOOKIE_1310=23431-b81c3g7dr67i&KRTB&23446-b81c3g7dr67i&KRTB&23465-b81c3g7dr67i; KRTBCOOKIE_1290=23368-vkf3yv9lbbl; KRTBCOOKIE_22=14911-4554572065121110164&KRTB&23150-4554572065121110164; KRTBCOOKIE_860=16335-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23334-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23417-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23426-YGAqDU1zUTdjyAFxCoe3kctlNPo; KRTBCOOKIE_904=16787-KwJwE7NkCZClNJRysN2iYg; KRTBCOOKIE_1159=23138-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23328-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23427-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23445-5545f53f3d6e4ec199d8ed627ff026f3; KRTBCOOKIE_32=11175-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22713-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22715-AQEI_1QecY2ESAIjEW6KAQEBAQE; SyncRTB3=1685577600%3A35%7C1685491200%3A107_21_71_56_204_247_165_231_233_179_22_209_54_254_238_96_99_220_7_214_13_3_8_234_176_46_5%7C1684886400%3A2_223_15%7C1689465600%3A69%7C1685145600%3A63; KRTBCOOKIE_107=1471-uid:EK38R0PM1NQR0H5&KRTB&23421-uid:EK38R0PM1NQR0H5; KRTBCOOKIE_594=17105-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004&KRTB&17107-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004; SPugT=1684310122; chkChromeAb67Sec=133; KRTBCOOKIE_699=22727-AAFy2k7FBosAAEasbJoXnw; PugT=1684310473; origin=go-test`) + return r + }(), + Body: []byte(`{"imp":[{"tagid":"/43743431/DMDemo","ext":{"prebid":{"bidder":{"pubmatic":{"publisherId":"5890"}},"adunitcode":"div-gpt-ad-1460505748561-0"}},"id":"div-gpt-ad-1460505748561-0","banner":{"topframe":1,"format":[{"w":300,"h":250}]}}],"site":{"domain":"localhost:9999","publisher":{"domain":"localhost:9999","id":"5890"},"page":"http://localhost:9999/integrationExamples/gpt/owServer_example.html"},"device":{"w":1792,"h":446,"dnt":0,"ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36","language":"en","sua":{"source":1,"platform":{"brand":"macOS"},"browsers":[{"brand":"Google Chrome","version":["117"]},{"brand":"Not;A=Brand","version":["8"]},{"brand":"Chromium","version":["117"]}],"mobile":0}},"ext":{"prebid":{"auctiontimestamp":1697191822565,"targeting":{"includewinners":true,"includebidderkeys":true},"bidderparams":{"pubmatic":{"publisherId":"5890","wrapper":{}}},"channel":{"name":"pbjs","version":"v8.7.0-pre"},"createtids":false}},"id":"5bdd7da5-1166-40fe-a9cb-3bf3c3164cd3","test":0,"tmax":3000}`), + }, + setup: func(mme *mock_metrics.MockMetricsEngine) {}, + }, + want: hookstage.HookResult[hookstage.EntrypointPayload]{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + Endpoint: models.EndpointHybrid, + }, + }, + }, + wantErr: nil, + }, + { + name: "hybrid api pbs/openrtb2/auction should not execute entrypointhook", + args: args{ + in0: context.Background(), + miCtx: hookstage.ModuleInvocationContext{}, + payload: hookstage.EntrypointPayload{ + Request: func() *http.Request { + r, err := http.NewRequest("POST", "http://localhost/pbs/openrtb2/auction", nil) + if err != nil { + panic(err) + } + r.Header.Add("User-Agent", "go-test") + r.Header.Add("SOURCE_IP", "127.0.0.1") + r.Header.Add("Cookie", `KADUSERCOOKIE=7D75D25F-FAC9-443D-B2D1-B17FEE11E027; DPSync3=1684886400%3A248%7C1685491200%3A245_226_201; KRTBCOOKIE_80=16514-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&22987-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23025-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23386-CAESEMih0bN7ISRdZT8xX8LXzEw; KRTBCOOKIE_377=6810-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&22918-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&23031-59dc50c9-d658-44ce-b442-5a1f344d97c0; uids=eyJ0ZW1wVUlEcyI6eyIzM2Fjcm9zcyI6eyJ1aWQiOiIxMTkxNzkxMDk5Nzc2NjEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTo0My4zODg4Nzk5NVoifSwiYWRmIjp7InVpZCI6IjgwNDQ2MDgzMzM3Nzg4MzkwNzgiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMS4wMzMwNTQ3MjdaIn0sImFka2VybmVsIjp7InVpZCI6IkE5MTYzNTAwNzE0OTkyOTMyOTkwIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuMzczMzg1NjYyWiJ9LCJhZGtlcm5lbEFkbiI6eyJ1aWQiOiJBOTE2MzUwMDcxNDk5MjkzMjk5MCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEzLjQzNDkyNTg5NloifSwiYWRtaXhlciI6eyJ1aWQiOiIzNjZhMTdiMTJmMjI0ZDMwOGYzZTNiOGRhOGMzYzhhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjU5MjkxNDgwMVoifSwiYWRueHMiOnsidWlkIjoiNDE5Mjg5ODUzMDE0NTExOTMiLCJleHBpcmVzIjoiMjAyMy0wMS0xOFQwOTo1MzowOC44MjU0NDI2NzZaIn0sImFqYSI6eyJ1aWQiOiJzMnN1aWQ2RGVmMFl0bjJveGQ1aG9zS1AxVmV3IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTMuMjM5MTc2MDU0WiJ9LCJlcGxhbm5pbmciOnsidWlkIjoiQUoxRjBTOE5qdTdTQ0xWOSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjkyOTk2MDQ3M1oifSwiZ2Ftb3NoaSI6eyJ1aWQiOiJndXNyXzM1NmFmOWIxZDhjNjQyYjQ4MmNiYWQyYjdhMjg4MTYxIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuNTI0MTU3MjI1WiJ9LCJncmlkIjp7InVpZCI6IjRmYzM2MjUwLWQ4NTItNDU5Yy04NzcyLTczNTZkZTE3YWI5NyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE0LjY5NjMxNjIyN1oifSwiZ3JvdXBtIjp7InVpZCI6IjdENzVEMjVGLUZBQzktNDQzRC1CMkQxLUIxN0ZFRTExRTAyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjM5LjIyNjIxMjUzMloifSwiaXgiOnsidWlkIjoiWW9ORlNENlc5QkphOEh6eEdtcXlCUUFBXHUwMDI2Mjk3IiwiZXhwaXJlcyI6IjIwMjMtMDUtMzFUMDc6NTM6MzguNTU1ODI3MzU0WiJ9LCJqaXhpZSI6eyJ1aWQiOiI3MzY3MTI1MC1lODgyLTExZWMtYjUzOC0xM2FjYjdhZjBkZTQiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi4xOTEwOTk3MzJaIn0sImxvZ2ljYWQiOnsidWlkIjoiQVZ4OVROQS11c25pa3M4QURzTHpWa3JvaDg4QUFBR0JUREh0UUEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS40NTUxNDk2MTZaIn0sIm1lZGlhbmV0Ijp7InVpZCI6IjI5Nzg0MjM0OTI4OTU0MTAwMDBWMTAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMy42NzIyMTUxMjhaIn0sIm1naWQiOnsidWlkIjoibTU5Z1hyN0xlX1htIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTcuMDk3MDAxNDcxWiJ9LCJuYW5vaW50ZXJhY3RpdmUiOnsidWlkIjoiNmFlYzhjMTAzNzlkY2I3ODQxMmJjODBiNmRkOWM5NzMxNzNhYjdkNzEyZTQzMWE1YTVlYTcwMzRlNTZhNThhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE2LjcxNDgwNzUwNVoifSwib25ldGFnIjp7InVpZCI6IjdPelZoVzFOeC1LOGFVak1HMG52NXVNYm5YNEFHUXZQbnVHcHFrZ3k0ckEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS4xNDE3NDEyNjJaIn0sIm9wZW54Ijp7InVpZCI6IjVkZWNlNjIyLTBhMjMtMGRhYi0zYTI0LTVhNzcwMTBlNDU4MiIsImV4cGlyZXMiOiIyMDIzLTA1LTMxVDA3OjUyOjQ3LjE0MDQxNzM2M1oifSwicHVibWF0aWMiOnsidWlkIjoiN0Q3NUQyNUYtRkFDOS00NDNELUIyRDEtQjE3RkVFMTFFMDI3IiwiZXhwaXJlcyI6IjIwMjItMTAtMzFUMDk6MTQ6MjUuNzM3MjU2ODk5WiJ9LCJyaWNoYXVkaWVuY2UiOnsidWlkIjoiY2I2YzYzMjAtMzNlMi00Nzc0LWIxNjAtMXp6MTY1NDg0MDc0OSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjUyNTA3NDE4WiJ9LCJzbWFydHlhZHMiOnsidWlkIjoiMTJhZjE1ZTQ0ZjAwZDA3NjMwZTc0YzQ5MTU0Y2JmYmE0Zjg0N2U4ZDRhMTU0YzhjM2Q1MWY1OGNmNzJhNDYyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjgyNTAzMTg4NFoifSwic21pbGV3YW50ZWQiOnsidWlkIjoiZGQ5YzNmZTE4N2VmOWIwOWNhYTViNzExNDA0YzI4MzAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNC4yNTU2MDkzNjNaIn0sInN5bmFjb3JtZWRpYSI6eyJ1aWQiOiJHRFBSIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuOTc5NTgzNDM4WiJ9LCJ0cmlwbGVsaWZ0Ijp7InVpZCI6IjcwMjE5NzUwNTQ4MDg4NjUxOTQ2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA4Ljk4OTY3MzU3NFoifSwidmFsdWVpbXByZXNzaW9uIjp7InVpZCI6IjlkMDgxNTVmLWQ5ZmUtNGI1OC04OThlLWUyYzU2MjgyYWIzZSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjA2NzgzOTE2NFoifSwidmlzeCI6eyJ1aWQiOiIyN2UwYWMzYy1iNDZlLTQxYjMtOTkyYy1mOGQyNzE0OTQ5NWUiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi45ODk1MjM1NzNaIn0sInlpZWxkbGFiIjp7InVpZCI6IjY5NzE0ZDlmLWZiMDAtNGE1Zi04MTljLTRiZTE5MTM2YTMyNSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjExLjMwMzAyNjYxNVoifSwieWllbGRtbyI6eyJ1aWQiOiJnOTZjMmY3MTlmMTU1MWIzMWY2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjExMDUyODYwOVoifSwieWllbGRvbmUiOnsidWlkIjoiMmE0MmZiZDMtMmM3MC00ZWI5LWIxYmQtMDQ2OTY2NTBkOTQ4IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuMzE4MzMzOTM5WiJ9LCJ6ZXJvY2xpY2tmcmF1ZCI6eyJ1aWQiOiJiOTk5NThmZS0yYTg3LTJkYTQtOWNjNC05NjFmZDExM2JlY2UiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNS43MTk1OTQ1NjZaIn19LCJiZGF5IjoiMjAyMi0wNS0xN1QwNjo0ODozOC4wMTc5ODgyMDZaIn0=; KRTBCOOKIE_153=1923-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&19420-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&22979-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&23462-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse; KRTBCOOKIE_57=22776-41928985301451193&KRTB&23339-41928985301451193; KRTBCOOKIE_27=16735-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&16736-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23019-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23114-uid:3cab6283-4546-4500-a7b6-40ef605fe745; KRTBCOOKIE_18=22947-1978557989514665832; KRTBCOOKIE_466=16530-4fc36250-d852-459c-8772-7356de17ab97; KRTBCOOKIE_391=22924-8044608333778839078&KRTB&23263-8044608333778839078&KRTB&23481-8044608333778839078; KRTBCOOKIE_1310=23431-b81c3g7dr67i&KRTB&23446-b81c3g7dr67i&KRTB&23465-b81c3g7dr67i; KRTBCOOKIE_1290=23368-vkf3yv9lbbl; KRTBCOOKIE_22=14911-4554572065121110164&KRTB&23150-4554572065121110164; KRTBCOOKIE_860=16335-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23334-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23417-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23426-YGAqDU1zUTdjyAFxCoe3kctlNPo; KRTBCOOKIE_904=16787-KwJwE7NkCZClNJRysN2iYg; KRTBCOOKIE_1159=23138-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23328-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23427-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23445-5545f53f3d6e4ec199d8ed627ff026f3; KRTBCOOKIE_32=11175-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22713-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22715-AQEI_1QecY2ESAIjEW6KAQEBAQE; SyncRTB3=1685577600%3A35%7C1685491200%3A107_21_71_56_204_247_165_231_233_179_22_209_54_254_238_96_99_220_7_214_13_3_8_234_176_46_5%7C1684886400%3A2_223_15%7C1689465600%3A69%7C1685145600%3A63; KRTBCOOKIE_107=1471-uid:EK38R0PM1NQR0H5&KRTB&23421-uid:EK38R0PM1NQR0H5; KRTBCOOKIE_594=17105-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004&KRTB&17107-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004; SPugT=1684310122; chkChromeAb67Sec=133; KRTBCOOKIE_699=22727-AAFy2k7FBosAAEasbJoXnw; PugT=1684310473; origin=go-test`) + return r + }(), + Body: []byte(`{"ext":{"wrapper":{"profileid":5890,"versionid":1}}}`), + }, + setup: func(mme *mock_metrics.MockMetricsEngine) {}, + }, + want: hookstage.HookResult[hookstage.EntrypointPayload]{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + Endpoint: models.EndpointHybrid, + }, + }, + }, + wantErr: nil, + }, + { + name: "valid /openrtb2/auction?source=inapp request directly on port 8000", + fields: fields{ + cfg: config.Config{ + Tracker: config.Tracker{ + Endpoint: "t.pubmatic.com", + VideoErrorTrackerEndpoint: "t.pubmatic.com/error", + }, + }, + cache: nil, + }, + args: args{ + in0: context.Background(), + miCtx: hookstage.ModuleInvocationContext{}, + payload: hookstage.EntrypointPayload{ + Request: func() *http.Request { + r, err := http.NewRequest("POST", "http://localhost/openrtb2/auction?source=inapp&debug=1", nil) + if err != nil { + panic(err) + } + r.Header.Add("User-Agent", "go-test") + r.Header.Add("SOURCE_IP", "127.0.0.1") + r.Header.Add("Cookie", `KADUSERCOOKIE=7D75D25F-FAC9-443D-B2D1-B17FEE11E027; DPSync3=1684886400%3A248%7C1685491200%3A245_226_201; KRTBCOOKIE_80=16514-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&22987-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23025-CAESEMih0bN7ISRdZT8xX8LXzEw&KRTB&23386-CAESEMih0bN7ISRdZT8xX8LXzEw; KRTBCOOKIE_377=6810-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&22918-59dc50c9-d658-44ce-b442-5a1f344d97c0&KRTB&23031-59dc50c9-d658-44ce-b442-5a1f344d97c0; uids=eyJ0ZW1wVUlEcyI6eyIzM2Fjcm9zcyI6eyJ1aWQiOiIxMTkxNzkxMDk5Nzc2NjEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTo0My4zODg4Nzk5NVoifSwiYWRmIjp7InVpZCI6IjgwNDQ2MDgzMzM3Nzg4MzkwNzgiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMS4wMzMwNTQ3MjdaIn0sImFka2VybmVsIjp7InVpZCI6IkE5MTYzNTAwNzE0OTkyOTMyOTkwIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuMzczMzg1NjYyWiJ9LCJhZGtlcm5lbEFkbiI6eyJ1aWQiOiJBOTE2MzUwMDcxNDk5MjkzMjk5MCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEzLjQzNDkyNTg5NloifSwiYWRtaXhlciI6eyJ1aWQiOiIzNjZhMTdiMTJmMjI0ZDMwOGYzZTNiOGRhOGMzYzhhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjU5MjkxNDgwMVoifSwiYWRueHMiOnsidWlkIjoiNDE5Mjg5ODUzMDE0NTExOTMiLCJleHBpcmVzIjoiMjAyMy0wMS0xOFQwOTo1MzowOC44MjU0NDI2NzZaIn0sImFqYSI6eyJ1aWQiOiJzMnN1aWQ2RGVmMFl0bjJveGQ1aG9zS1AxVmV3IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTMuMjM5MTc2MDU0WiJ9LCJlcGxhbm5pbmciOnsidWlkIjoiQUoxRjBTOE5qdTdTQ0xWOSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjkyOTk2MDQ3M1oifSwiZ2Ftb3NoaSI6eyJ1aWQiOiJndXNyXzM1NmFmOWIxZDhjNjQyYjQ4MmNiYWQyYjdhMjg4MTYxIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuNTI0MTU3MjI1WiJ9LCJncmlkIjp7InVpZCI6IjRmYzM2MjUwLWQ4NTItNDU5Yy04NzcyLTczNTZkZTE3YWI5NyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE0LjY5NjMxNjIyN1oifSwiZ3JvdXBtIjp7InVpZCI6IjdENzVEMjVGLUZBQzktNDQzRC1CMkQxLUIxN0ZFRTExRTAyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjM5LjIyNjIxMjUzMloifSwiaXgiOnsidWlkIjoiWW9ORlNENlc5QkphOEh6eEdtcXlCUUFBXHUwMDI2Mjk3IiwiZXhwaXJlcyI6IjIwMjMtMDUtMzFUMDc6NTM6MzguNTU1ODI3MzU0WiJ9LCJqaXhpZSI6eyJ1aWQiOiI3MzY3MTI1MC1lODgyLTExZWMtYjUzOC0xM2FjYjdhZjBkZTQiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi4xOTEwOTk3MzJaIn0sImxvZ2ljYWQiOnsidWlkIjoiQVZ4OVROQS11c25pa3M4QURzTHpWa3JvaDg4QUFBR0JUREh0UUEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS40NTUxNDk2MTZaIn0sIm1lZGlhbmV0Ijp7InVpZCI6IjI5Nzg0MjM0OTI4OTU0MTAwMDBWMTAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMy42NzIyMTUxMjhaIn0sIm1naWQiOnsidWlkIjoibTU5Z1hyN0xlX1htIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTcuMDk3MDAxNDcxWiJ9LCJuYW5vaW50ZXJhY3RpdmUiOnsidWlkIjoiNmFlYzhjMTAzNzlkY2I3ODQxMmJjODBiNmRkOWM5NzMxNzNhYjdkNzEyZTQzMWE1YTVlYTcwMzRlNTZhNThhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE2LjcxNDgwNzUwNVoifSwib25ldGFnIjp7InVpZCI6IjdPelZoVzFOeC1LOGFVak1HMG52NXVNYm5YNEFHUXZQbnVHcHFrZ3k0ckEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS4xNDE3NDEyNjJaIn0sIm9wZW54Ijp7InVpZCI6IjVkZWNlNjIyLTBhMjMtMGRhYi0zYTI0LTVhNzcwMTBlNDU4MiIsImV4cGlyZXMiOiIyMDIzLTA1LTMxVDA3OjUyOjQ3LjE0MDQxNzM2M1oifSwicHVibWF0aWMiOnsidWlkIjoiN0Q3NUQyNUYtRkFDOS00NDNELUIyRDEtQjE3RkVFMTFFMDI3IiwiZXhwaXJlcyI6IjIwMjItMTAtMzFUMDk6MTQ6MjUuNzM3MjU2ODk5WiJ9LCJyaWNoYXVkaWVuY2UiOnsidWlkIjoiY2I2YzYzMjAtMzNlMi00Nzc0LWIxNjAtMXp6MTY1NDg0MDc0OSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjUyNTA3NDE4WiJ9LCJzbWFydHlhZHMiOnsidWlkIjoiMTJhZjE1ZTQ0ZjAwZDA3NjMwZTc0YzQ5MTU0Y2JmYmE0Zjg0N2U4ZDRhMTU0YzhjM2Q1MWY1OGNmNzJhNDYyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjgyNTAzMTg4NFoifSwic21pbGV3YW50ZWQiOnsidWlkIjoiZGQ5YzNmZTE4N2VmOWIwOWNhYTViNzExNDA0YzI4MzAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNC4yNTU2MDkzNjNaIn0sInN5bmFjb3JtZWRpYSI6eyJ1aWQiOiJHRFBSIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuOTc5NTgzNDM4WiJ9LCJ0cmlwbGVsaWZ0Ijp7InVpZCI6IjcwMjE5NzUwNTQ4MDg4NjUxOTQ2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA4Ljk4OTY3MzU3NFoifSwidmFsdWVpbXByZXNzaW9uIjp7InVpZCI6IjlkMDgxNTVmLWQ5ZmUtNGI1OC04OThlLWUyYzU2MjgyYWIzZSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjA2NzgzOTE2NFoifSwidmlzeCI6eyJ1aWQiOiIyN2UwYWMzYy1iNDZlLTQxYjMtOTkyYy1mOGQyNzE0OTQ5NWUiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi45ODk1MjM1NzNaIn0sInlpZWxkbGFiIjp7InVpZCI6IjY5NzE0ZDlmLWZiMDAtNGE1Zi04MTljLTRiZTE5MTM2YTMyNSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjExLjMwMzAyNjYxNVoifSwieWllbGRtbyI6eyJ1aWQiOiJnOTZjMmY3MTlmMTU1MWIzMWY2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjExMDUyODYwOVoifSwieWllbGRvbmUiOnsidWlkIjoiMmE0MmZiZDMtMmM3MC00ZWI5LWIxYmQtMDQ2OTY2NTBkOTQ4IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuMzE4MzMzOTM5WiJ9LCJ6ZXJvY2xpY2tmcmF1ZCI6eyJ1aWQiOiJiOTk5NThmZS0yYTg3LTJkYTQtOWNjNC05NjFmZDExM2JlY2UiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNS43MTk1OTQ1NjZaIn19LCJiZGF5IjoiMjAyMi0wNS0xN1QwNjo0ODozOC4wMTc5ODgyMDZaIn0=; KRTBCOOKIE_153=1923-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&19420-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&22979-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse&KRTB&23462-LZw-Ny-bMjI2m2Q8IpsrNnnIYzw2yjdnLJsrdYse; KRTBCOOKIE_57=22776-41928985301451193&KRTB&23339-41928985301451193; KRTBCOOKIE_27=16735-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&16736-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23019-uid:3cab6283-4546-4500-a7b6-40ef605fe745&KRTB&23114-uid:3cab6283-4546-4500-a7b6-40ef605fe745; KRTBCOOKIE_18=22947-1978557989514665832; KRTBCOOKIE_466=16530-4fc36250-d852-459c-8772-7356de17ab97; KRTBCOOKIE_391=22924-8044608333778839078&KRTB&23263-8044608333778839078&KRTB&23481-8044608333778839078; KRTBCOOKIE_1310=23431-b81c3g7dr67i&KRTB&23446-b81c3g7dr67i&KRTB&23465-b81c3g7dr67i; KRTBCOOKIE_1290=23368-vkf3yv9lbbl; KRTBCOOKIE_22=14911-4554572065121110164&KRTB&23150-4554572065121110164; KRTBCOOKIE_860=16335-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23334-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23417-YGAqDU1zUTdjyAFxCoe3kctlNPo&KRTB&23426-YGAqDU1zUTdjyAFxCoe3kctlNPo; KRTBCOOKIE_904=16787-KwJwE7NkCZClNJRysN2iYg; KRTBCOOKIE_1159=23138-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23328-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23427-5545f53f3d6e4ec199d8ed627ff026f3&KRTB&23445-5545f53f3d6e4ec199d8ed627ff026f3; KRTBCOOKIE_32=11175-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22713-AQEI_1QecY2ESAIjEW6KAQEBAQE&KRTB&22715-AQEI_1QecY2ESAIjEW6KAQEBAQE; SyncRTB3=1685577600%3A35%7C1685491200%3A107_21_71_56_204_247_165_231_233_179_22_209_54_254_238_96_99_220_7_214_13_3_8_234_176_46_5%7C1684886400%3A2_223_15%7C1689465600%3A69%7C1685145600%3A63; KRTBCOOKIE_107=1471-uid:EK38R0PM1NQR0H5&KRTB&23421-uid:EK38R0PM1NQR0H5; KRTBCOOKIE_594=17105-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004&KRTB&17107-RX-447a6332-530e-456a-97f4-3f0fd1ed48c9-004; SPugT=1684310122; chkChromeAb67Sec=133; KRTBCOOKIE_699=22727-AAFy2k7FBosAAEasbJoXnw; PugT=1684310473; origin=go-test`) + return r + }(), + Body: []byte(`{"ext":{"wrapper":{"profileid":5890,"versionid":1}}}`), + }, + setup: func(mme *mock_metrics.MockMetricsEngine) {}, + }, + want: hookstage.HookResult[hookstage.EntrypointPayload]{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + ProfileID: 5890, + DisplayID: 1, + SSAuction: -1, + 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: "5890", + Endpoint: models.EndpointV25, + MetricsEngine: mockEngine, + SeatNonBids: make(map[string][]openrtb_ext.NonBid), + }, + }, + }, + wantErr: nil, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -223,6 +503,10 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { if tt.want.ModuleContext != nil { // validate runtime values individually and reset them gotRctx := got.ModuleContext["rctx"].(models.RequestCtx) + if gotRctx.Endpoint == models.EndpointHybrid || gotRctx.Sshb == "1" { + assert.Equal(t, got, tt.want) + return + } assert.NotEmpty(t, gotRctx.StartTime) gotRctx.StartTime = 0 diff --git a/modules/pubmatic/openwrap/models/constants.go b/modules/pubmatic/openwrap/models/constants.go index 9f2f962cc85..58f67e9cfb5 100755 --- a/modules/pubmatic/openwrap/models/constants.go +++ b/modules/pubmatic/openwrap/models/constants.go @@ -408,7 +408,9 @@ const ( EndpointJson = "json" EndpointORTB = "ortb" EndpointVAST = "vast" + EndpointOWS2S = "ows2s" EndPointCTV = "ctv" + EndpointHybrid = "hybrid" Openwrap = "openwrap" ImpTypeBanner = "banner" ImpTypeVideo = "video" diff --git a/modules/pubmatic/openwrap/models/openwrap.go b/modules/pubmatic/openwrap/models/openwrap.go index d33107f5469..04ee42d3cbf 100644 --- a/modules/pubmatic/openwrap/models/openwrap.go +++ b/modules/pubmatic/openwrap/models/openwrap.go @@ -78,7 +78,8 @@ type RequestCtx struct { Endpoint string PubIDStr, ProfileIDStr string // TODO: remove this once we completely move away from header-bidding MetricsEngine metrics.MetricsEngine - ReturnAllBidStatus bool // ReturnAllBidStatus stores the value of request.ext.prebid.returnallbidstatus + ReturnAllBidStatus bool // ReturnAllBidStatus stores the value of request.ext.prebid.returnallbidstatus + Sshb string //Sshb query param to identify that the request executed heder-bidding or not, sshb=1(executed HB(8001)), sshb=2(reverse proxy set from HB(8001->8000)), sshb=""(direct request(8000)). } type OwBid struct { diff --git a/modules/pubmatic/openwrap/models/request.go b/modules/pubmatic/openwrap/models/request.go index 43a5f2c800e..e8f454e8e15 100644 --- a/modules/pubmatic/openwrap/models/request.go +++ b/modules/pubmatic/openwrap/models/request.go @@ -41,8 +41,9 @@ type ImpExtension struct { Bidder map[string]*BidderExtension `json:"bidder,omitempty"` SKAdnetwork json.RawMessage `json:"skadn,omitempty"` - Data json.RawMessage `json:"data,omitempty"` + Data openrtb_ext.ExtImpData `json:"data,omitempty"` Prebid openrtb_ext.ExtImpPrebid `json:"prebid,omitempty"` + Gpid string `json:"gpid,omitempty"` } // BidderExtension - Bidder specific items From 7afb162cbfc42be8b4f925803dea50ec270346d3 Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Thu, 9 Nov 2023 09:31:39 +0530 Subject: [PATCH 21/30] UOE-9681: fix pubmatic2 params (#641) --- modules/pubmatic/openwrap/bidderparams/pubmatic.go | 6 ++++++ modules/pubmatic/openwrap/bidderparams/pubmatic_test.go | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/pubmatic/openwrap/bidderparams/pubmatic.go b/modules/pubmatic/openwrap/bidderparams/pubmatic.go index e6fc965cee3..cf440a14a35 100644 --- a/modules/pubmatic/openwrap/bidderparams/pubmatic.go +++ b/modules/pubmatic/openwrap/bidderparams/pubmatic.go @@ -13,6 +13,12 @@ import ( func PreparePubMaticParamsV25(rctx models.RequestCtx, cache cache.Cache, bidRequest openrtb2.BidRequest, imp openrtb2.Imp, impExt models.ImpExtension, partnerID int) (string, string, bool, []byte, error) { wrapExt := fmt.Sprintf(`{"%s":%d,"%s":%d}`, models.SS_PM_VERSION_ID, rctx.DisplayID, models.SS_PM_PROFILE_ID, rctx.ProfileID) + + // change profile id for pubmatic2 + if secondaryProfileID, ok := rctx.PartnerConfigMap[partnerID][models.KEY_PROFILE_ID]; ok { + wrapExt = fmt.Sprintf(`{"%s":0,"%s":%s}`, models.SS_PM_VERSION_ID, models.SS_PM_PROFILE_ID, secondaryProfileID) + } + extImpPubMatic := openrtb_ext.ExtImpPubmatic{ PublisherId: strconv.Itoa(rctx.PubID), WrapExt: json.RawMessage(wrapExt), diff --git a/modules/pubmatic/openwrap/bidderparams/pubmatic_test.go b/modules/pubmatic/openwrap/bidderparams/pubmatic_test.go index 728fb2c6880..62788ad0429 100644 --- a/modules/pubmatic/openwrap/bidderparams/pubmatic_test.go +++ b/modules/pubmatic/openwrap/bidderparams/pubmatic_test.go @@ -322,6 +322,7 @@ func TestPreparePubMaticParamsV25(t *testing.T) { models.TIMEOUT: "200", models.KEY_GEN_PATTERN: "_AU_@_DIV_@_W_x_H_", models.SERVER_SIDE_FLAG: "1", + models.KEY_PROFILE_ID: "1323", }, }, }, @@ -369,7 +370,7 @@ func TestPreparePubMaticParamsV25(t *testing.T) { matchedSlot: "/Test_Adunit1234@Div1@200x300", matchedPattern: "", isRegexSlot: false, - params: []byte(`{"publisherId":"5890","adSlot":"/Test_Adunit1234@DIV1@200x300","wrapper":{"version":1,"profile":123},"keywords":[{"key":"test_key1","value":["test_value1","test_value2"]},{"key":"test_key2","value":["test_value1","test_value2"]}]}`), + params: []byte(`{"publisherId":"5890","adSlot":"/Test_Adunit1234@DIV1@200x300","wrapper":{"version":0,"profile":1323},"keywords":[{"key":"test_key1","value":["test_value1","test_value2"]},{"key":"test_key2","value":["test_value1","test_value2"]}]}`), wantErr: false, }, }, From 7cffa2e88326df01f0572702d27326098a7226c2 Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Fri, 10 Nov 2023 14:03:31 +0530 Subject: [PATCH 22/30] UOE-9681: mapping not uploaded nbr (#643) --- .../pubmatic/openwrap/beforevalidationhook.go | 19 ++++- .../openwrap/beforevalidationhook_test.go | 74 +++++++++++++++++++ modules/pubmatic/openwrap/models/nbr/codes.go | 1 + modules/pubmatic/openwrap/util.go | 2 +- 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go index 3256cda3a29..58052533c99 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook.go +++ b/modules/pubmatic/openwrap/beforevalidationhook.go @@ -157,7 +157,7 @@ func (m OpenWrap) handleBeforeValidationHook( } isAdPodRequest := false - disabledSlots := 0 + disabledSlots, candidatePartners, nonMappedPartners := 0, 0, 0 serviceSideBidderPresent := false aliasgvlids := make(map[string]uint16) @@ -269,6 +269,8 @@ func (m OpenWrap) handleBeforeValidationHook( continue } + candidatePartners++ + var isRegex bool var slot, kgpv string var bidderParams json.RawMessage @@ -282,8 +284,13 @@ func (m OpenWrap) handleBeforeValidationHook( slot, kgpv, isRegex, bidderParams, err = bidderparams.PrepareAdapterParamsV25(rCtx, m.cache, *payload.BidRequest, imp, *impExt, partnerID) } - if err != nil || len(bidderParams) == 0 { - result.Errors = append(result.Errors, fmt.Sprintf("no bidder params found for imp:%s partner: %s", imp.ID, prebidBidderCode)) + if err != nil || len(slot) == 0 || len(bidderParams) == 0 { + nonMappedPartners++ + if len(slot) == 0 { + result.Errors = append(result.Errors, fmt.Sprintf("mappings not found for imp:%s partner: %s", imp.ID, prebidBidderCode)) + } else { + result.Errors = append(result.Errors, fmt.Sprintf("no bidder params found for imp:%s partner: %s", imp.ID, prebidBidderCode)) + } nonMapped[bidderCode] = struct{}{} m.metricEngine.RecordPartnerConfigErrors(rCtx.PubIDStr, rCtx.ProfileIDStr, bidderCode, models.PartnerErrSlotNotMapped) continue @@ -387,6 +394,12 @@ func (m OpenWrap) handleBeforeValidationHook( } if !serviceSideBidderPresent { + if candidatePartners != 0 && candidatePartners == nonMappedPartners { + result.NbrCode = nbr.SlotNotMapped + result.Errors = append(result.Errors, "slot not mapped") + return result, nil + } + result.NbrCode = nbr.ServerSidePartnerNotConfigured if err != nil { err = errors.New("server side partner not found: " + err.Error()) diff --git a/modules/pubmatic/openwrap/beforevalidationhook_test.go b/modules/pubmatic/openwrap/beforevalidationhook_test.go index e6922046ce0..4b8d53c63a7 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook_test.go +++ b/modules/pubmatic/openwrap/beforevalidationhook_test.go @@ -2256,6 +2256,80 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { wantErr: false, isRequestNotRejected: true, }, + { + name: "draft profile (mapping not uploaded)", + 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","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().GetMappingsFromCacheV25(gomock.Any(), gomock.Any()).Return(nil) + 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{}) + //prometheus metrics + mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") + mockEngine.EXPECT().RecordPublisherRequests(rctx.Endpoint, "5890", rctx.Platform) + mockEngine.EXPECT().RecordPartnerConfigErrors("5890", "1234", "appnexus", models.PartnerErrSlotNotMapped) + mockEngine.EXPECT().RecordBadRequests("v25", 6) + mockEngine.EXPECT().RecordNobidErrPrebidServerRequests("5890", 613) + }, + want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ + Reject: true, + NbrCode: 613, + Errors: []string{"mappings not found for imp:123 partner: appnexus", "slot not mapped"}, + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/modules/pubmatic/openwrap/models/nbr/codes.go b/modules/pubmatic/openwrap/models/nbr/codes.go index aeacbaea6d0..53fc8066bf0 100644 --- a/modules/pubmatic/openwrap/models/nbr/codes.go +++ b/modules/pubmatic/openwrap/models/nbr/codes.go @@ -15,4 +15,5 @@ const ( InternalError AllSlotsDisabled ServerSidePartnerNotConfigured + SlotNotMapped ) diff --git a/modules/pubmatic/openwrap/util.go b/modules/pubmatic/openwrap/util.go index e2522ef6ca8..1f3357748b6 100644 --- a/modules/pubmatic/openwrap/util.go +++ b/modules/pubmatic/openwrap/util.go @@ -276,7 +276,7 @@ func getPubmaticErrorCode(standardNBR int) int { case nbr.InvalidImpressionTagID: return 605 // ErrMissingTagID - case nbr.InvalidProfileConfiguration, nbr.InvalidPlatform, nbr.AllSlotsDisabled, nbr.ServerSidePartnerNotConfigured: + case nbr.InvalidProfileConfiguration, nbr.InvalidPlatform, nbr.AllSlotsDisabled, nbr.ServerSidePartnerNotConfigured, nbr.SlotNotMapped: return 6 // ErrInvalidConfiguration case nbr.InternalError: From 3eb9b5cde9b61db48e0c2e618aef80a64ca0cd59 Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Fri, 10 Nov 2023 15:29:05 +0530 Subject: [PATCH 23/30] UOE-9681: fix eg, en multicurrency case (#644) --- .../pubmatic/openwrap/auctionresponsehook.go | 117 ++++++++++-------- modules/pubmatic/openwrap/models/openwrap.go | 5 + modules/pubmatic/openwrap/tracker/create.go | 15 +-- 3 files changed, 73 insertions(+), 64 deletions(-) diff --git a/modules/pubmatic/openwrap/auctionresponsehook.go b/modules/pubmatic/openwrap/auctionresponsehook.go index b678413ad9f..35e8db508e2 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook.go +++ b/modules/pubmatic/openwrap/auctionresponsehook.go @@ -81,80 +81,85 @@ func (m OpenWrap) handleAuctionResponseHook( result.Errors = append(result.Errors, "invalid impCtx.ID for bid"+bid.ImpID) continue } + partnerID := 0 if bidderMeta, ok := impCtx.Bidders[seatBid.Seat]; ok { partnerID = bidderMeta.PartnerID } - revShare := models.GetRevenueShare(rctx.PartnerConfigMap[partnerID]) - price := bid.Price - + var eg, en float64 bidExt := &models.BidExt{} - if len(bid.Ext) != 0 { //NYC_TODO: most of the fields should be filled even if unmarshal fails + + if len(bid.Ext) != 0 { err := json.Unmarshal(bid.Ext, bidExt) if err != nil { result.Errors = append(result.Errors, "failed to unmarshal bid.ext for "+bid.ID) // continue } + } - // NYC_TODO: fix this in PBS-Core or ExecuteAllProcessedBidResponsesStage - if bidExt.Prebid != nil && bidExt.Prebid.Video != nil && bidExt.Prebid.Video.Duration == 0 && - bidExt.Prebid.Video.PrimaryCategory == "" && bidExt.Prebid.Video.VASTTagID == "" { - bidExt.Prebid.Video = nil - } + // NYC_TODO: fix this in PBS-Core or ExecuteAllProcessedBidResponsesStage + if bidExt.Prebid != nil && bidExt.Prebid.Video != nil && bidExt.Prebid.Video.Duration == 0 && + bidExt.Prebid.Video.PrimaryCategory == "" && bidExt.Prebid.Video.VASTTagID == "" { + bidExt.Prebid.Video = nil + } - if v, ok := rctx.PartnerConfigMap[models.VersionLevelConfigID]["refreshInterval"]; ok { - n, err := strconv.Atoi(v) - if err == nil { - bidExt.RefreshInterval = n - } + if v, ok := rctx.PartnerConfigMap[models.VersionLevelConfigID]["refreshInterval"]; ok { + n, err := strconv.Atoi(v) + if err == nil { + bidExt.RefreshInterval = n } + } + + if bidExt.Prebid != nil { + bidExt.CreativeType = string(bidExt.Prebid.Type) + } + if bidExt.CreativeType == "" { + bidExt.CreativeType = models.GetAdFormat(bid.AdM) + } + + // set response netecpm and logger/tracker en + revShare := models.GetRevenueShare(rctx.PartnerConfigMap[partnerID]) + bidExt.NetECPM = models.GetNetEcpm(bid.Price, revShare) + eg = bid.Price + en = bidExt.NetECPM + if payload.BidResponse.Cur != "USD" { + eg = bidExt.OriginalBidCPMUSD + en = models.GetNetEcpm(bidExt.OriginalBidCPMUSD, revShare) + bidExt.OriginalBidCPMUSD = 0 + } - if bidExt.Prebid != nil { - bidExt.CreativeType = string(bidExt.Prebid.Type) + if impCtx.Video != nil && impCtx.Type == "video" && bidExt.CreativeType == "video" { + if bidExt.Video == nil { + bidExt.Video = &models.ExtBidVideo{} } - if bidExt.CreativeType == "" { - bidExt.CreativeType = models.GetAdFormat(bid.AdM) + if impCtx.Video.MaxDuration != 0 { + bidExt.Video.MaxDuration = impCtx.Video.MaxDuration } - - if payload.BidResponse.Cur != "USD" { - price = bidExt.OriginalBidCPMUSD + if impCtx.Video.MinDuration != 0 { + bidExt.Video.MinDuration = impCtx.Video.MinDuration } - - bidExt.NetECPM = models.GetNetEcpm(price, revShare) - - if impCtx.Video != nil && impCtx.Type == "video" && bidExt.CreativeType == "video" { - if bidExt.Video == nil { - bidExt.Video = &models.ExtBidVideo{} - } - if impCtx.Video.MaxDuration != 0 { - bidExt.Video.MaxDuration = impCtx.Video.MaxDuration - } - if impCtx.Video.MinDuration != 0 { - bidExt.Video.MinDuration = impCtx.Video.MinDuration - } - if impCtx.Video.Skip != nil { - bidExt.Video.Skip = impCtx.Video.Skip - } - if impCtx.Video.SkipAfter != 0 { - bidExt.Video.SkipAfter = impCtx.Video.SkipAfter - } - if impCtx.Video.SkipMin != 0 { - bidExt.Video.SkipMin = impCtx.Video.SkipMin - } - bidExt.Video.BAttr = impCtx.Video.BAttr - bidExt.Video.PlaybackMethod = impCtx.Video.PlaybackMethod - if rctx.ClientConfigFlag == 1 { - bidExt.Video.ClientConfig = adunitconfig.GetClientConfigForMediaType(rctx, bid.ImpID, "video") - } - } else if impCtx.Banner && bidExt.CreativeType == "banner" && rctx.ClientConfigFlag == 1 { - cc := adunitconfig.GetClientConfigForMediaType(rctx, bid.ImpID, "banner") - if len(cc) != 0 { - if bidExt.Banner == nil { - bidExt.Banner = &models.ExtBidBanner{} - } - bidExt.Banner.ClientConfig = cc + if impCtx.Video.Skip != nil { + bidExt.Video.Skip = impCtx.Video.Skip + } + if impCtx.Video.SkipAfter != 0 { + bidExt.Video.SkipAfter = impCtx.Video.SkipAfter + } + if impCtx.Video.SkipMin != 0 { + bidExt.Video.SkipMin = impCtx.Video.SkipMin + } + bidExt.Video.BAttr = impCtx.Video.BAttr + bidExt.Video.PlaybackMethod = impCtx.Video.PlaybackMethod + if rctx.ClientConfigFlag == 1 { + bidExt.Video.ClientConfig = adunitconfig.GetClientConfigForMediaType(rctx, bid.ImpID, "video") + } + } else if impCtx.Banner && bidExt.CreativeType == "banner" && rctx.ClientConfigFlag == 1 { + cc := adunitconfig.GetClientConfigForMediaType(rctx, bid.ImpID, "banner") + if len(cc) != 0 { + if bidExt.Banner == nil { + bidExt.Banner = &models.ExtBidBanner{} } + bidExt.Banner.ClientConfig = cc } } @@ -179,6 +184,8 @@ func (m OpenWrap) handleAuctionResponseHook( } impCtx.BidCtx[bid.ID] = models.BidCtx{ BidExt: *bidExt, + EG: eg, + EN: en, } rctx.ImpBidCtx[bid.ImpID] = impCtx } diff --git a/modules/pubmatic/openwrap/models/openwrap.go b/modules/pubmatic/openwrap/models/openwrap.go index 04ee42d3cbf..77f689e0cf2 100644 --- a/modules/pubmatic/openwrap/models/openwrap.go +++ b/modules/pubmatic/openwrap/models/openwrap.go @@ -135,6 +135,11 @@ type PartnerData struct { type BidCtx struct { BidExt + + // EG gross net in USD for tracker and logger + EG float64 + // EN gross net in USD for tracker and logger + EN float64 } type AdUnitCtx struct { diff --git a/modules/pubmatic/openwrap/tracker/create.go b/modules/pubmatic/openwrap/tracker/create.go index c20eeb70b49..61788b707c9 100644 --- a/modules/pubmatic/openwrap/tracker/create.go +++ b/modules/pubmatic/openwrap/tracker/create.go @@ -36,9 +36,8 @@ func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) m } tagid := "" - netECPM := float64(0) + var eg, en float64 matchedSlot := "" - price := bid.Price isRewardInventory := 0 partnerID := seatBid.Seat bidType := "banner" @@ -54,10 +53,6 @@ func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) m } if bidCtx, ok := impCtx.BidCtx[bid.ID]; ok { - if bidResponse.Cur != "USD" { - price = bidCtx.OriginalBidCPMUSD - } - netECPM = bidCtx.NetECPM // TODO do most calculation in wt // marketplace/alternatebiddercodes feature @@ -73,6 +68,8 @@ func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) m } bidType = bidCtx.CreativeType dspId = bidCtx.DspId + eg = bidCtx.EG + en = bidCtx.EN } _ = matchedSlot @@ -136,8 +133,8 @@ func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) m BidID: utils.GetOriginalBidId(bid.ID), OrigBidID: utils.GetOriginalBidId(bid.ID), KGPV: kgpv, - NetECPM: float64(netECPM), - GrossECPM: models.GetGrossEcpm(price), + GrossECPM: eg, + NetECPM: en, } if len(bid.ADomain) != 0 { @@ -157,7 +154,7 @@ func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) m trackers[bid.ID] = models.OWTracker{ Tracker: tracker, TrackerURL: finalTrackerURL, - Price: price, + Price: bid.Price, PriceModel: models.VideoPricingModelCPM, PriceCurrency: bidResponse.Cur, ErrorURL: ConstructVideoErrorURL(rctx, rctx.VideoErrorTrackerEndpoint, bid, tracker), From c49251a02e21dbf141714b31b2588b20fad54373 Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Mon, 20 Nov 2023 11:10:15 +0530 Subject: [PATCH 24/30] Revert "UOE-9681: mapping not uploaded nbr (#643)" (#645) This reverts commit 7cffa2e88326df01f0572702d27326098a7226c2. --- .../pubmatic/openwrap/beforevalidationhook.go | 19 +---- .../openwrap/beforevalidationhook_test.go | 74 ------------------- modules/pubmatic/openwrap/models/nbr/codes.go | 1 - modules/pubmatic/openwrap/util.go | 2 +- 4 files changed, 4 insertions(+), 92 deletions(-) diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go index 58052533c99..3256cda3a29 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook.go +++ b/modules/pubmatic/openwrap/beforevalidationhook.go @@ -157,7 +157,7 @@ func (m OpenWrap) handleBeforeValidationHook( } isAdPodRequest := false - disabledSlots, candidatePartners, nonMappedPartners := 0, 0, 0 + disabledSlots := 0 serviceSideBidderPresent := false aliasgvlids := make(map[string]uint16) @@ -269,8 +269,6 @@ func (m OpenWrap) handleBeforeValidationHook( continue } - candidatePartners++ - var isRegex bool var slot, kgpv string var bidderParams json.RawMessage @@ -284,13 +282,8 @@ func (m OpenWrap) handleBeforeValidationHook( slot, kgpv, isRegex, bidderParams, err = bidderparams.PrepareAdapterParamsV25(rCtx, m.cache, *payload.BidRequest, imp, *impExt, partnerID) } - if err != nil || len(slot) == 0 || len(bidderParams) == 0 { - nonMappedPartners++ - if len(slot) == 0 { - result.Errors = append(result.Errors, fmt.Sprintf("mappings not found for imp:%s partner: %s", imp.ID, prebidBidderCode)) - } else { - result.Errors = append(result.Errors, fmt.Sprintf("no bidder params found for imp:%s partner: %s", imp.ID, prebidBidderCode)) - } + if err != nil || len(bidderParams) == 0 { + result.Errors = append(result.Errors, fmt.Sprintf("no bidder params found for imp:%s partner: %s", imp.ID, prebidBidderCode)) nonMapped[bidderCode] = struct{}{} m.metricEngine.RecordPartnerConfigErrors(rCtx.PubIDStr, rCtx.ProfileIDStr, bidderCode, models.PartnerErrSlotNotMapped) continue @@ -394,12 +387,6 @@ func (m OpenWrap) handleBeforeValidationHook( } if !serviceSideBidderPresent { - if candidatePartners != 0 && candidatePartners == nonMappedPartners { - result.NbrCode = nbr.SlotNotMapped - result.Errors = append(result.Errors, "slot not mapped") - return result, nil - } - result.NbrCode = nbr.ServerSidePartnerNotConfigured if err != nil { err = errors.New("server side partner not found: " + err.Error()) diff --git a/modules/pubmatic/openwrap/beforevalidationhook_test.go b/modules/pubmatic/openwrap/beforevalidationhook_test.go index 4b8d53c63a7..e6922046ce0 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook_test.go +++ b/modules/pubmatic/openwrap/beforevalidationhook_test.go @@ -2256,80 +2256,6 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { wantErr: false, isRequestNotRejected: true, }, - { - name: "draft profile (mapping not uploaded)", - 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","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().GetMappingsFromCacheV25(gomock.Any(), gomock.Any()).Return(nil) - 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{}) - //prometheus metrics - mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") - mockEngine.EXPECT().RecordPublisherRequests(rctx.Endpoint, "5890", rctx.Platform) - mockEngine.EXPECT().RecordPartnerConfigErrors("5890", "1234", "appnexus", models.PartnerErrSlotNotMapped) - mockEngine.EXPECT().RecordBadRequests("v25", 6) - mockEngine.EXPECT().RecordNobidErrPrebidServerRequests("5890", 613) - }, - want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ - Reject: true, - NbrCode: 613, - Errors: []string{"mappings not found for imp:123 partner: appnexus", "slot not mapped"}, - }, - wantErr: false, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/modules/pubmatic/openwrap/models/nbr/codes.go b/modules/pubmatic/openwrap/models/nbr/codes.go index 53fc8066bf0..aeacbaea6d0 100644 --- a/modules/pubmatic/openwrap/models/nbr/codes.go +++ b/modules/pubmatic/openwrap/models/nbr/codes.go @@ -15,5 +15,4 @@ const ( InternalError AllSlotsDisabled ServerSidePartnerNotConfigured - SlotNotMapped ) diff --git a/modules/pubmatic/openwrap/util.go b/modules/pubmatic/openwrap/util.go index 1f3357748b6..e2522ef6ca8 100644 --- a/modules/pubmatic/openwrap/util.go +++ b/modules/pubmatic/openwrap/util.go @@ -276,7 +276,7 @@ func getPubmaticErrorCode(standardNBR int) int { case nbr.InvalidImpressionTagID: return 605 // ErrMissingTagID - case nbr.InvalidProfileConfiguration, nbr.InvalidPlatform, nbr.AllSlotsDisabled, nbr.ServerSidePartnerNotConfigured, nbr.SlotNotMapped: + case nbr.InvalidProfileConfiguration, nbr.InvalidPlatform, nbr.AllSlotsDisabled, nbr.ServerSidePartnerNotConfigured: return 6 // ErrInvalidConfiguration case nbr.InternalError: From f275b8f79722d1389bc720349c990f68b7754344 Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Mon, 20 Nov 2023 15:08:55 +0530 Subject: [PATCH 25/30] OTT-1390,UOE-9175: add missing fields in owlogger & tracker so that OW module can run independently (#626) --- Makefile | 4 + analytics/pubmatic/helper.go | 136 +- analytics/pubmatic/helper_test.go | 220 + analytics/pubmatic/logger.go | 382 +- analytics/pubmatic/logger_test.go | 4367 +++++++++++++++++ analytics/pubmatic/{ => mhttp}/http_util.go | 32 +- analytics/pubmatic/mhttp/http_util_test.go | 162 + analytics/pubmatic/mhttp/mock/mock.go | 149 + analytics/pubmatic/models.go | 133 - analytics/pubmatic/pubmatic.go | 46 +- analytics/pubmatic/pubmatic_test.go | 79 + analytics/pubmatic/record.go | 121 +- analytics/pubmatic/record_test.go | 514 ++ exchange/exchange.go | 16 +- exchange/exchange_test.go | 78 +- exchange/seat_non_bids.go | 1 + go.mod | 4 +- go.sum | 424 +- modules/moduledeps/deps.go | 8 +- .../pubmatic/openwrap/adunitconfig/common.go | 3 +- .../pubmatic/openwrap/adunitconfig/utils.go | 3 +- .../pubmatic/openwrap/auctionresponsehook.go | 77 +- .../openwrap/auctionresponsehook_test.go | 941 ++++ .../pubmatic/openwrap/beforevalidationhook.go | 156 +- .../openwrap/beforevalidationhook_test.go | 682 ++- .../pubmatic/openwrap/bidderparams/common.go | 39 +- .../openwrap/bidderparams/common_test.go | 143 - .../openwrap/bidderparams/pubmatic.go | 2 +- modules/pubmatic/openwrap/defaultbids.go | 92 +- modules/pubmatic/openwrap/defaultbids_test.go | 87 + modules/pubmatic/openwrap/entrypointhook.go | 10 + .../pubmatic/openwrap/entrypointhook_test.go | 45 +- modules/pubmatic/openwrap/logger.go | 9 +- .../pubmatic/openwrap/matchedimpression.go | 37 +- .../openwrap/metrics/prometheus/prometheus.go | 17 +- modules/pubmatic/openwrap/models/constants.go | 33 +- modules/pubmatic/openwrap/models/openwrap.go | 45 +- .../pubmatic/openwrap/models/openwrap_test.go | 113 +- modules/pubmatic/openwrap/models/reponse.go | 10 +- modules/pubmatic/openwrap/models/request.go | 2 +- modules/pubmatic/openwrap/models/tracker.go | 50 +- modules/pubmatic/openwrap/models/tracking.go | 39 + modules/pubmatic/openwrap/models/utils.go | 217 +- .../pubmatic/openwrap/models/utils_test.go | 1291 +++++ modules/pubmatic/openwrap/nonbids.go | 34 + modules/pubmatic/openwrap/nonbids_test.go | 431 ++ modules/pubmatic/openwrap/openwrap.go | 17 +- modules/pubmatic/openwrap/targeting.go | 4 +- modules/pubmatic/openwrap/tracker/banner.go | 38 +- .../pubmatic/openwrap/tracker/banner_test.go | 301 ++ modules/pubmatic/openwrap/tracker/create.go | 254 +- .../pubmatic/openwrap/tracker/create_test.go | 846 ++++ modules/pubmatic/openwrap/tracker/inject.go | 53 +- .../pubmatic/openwrap/tracker/inject_test.go | 688 +++ modules/pubmatic/openwrap/tracker/models.go | 8 + .../pubmatic/openwrap/tracker/models_test.go | 1 + modules/pubmatic/openwrap/tracker/native.go | 77 + .../pubmatic/openwrap/tracker/native_test.go | 331 ++ modules/pubmatic/openwrap/tracker/tracker.go | 26 +- .../pubmatic/openwrap/tracker/tracker_test.go | 87 + .../pubmatic/openwrap/tracker/video_test.go | 565 +++ modules/pubmatic/openwrap/util.go | 29 +- modules/pubmatic/openwrap/util_test.go | 98 +- openrtb_ext/response.go | 1 + router/router.go | 2 +- 65 files changed, 13245 insertions(+), 1665 deletions(-) create mode 100644 analytics/pubmatic/helper_test.go create mode 100644 analytics/pubmatic/logger_test.go rename analytics/pubmatic/{ => mhttp}/http_util.go (90%) create mode 100644 analytics/pubmatic/mhttp/http_util_test.go create mode 100644 analytics/pubmatic/mhttp/mock/mock.go delete mode 100644 analytics/pubmatic/models.go create mode 100644 analytics/pubmatic/pubmatic_test.go create mode 100644 analytics/pubmatic/record_test.go create mode 100644 modules/pubmatic/openwrap/defaultbids_test.go create mode 100644 modules/pubmatic/openwrap/tracker/banner_test.go create mode 100644 modules/pubmatic/openwrap/tracker/create_test.go create mode 100644 modules/pubmatic/openwrap/tracker/inject_test.go create mode 100644 modules/pubmatic/openwrap/tracker/models.go create mode 100644 modules/pubmatic/openwrap/tracker/models_test.go create mode 100644 modules/pubmatic/openwrap/tracker/native.go create mode 100644 modules/pubmatic/openwrap/tracker/native_test.go create mode 100644 modules/pubmatic/openwrap/tracker/tracker_test.go create mode 100644 modules/pubmatic/openwrap/tracker/video_test.go diff --git a/Makefile b/Makefile index 8d92509a9f3..402bfa10a96 100644 --- a/Makefile +++ b/Makefile @@ -52,3 +52,7 @@ mockgencache: mockgenmetrics: mkdir -p modules/pubmatic/openwrap/metrics/mock mockgen github.com/PubMatic-OpenWrap/prebid-server/modules/pubmatic/openwrap/metrics MetricsEngine > modules/pubmatic/openwrap/metrics/mock/mock.go + +mockgenlogger: + mkdir -p analytics/pubmatic/mhttp/mock + mockgen github.com/PubMatic-OpenWrap/prebid-server/analytics/pubmatic/mhttp HttpCallInterface,MultiHttpContextInterface > analytics/pubmatic/mhttp/mock/mock.go \ No newline at end of file diff --git a/analytics/pubmatic/helper.go b/analytics/pubmatic/helper.go index 272ab9c5efb..c8907410a42 100644 --- a/analytics/pubmatic/helper.go +++ b/analytics/pubmatic/helper.go @@ -2,42 +2,21 @@ package pubmatic import ( "encoding/json" - "errors" - "fmt" "net/http" "net/url" "strconv" + "time" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/golang/glog" + "github.com/prebid/prebid-server/analytics/pubmatic/mhttp" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" ) -// Send method -func Send(url string, headers http.Header) error { - mhc := NewMultiHttpContext() - hc, err := NewHttpCall(url, "") - if err != nil { - return err - } - - for k, v := range headers { - if len(v) != 0 { - hc.AddHeader(k, v[0]) - } - } - - mhc.AddHttpCall(hc) - _, erc := mhc.Execute() - if erc != 0 { - return errors.New("error in sending logger pixel") - } - - return nil -} - // PrepareLoggerURL returns the url for OW logger call func PrepareLoggerURL(wlog *WloggerRecord, loggerURL string, gdprEnabled int) string { + if wlog == nil { + return "" + } v := url.Values{} jsonString, err := json.Marshal(wlog.record) @@ -56,87 +35,40 @@ func PrepareLoggerURL(wlog *WloggerRecord, loggerURL string, gdprEnabled int) st return finalLoggerURL } -func (wlog *WloggerRecord) logContentObject(content *openrtb2.Content) { - if nil == content { - return - } - - wlog.Content = &Content{ - ID: content.ID, - Episode: int(content.Episode), - Title: content.Title, - Series: content.Series, - Season: content.Season, - Cat: content.Cat, - } -} -func getSizeForPlatform(width, height int64, platform string) string { - s := models.GetSize(width, height) - if platform == models.PLATFORM_VIDEO { - s = s + models.VideoSizeSuffix +// getGdprEnabledFlag returns gdpr flag set in the partner config +func getGdprEnabledFlag(partnerConfigMap map[int]map[string]string) int { + gdpr := 0 + if val := partnerConfigMap[models.VersionLevelConfigID][models.GDPR_ENABLED]; val != "" { + gdpr, _ = strconv.Atoi(val) } - return s + return gdpr } -// set partnerRecord MetaData -func (partnerRecord *PartnerRecord) setMetaDataObject(meta *openrtb_ext.ExtBidPrebidMeta) { +// send function will send the owlogger to analytics endpoint +func send(rCtx *models.RequestCtx, url string, headers http.Header, mhc mhttp.MultiHttpContextInterface) { + startTime := time.Now() + hc, _ := mhttp.NewHttpCall(url, "") - if meta.NetworkID != 0 || meta.AdvertiserID != 0 || len(meta.SecondaryCategoryIDs) > 0 { - partnerRecord.MetaData = &MetaData{ - NetworkID: meta.NetworkID, - AdvertiserID: meta.AdvertiserID, - PrimaryCategoryID: meta.PrimaryCategoryID, - AgencyID: meta.AgencyID, - DemandSource: meta.DemandSource, - SecondaryCategoryIDs: meta.SecondaryCategoryIDs, + for k, v := range headers { + if len(v) != 0 { + hc.AddHeader(k, v[0]) } } - //NOTE : We Don't get following Data points in Response, whenever got from translator, - //they can be populated. - //partnerRecord.MetaData.NetworkName = meta.NetworkName - //partnerRecord.MetaData.AdvertiserName = meta.AdvertiserName - //partnerRecord.MetaData.AgencyName = meta.AgencyName - //partnerRecord.MetaData.BrandName = meta.BrandName - //partnerRecord.MetaData.BrandID = meta.BrandID - //partnerRecord.MetaData.DChain = meta.DChain (type is json.RawMessage) -} -// Harcode would be the optimal. We could make it configurable like _AU_@_W_x_H_:%s@%dx%d entries in pbs.yaml -// mysql> SELECT DISTINCT key_gen_pattern FROM wrapper_mapping_template; -// +----------------------+ -// | key_gen_pattern | -// +----------------------+ -// | _AU_@_W_x_H_ | -// | _DIV_@_W_x_H_ | -// | _W_x_H_@_W_x_H_ | -// | _DIV_ | -// | _AU_@_DIV_@_W_x_H_ | -// | _AU_@_SRC_@_VASTTAG_ | -// +----------------------+ -// 6 rows in set (0.21 sec) -func GenerateSlotName(h, w int64, kgp, tagid, div, src string) string { - // func (H, W, Div), no need to validate, will always be non-nil - switch kgp { - case "_AU_": // adunitconfig - return tagid - case "_DIV_": - return div - case "_AU_@_W_x_H_": - return fmt.Sprintf("%s@%dx%d", tagid, w, h) - case "_DIV_@_W_x_H_": - return fmt.Sprintf("%s@%dx%d", div, w, h) - case "_W_x_H_@_W_x_H_": - return fmt.Sprintf("%dx%d@%dx%d", w, h, w, h) - case "_AU_@_DIV_@_W_x_H_": - if div == "" { - return fmt.Sprintf("%s@%s@s%dx%d", tagid, div, w, h) - } - return fmt.Sprintf("%s@%s@s%dx%d", tagid, div, w, h) - case "_AU_@_SRC_@_VASTTAG_": - return fmt.Sprintf("%s@%s@s_VASTTAG_", tagid, src) //TODO check where/how _VASTTAG_ is updated - default: - // TODO: check if we need to fallback to old generic flow (below) - // Add this cases in a map and read it from yaml file + if rCtx.KADUSERCookie != nil { + hc.AddCookie(models.KADUSERCOOKIE, rCtx.KADUSERCookie.Value) + } + + mhc.AddHttpCall(hc) + _, erc := mhc.Execute() + if erc != 0 { + glog.Errorf("Failed to send the owlogger for pub:[%d], profile:[%d], version:[%d].", + rCtx.PubID, rCtx.ProfileID, rCtx.VersionID) + + // we will not record at version level in prometheus metric + rCtx.MetricsEngine.RecordPublisherWrapperLoggerFailure(rCtx.PubIDStr, rCtx.ProfileIDStr, "") + return } - return "" + rCtx.MetricsEngine.RecordSendLoggerDataTime(rCtx.Endpoint, rCtx.ProfileIDStr, time.Since(startTime)) + // TODO: this will increment HB specific metric (ow_pbs_sshb_*), verify labels } diff --git a/analytics/pubmatic/helper_test.go b/analytics/pubmatic/helper_test.go new file mode 100644 index 00000000000..c4464c08d80 --- /dev/null +++ b/analytics/pubmatic/helper_test.go @@ -0,0 +1,220 @@ +package pubmatic + +import ( + "net/http" + "net/url" + "testing" + + "github.com/golang/mock/gomock" + "github.com/prebid/prebid-server/analytics/pubmatic/mhttp" + mock_mhttp "github.com/prebid/prebid-server/analytics/pubmatic/mhttp/mock" + mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/stretchr/testify/assert" +) + +func TestPrepareLoggerURL(t *testing.T) { + type args struct { + wlog *WloggerRecord + loggerURL string + gdprEnabled int + } + tests := []struct { + name string + args args + owlogger string + }{ + { + name: "nil_wlog", + args: args{ + wlog: nil, + loggerURL: "http://t.pubmatic.com/wl", + gdprEnabled: 1, + }, + owlogger: "", + }, + { + name: "gdprEnabled=1", + args: args{ + wlog: &WloggerRecord{ + record: record{ + PubID: 10, + ProfileID: "1", + VersionID: "0", + }, + }, + loggerURL: "http://t.pubmatic.com/wl", + gdprEnabled: 1, + }, + owlogger: `http://t.pubmatic.com/wl?gdEn=1&json={"pubid":10,"pid":"1","pdvid":"0","dvc":{},"ft":0}&pubid=10`, + }, + { + name: "gdprEnabled=0", + args: args{ + wlog: &WloggerRecord{ + record: record{ + PubID: 10, + ProfileID: "1", + VersionID: "0", + }, + }, + loggerURL: "http://t.pubmatic.com/wl", + gdprEnabled: 0, + }, + owlogger: `http://t.pubmatic.com/wl?json={"pubid":10,"pid":"1","pdvid":"0","dvc":{},"ft":0}&pubid=10`, + }, + { + name: "private endpoint", + args: args{ + wlog: &WloggerRecord{ + record: record{ + PubID: 5, + ProfileID: "5", + VersionID: "1", + }, + }, + loggerURL: "http://10.172.141.11/wl", + gdprEnabled: 0, + }, + owlogger: `http://10.172.141.11/wl?json={"pubid":5,"pid":"5","pdvid":"1","dvc":{},"ft":0}&pubid=5`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + owlogger := PrepareLoggerURL(tt.args.wlog, tt.args.loggerURL, tt.args.gdprEnabled) + decodedOwlogger, _ := url.QueryUnescape(owlogger) + assert.Equal(t, tt.owlogger, decodedOwlogger, tt.name) + }) + } +} +func TestGetGdprEnabledFlag(t *testing.T) { + tests := []struct { + name string + partnerConfig map[int]map[string]string + gdprFlag int + }{ + { + name: "Empty partnerConfig", + partnerConfig: make(map[int]map[string]string), + gdprFlag: 0, + }, + { + name: "partnerConfig without versionlevel cfg", + partnerConfig: map[int]map[string]string{ + 2: {models.GDPR_ENABLED: "1"}, + }, + gdprFlag: 0, + }, + { + name: "partnerConfig without GDPR_ENABLED", + partnerConfig: map[int]map[string]string{ + models.VersionLevelConfigID: {"any": "1"}, + }, + gdprFlag: 0, + }, + { + name: "partnerConfig with invalid GDPR_ENABLED", + partnerConfig: map[int]map[string]string{ + models.VersionLevelConfigID: {models.GDPR_ENABLED: "non-int"}, + }, + gdprFlag: 0, + }, + { + name: "partnerConfig with GDPR_ENABLED=1", + partnerConfig: map[int]map[string]string{ + models.VersionLevelConfigID: {models.GDPR_ENABLED: "1"}, + }, + gdprFlag: 1, + }, + { + name: "partnerConfig with GDPR_ENABLED=2", + partnerConfig: map[int]map[string]string{ + models.VersionLevelConfigID: {models.GDPR_ENABLED: "2"}, + }, + gdprFlag: 2, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gdprFlag := getGdprEnabledFlag(tt.partnerConfig) + assert.Equal(t, tt.gdprFlag, gdprFlag, tt.name) + }) + } +} +func TestSendMethod(t *testing.T) { + // initialise global variables + mhttp.Init(1, 1, 1, 2000) + // init mock + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + type args struct { + rctx *models.RequestCtx + url string + headers http.Header + } + tests := []struct { + name string + args args + getMetricsEngine func() *mock_metrics.MockMetricsEngine + getMockMultiHttpContext func() *mock_mhttp.MockMultiHttpContextInterface + }{ + { + name: "send success", + args: args{ + rctx: &models.RequestCtx{ + PubIDStr: "5890", + ProfileIDStr: "1", + Endpoint: models.EndpointV25, + }, + url: "http://10.172.11.11/wl", + headers: http.Header{ + "key": []string{"val"}, + }, + }, + getMetricsEngine: func() *mock_metrics.MockMetricsEngine { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordSendLoggerDataTime(models.EndpointV25, "1", gomock.Any()) + return mockEngine + }, + getMockMultiHttpContext: func() *mock_mhttp.MockMultiHttpContextInterface { + mockHttpCtx := mock_mhttp.NewMockMultiHttpContextInterface(ctrl) + mockHttpCtx.EXPECT().AddHttpCall(gomock.Any()) + mockHttpCtx.EXPECT().Execute().Return(0, 0) + return mockHttpCtx + }, + }, + { + name: "send fail", + args: args{ + rctx: &models.RequestCtx{ + PubIDStr: "5890", + ProfileIDStr: "1", + Endpoint: models.EndpointV25, + KADUSERCookie: &http.Cookie{}, + }, + url: "http://10.172.11.11/wl", + headers: http.Header{ + "key": []string{"val"}, + }, + }, + getMetricsEngine: func() *mock_metrics.MockMetricsEngine { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPublisherWrapperLoggerFailure("5890", "1", "") + return mockEngine + }, + getMockMultiHttpContext: func() *mock_mhttp.MockMultiHttpContextInterface { + mockHttpCtx := mock_mhttp.NewMockMultiHttpContextInterface(ctrl) + mockHttpCtx.EXPECT().AddHttpCall(gomock.Any()) + mockHttpCtx.EXPECT().Execute().Return(0, 1) + return mockHttpCtx + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.args.rctx.MetricsEngine = tt.getMetricsEngine() + send(tt.args.rctx, tt.args.url, tt.args.headers, tt.getMockMultiHttpContext()) + }) + } +} diff --git a/analytics/pubmatic/logger.go b/analytics/pubmatic/logger.go index 8aadd699e50..fec890e1301 100644 --- a/analytics/pubmatic/logger.go +++ b/analytics/pubmatic/logger.go @@ -3,23 +3,43 @@ package pubmatic import ( "encoding/json" "fmt" + "strings" + "net/http" "strconv" "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v19/openrtb3" "github.com/prebid/prebid-server/analytics" "github.com/prebid/prebid-server/hooks/hookexecution" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/utils" "github.com/prebid/prebid-server/openrtb_ext" + uuid "github.com/satori/go.uuid" ) +type bidWrapper struct { + *openrtb2.Bid + Nbr *openrtb3.NonBidStatusCode +} + +// getUUID is a function variable which will return uuid +var getUUID = func() string { + return uuid.NewV4().String() +} + +// GetLogAuctionObjectAsURL will form the owlogger-url and http-headers func GetLogAuctionObjectAsURL(ao analytics.AuctionObject, rCtx *models.RequestCtx, logInfo, forRespExt bool) (string, http.Header) { + if ao.RequestWrapper == nil || ao.RequestWrapper.BidRequest == nil || rCtx == nil || rCtx.PubID == 0 { + return "", nil + } wlog := WloggerRecord{ record: record{ PubID: rCtx.PubID, ProfileID: fmt.Sprintf("%d", rCtx.ProfileID), - VersionID: fmt.Sprintf("%d", rCtx.DisplayID), + VersionID: fmt.Sprintf("%d", rCtx.DisplayVersionID), Origin: rCtx.Origin, PageURL: rCtx.PageURL, IID: rCtx.LoggerImpressionID, @@ -27,20 +47,20 @@ func GetLogAuctionObjectAsURL(ao analytics.AuctionObject, rCtx *models.RequestCt ServerLogger: 1, TestConfigApplied: rCtx.ABTestConfigApplied, Timeout: int(rCtx.TMax), + PDC: rCtx.DCName, + CachePutMiss: rCtx.CachePutMiss, }, } + wlog.logIntegrationType(rCtx.Endpoint) + wlog.logDeviceObject(rCtx, ao.RequestWrapper.BidRequest) + if ao.RequestWrapper.User != nil { extUser := openrtb_ext.ExtUser{} _ = json.Unmarshal(ao.RequestWrapper.User.Ext, &extUser) wlog.ConsentString = extUser.Consent } - if ao.RequestWrapper.Device != nil { - wlog.IP = ao.RequestWrapper.Device.IP - wlog.UserAgent = ao.RequestWrapper.Device.UA - } - if ao.RequestWrapper.Regs != nil { extReg := openrtb_ext.ExtRegs{} _ = json.Unmarshal(ao.RequestWrapper.Regs.Ext, &extReg) @@ -49,18 +69,13 @@ func GetLogAuctionObjectAsURL(ao analytics.AuctionObject, rCtx *models.RequestCt } } - //log device object - wlog.logDeviceObject(*rCtx, rCtx.UA, ao.RequestWrapper.BidRequest, rCtx.Platform) - - //log content object - if nil != ao.RequestWrapper.Site { + if ao.RequestWrapper.Site != nil { wlog.logContentObject(ao.RequestWrapper.Site.Content) - } else if nil != ao.RequestWrapper.App { + } else if ao.RequestWrapper.App != nil { wlog.logContentObject(ao.RequestWrapper.App.Content) } var ipr map[string][]PartnerRecord - if logInfo { ipr = getDefaultPartnerRecordsByImp(rCtx) } else { @@ -70,13 +85,13 @@ func GetLogAuctionObjectAsURL(ao analytics.AuctionObject, rCtx *models.RequestCt // parent bidder could in one of the above and we need them by prebid's bidderCode and not seat(could be alias) slots := make([]SlotRecord, 0) for _, imp := range ao.RequestWrapper.Imp { + impCtx, ok := rCtx.ImpBidCtx[imp.ID] + if !ok { + continue + } reward := 0 - var incomingSlots []string - if impCtx, ok := rCtx.ImpBidCtx[imp.ID]; ok { - if impCtx.IsRewardInventory != nil { - reward = int(*impCtx.IsRewardInventory) - } - incomingSlots = impCtx.IncomingSlots + if impCtx.IsRewardInventory != nil { + reward = int(*impCtx.IsRewardInventory) } // to keep existing response intact @@ -86,9 +101,10 @@ func GetLogAuctionObjectAsURL(ao analytics.AuctionObject, rCtx *models.RequestCt } slots = append(slots, SlotRecord{ - SlotName: getSlotName(imp.ID, imp.TagID), - SlotSize: incomingSlots, - Adunit: imp.TagID, + SlotId: getUUID(), + SlotName: impCtx.SlotName, + SlotSize: impCtx.IncomingSlots, + Adunit: impCtx.AdUnitName, PartnerData: partnerData, RewardedInventory: int(reward), // AdPodSlot: getAdPodSlot(imp, responseMap.AdPodBidsExt), @@ -101,16 +117,50 @@ func GetLogAuctionObjectAsURL(ao analytics.AuctionObject, rCtx *models.RequestCt models.USER_AGENT_HEADER: []string{rCtx.UA}, models.IP_HEADER: []string{rCtx.IP}, } - if rCtx.KADUSERCookie != nil { - headers.Add(models.KADUSERCOOKIE, rCtx.KADUSERCookie.Value) + + // first set the floor type from bidrequest.ext + if rCtx.NewReqExt != nil { + wlog.logFloorType(&rCtx.NewReqExt.Prebid) + } + + // set the floor details from tracker + floorDetailsSet := false + for _, tracker := range rCtx.Trackers { + wlog.FloorType = tracker.Tracker.FloorType + wlog.FloorModelVersion = tracker.Tracker.FloorModelVersion + wlog.FloorSource = tracker.Tracker.FloorSource + wlog.FloorFetchStatus = tracker.Tracker.LoggerData.FloorFetchStatus + wlog.FloorProvider = tracker.Tracker.LoggerData.FloorProvider + for i := range wlog.Slots { + wlog.Slots[i].FloorSkippedFlag = tracker.Tracker.FloorSkippedFlag + } + floorDetailsSet = true + break // For all trackers, floor-details are common so break the loop + } + + // if floor details not present in tracker then use response.ext + // this wil happen only if no valid bid is present in response.seatbid + if !floorDetailsSet { + if rCtx.ResponseExt.Prebid != nil { + // wlog.SetFloorDetails(rCtx.ResponseExt.Prebid.Floors) + floorDetails := models.GetFloorsDetails(rCtx.ResponseExt) + wlog.FloorSource = floorDetails.FloorSource + wlog.FloorModelVersion = floorDetails.FloorModelVersion + wlog.FloorFetchStatus = floorDetails.FloorFetchStatus + wlog.FloorProvider = floorDetails.FloorProvider + wlog.FloorType = floorDetails.FloorType + for i := range wlog.Slots { + wlog.Slots[i].FloorSkippedFlag = floorDetails.Skipfloors + } + } } url := ow.cfg.Endpoint - if logInfo || forRespExt { + if logInfo { url = ow.cfg.PublicEndpoint } - return PrepareLoggerURL(&wlog, url, GetGdprEnabledFlag(rCtx.PartnerConfigMap)), headers + return PrepareLoggerURL(&wlog, url, getGdprEnabledFlag(rCtx.PartnerConfigMap)), headers } // TODO filter by name. (*stageOutcomes[8].Groups[0].InvocationResults[0].AnalyticsTags.Activities[0].Results[0].Values["request-ctx"].(data)) @@ -137,43 +187,95 @@ func GetRequestCtx(hookExecutionOutcome []hookexecution.StageOutcome) *models.Re return nil } +func convertNonBidToBidWrapper(nonBid *openrtb_ext.NonBid) (bid bidWrapper) { + bid.Bid = &openrtb2.Bid{ + Price: nonBid.Ext.Prebid.Bid.Price, + ADomain: nonBid.Ext.Prebid.Bid.ADomain, + CatTax: nonBid.Ext.Prebid.Bid.CatTax, + Cat: nonBid.Ext.Prebid.Bid.Cat, + DealID: nonBid.Ext.Prebid.Bid.DealID, + W: nonBid.Ext.Prebid.Bid.W, + H: nonBid.Ext.Prebid.Bid.H, + Dur: nonBid.Ext.Prebid.Bid.Dur, + MType: nonBid.Ext.Prebid.Bid.MType, + ID: nonBid.Ext.Prebid.Bid.ID, + ImpID: nonBid.ImpId, + } + bidExt := models.BidExt{ + OriginalBidCPM: nonBid.Ext.Prebid.Bid.OriginalBidCPM, + OriginalBidCPMUSD: nonBid.Ext.Prebid.Bid.OriginalBidCPMUSD, + OriginalBidCur: nonBid.Ext.Prebid.Bid.OriginalBidCur, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealPriority: nonBid.Ext.Prebid.Bid.DealPriority, + DealTierSatisfied: nonBid.Ext.Prebid.Bid.DealTierSatisfied, + Meta: nonBid.Ext.Prebid.Bid.Meta, + Targeting: nonBid.Ext.Prebid.Bid.Targeting, + Type: nonBid.Ext.Prebid.Bid.Type, + Video: nonBid.Ext.Prebid.Bid.Video, + BidId: nonBid.Ext.Prebid.Bid.BidId, + Floors: nonBid.Ext.Prebid.Bid.Floors, + }, + }, + } + bidExtBytes, err := json.Marshal(bidExt) + if err == nil { + bid.Ext = bidExtBytes + } + // the 'nbr' field will be lost due to json.Marshal hence do not set it inside bid.Ext + // set the 'nbr' code at bid level, while forming partner-records we give high priority to bid.nbr over bid.ext.nbr + bid.Nbr = openwrap.GetNonBidStatusCodePtr(openrtb3.NonBidStatusCode(nonBid.StatusCode)) + return bid +} + +// getPartnerRecordsByImp creates partnerRecords of valid-bids + dropped-bids + non-bids+ default-bids for owlogger func getPartnerRecordsByImp(ao analytics.AuctionObject, rCtx *models.RequestCtx) map[string][]PartnerRecord { // impID-partnerRecords: partner records per impression ipr := make(map[string][]PartnerRecord) - // Seat-impID (based on impID as default bids do not have ID). Shall we generate unique ID's for them? rejectedBids := map[string]map[string]struct{}{} - loggerSeat := make(map[string][]openrtb2.Bid) - // TODO : Uncomment and modify to add seatnonbids in logger - /*for _, seatBids := range ao.RejectedBids { - if _, ok := rejectedBids[seatBids.Seat]; !ok { - rejectedBids[seatBids.Seat] = map[string]struct{}{} + loggerSeat := make(map[string][]bidWrapper) + + // currently, ao.SeatNonBid will contain the bids that got rejected in the prebid core + // it does not contain the bids that got rejected inside pubmatic ow module + // As of now, pubmatic ow module logs slot-not-map and partner-throttle related nonbids + // for which we don't create partner-record in the owlogger + for _, seatNonBid := range ao.SeatNonBid { + if _, ok := rejectedBids[seatNonBid.Seat]; !ok { + rejectedBids[seatNonBid.Seat] = map[string]struct{}{} } - - if seatBids.Bid != nil && seatBids.Bid.Bid != nil { - rejectedBids[seatBids.Seat][seatBids.Bid.Bid.ImpID] = struct{}{} - - loggerSeat[seatBids.Seat] = append(loggerSeat[seatBids.Seat], *seatBids.Bid.Bid) + for _, nonBid := range seatNonBid.NonBid { + rejectedBids[seatNonBid.Seat][nonBid.ImpId] = struct{}{} + loggerSeat[seatNonBid.Seat] = append(loggerSeat[seatNonBid.Seat], convertNonBidToBidWrapper(&nonBid)) } - }*/ - for _, seatBid := range ao.Response.SeatBid { - for _, bid := range seatBid.Bid { - // Check if this is a default and RejectedBids bid. Ex. only one bid by pubmatic it was rejected by floors. - // Module would add a 0 bid. So, we want to skip this zero bid to avoid duplicate or incomplete data and log the correct one that was rejected. - // We don't have bid.ID here so using bid.ImpID - if bid.Price == 0 && bid.W == 0 && bid.H == 0 { + } + + // SeatBid contains valid-bids + default/proxy bids + // loggerSeat should not contain duplicate entry for same imp-seat combination + for seatIndex, seatBid := range ao.Response.SeatBid { + for bidIndex, bid := range seatBid.Bid { + // Check if this is a default as well as nonbid. + // Ex. if only one bid is returned by pubmatic and it got rejected due to floors. + // then OW-Module will add one default/proxy bid in seatbid. + // and prebid core will add one nonbid in seat-non-bid. + // So, we want to skip this default/proxy bid to avoid duplicate entry in logger + if models.IsDefaultBid(&bid) { if _, ok := rejectedBids[seatBid.Seat]; ok { if _, ok := rejectedBids[seatBid.Seat][bid.ImpID]; ok { continue } } } - loggerSeat[seatBid.Seat] = append(loggerSeat[seatBid.Seat], bid) + loggerSeat[seatBid.Seat] = append(loggerSeat[seatBid.Seat], bidWrapper{Bid: &ao.Response.SeatBid[seatIndex].Bid[bidIndex]}) } } + + // include bids that got dropped from ao.SeatBid by pubmatic ow module. Ex. sendAllBids=false + // in future, this dropped bids should be part of ao.SeatNonBid object for seat, Bids := range rCtx.DroppedBids { - // include bids dropped by module. Ex. sendAllBids=false - loggerSeat[seat] = append(loggerSeat[seat], Bids...) + for bid := range Bids { + loggerSeat[seat] = append(loggerSeat[seat], bidWrapper{Bid: &rCtx.DroppedBids[seat][bid]}) + } } // pubmatic's KGP details per impression @@ -183,12 +285,13 @@ func getPartnerRecordsByImp(ao analytics.AuctionObject, rCtx *models.RequestCtx) } pmMkt := make(map[string]pubmaticMarketplaceMeta) + // loggerSeat contains valid-bids + non-bids + dropped-bids + default-bids (ex-partnerTimeout) for seat, bids := range loggerSeat { if seat == string(openrtb_ext.BidderOWPrebidCTV) { continue } - // Response would not contain non-mapped bids, do we need this + // owlogger would not contain throttled bidders, do we need this if _, ok := rCtx.AdapterThrottleMap[seat]; ok { continue } @@ -199,60 +302,68 @@ func getPartnerRecordsByImp(ao analytics.AuctionObject, rCtx *models.RequestCtx) continue } - // Response would not contain non-mapped bids, do we need this + // owlogger would not contain non-mapped bidders, do we need this if _, ok := impCtx.NonMapped[seat]; ok { break } + var kgp, kgpv, kgpsv, adFormat string + revShare := 0.0 partnerID := seat - var isRegex bool - var kgp, kgpv, kgpsv string - - if bidderMeta, ok := impCtx.Bidders[seat]; ok { - revShare, _ = strconv.ParseFloat(rCtx.PartnerConfigMap[bidderMeta.PartnerID][models.REVSHARE], 64) + bidderMeta, ok := impCtx.Bidders[seat] + if ok { partnerID = bidderMeta.PrebidBidderCode - kgp = bidderMeta.KGP // _AU_@_W_x_H_ - kgpv = bidderMeta.KGPV // ^/43743431/DMDemo[0-9]*@Div[12]@^728x90$ - kgpsv = bidderMeta.MatchedSlot // /43743431/DMDemo1@@728x90 - isRegex = bidderMeta.IsRegex + kgp = bidderMeta.KGP + revShare, _ = strconv.ParseFloat(rCtx.PartnerConfigMap[bidderMeta.PartnerID][models.REVSHARE], 64) } - // 1. nobid - if bid.Price == 0 && bid.H == 0 && bid.W == 0 { - //NOTE: kgpsv = bidderMeta.MatchedSlot above. Use the same - if !isRegex && kgpv != "" { // unmapped pubmatic's slot - kgpsv = kgpv // - KGP: _AU_@_DIV_@_W_x_H_ - } else if !isRegex { - kgpv = kgpsv - } - } else if !isRegex { - if kgpv != "" { // unmapped pubmatic's slot - kgpsv = kgpv // /43743431/DMDemo1234@300x250 --> - } else if bid.H != 0 && bid.W != 0 { // Check when bid.H and bid.W will be zero with Price !=0. Ex: MobileInApp-MultiFormat-OnlyBannerMapping_Criteo_Partner_Validaton - // 2. valid bid - // kgpv has regex, do not generate slotName again - // kgpsv could be unmapped or mapped slot, generate slotName again based on bid.H and bid.W - kgpsv = GenerateSlotName(bid.H, bid.W, kgp, impCtx.TagID, impCtx.Div, rCtx.Source) - kgpv = kgpsv // original /43743431/DMDemo1234@300x250 but new could be /43743431/DMDemo1234@222x111 - } - } + // impBidCtx contains info about valid-bids + dropped-bids + default-bids + // impBidCtx not contains info about seat-non-bids. + // impBidCtx contains bid-id in form of 'bid-id::uuid' for valid-bids + dropped-bids + // impBidCtx contains bid-id in form of 'uuid' for default-bids + var bidExt models.BidExt + json.Unmarshal(bid.Ext, &bidExt) - if kgpv == "" { - kgpv = kgpsv + bidIDForLookup := bid.ID + if bidExt.Prebid != nil && !strings.Contains(bid.ID, models.BidIdSeparator) { + // this block will not be executed for default-bids. + bidIDForLookup = utils.SetUniqueBidID(bid.ID, bidExt.Prebid.BidId) } - var bidExt models.BidExt - if bidCtx, ok := impCtx.BidCtx[bid.ID]; ok { + bidCtx, ok := impCtx.BidCtx[bidIDForLookup] + if ok { + // override bidExt for valid-bids + default-bids + dropped-bids + // since we have already prepared it under auction-response-hook bidExt = bidCtx.BidExt } + // get the tracker details from rctx, to avoid repetitive computation. + // tracker will be available only for valid-bids and will be absent for dropped-bids + default-bids + seat-non-bids + // tracker contains bid-id in form of 'bid-id::uuid' + tracker, trackerPresent := rCtx.Trackers[bidIDForLookup] + + adFormat = tracker.Tracker.PartnerInfo.Adformat + if adFormat == "" { + adFormat = models.GetAdFormat(bid.Bid, &bidExt, &impCtx) + } + + kgpv = tracker.Tracker.PartnerInfo.KGPV + kgpsv = tracker.Tracker.LoggerData.KGPSV + if kgpv == "" || kgpsv == "" { + kgpv, kgpsv = models.GetKGPSV(*bid.Bid, bidderMeta, adFormat, impCtx.TagID, impCtx.Div, rCtx.Source) + } + price := bid.Price - if ao.Response.Cur != "USD" { - price = bidExt.OriginalBidCPMUSD + if ao.Response.Cur != models.USD { + if bidCtx.EG != 0 { // valid-bids + dropped-bids+ default-bids + price = bidCtx.EG + } else if bidExt.OriginalBidCPMUSD != 0 { // valid non-bids + price = bidExt.OriginalBidCPMUSD + } } - if seat == "pubmatic" { + if seat == models.BidderPubMatic { pmMkt[bid.ImpID] = pubmaticMarketplaceMeta{ PubmaticKGP: kgp, PubmaticKGPV: kgpv, @@ -260,50 +371,74 @@ func getPartnerRecordsByImp(ao analytics.AuctionObject, rCtx *models.RequestCtx) } } + nbr := bid.Nbr // only for seat-non-bids this will present at bid level + if nbr == nil { + nbr = bidExt.Nbr // valid-bids + default-bids + dropped-bids + } + pr := PartnerRecord{ - PartnerID: partnerID, // prebid biddercode - BidderCode: seat, // pubmatic biddercode: pubmatic2 - // AdapterCode: adapterCode, // prebid adapter that brought the bid - Latency1: rCtx.BidderResponseTimeMillis[seat], - KGPV: kgpv, - KGPSV: kgpsv, - BidID: bid.ID, - OrigBidID: bid.ID, - DefaultBidStatus: 0, - ServerSide: 1, - // MatchedImpression: matchedImpression, - NetECPM: func() float64 { - if revShare != 0.0 { - return GetNetEcpm(price, revShare) - } - return price - }(), - GrossECPM: GetGrossEcpm(price), - OriginalCPM: GetGrossEcpm(bidExt.OriginalBidCPM), - OriginalCur: bidExt.OriginalBidCur, - PartnerSize: getSizeForPlatform(bid.W, bid.H, rCtx.Platform), - DealID: bid.DealID, + PartnerID: partnerID, // prebid biddercode + BidderCode: seat, // pubmatic biddercode: pubmatic2 + Latency1: rCtx.BidderResponseTimeMillis[seat], // it is set inside auctionresponsehook for all bidders + KGPV: kgpv, + KGPSV: kgpsv, + BidID: utils.GetOriginalBidId(bid.ID), + OrigBidID: utils.GetOriginalBidId(bid.ID), + DefaultBidStatus: 0, // this will be always 0 , decide whether to drop this field in future + ServerSide: 1, + MatchedImpression: rCtx.MatchedImpression[seat], + OriginalCPM: models.GetGrossEcpm(bidExt.OriginalBidCPM), + OriginalCur: bidExt.OriginalBidCur, + DealID: bid.DealID, + Nbr: nbr, + Adformat: adFormat, + NetECPM: tracker.Tracker.PartnerInfo.NetECPM, + GrossECPM: tracker.Tracker.PartnerInfo.GrossECPM, + PartnerSize: tracker.Tracker.PartnerInfo.AdSize, + ADomain: tracker.Tracker.PartnerInfo.Advertiser, } - if b, ok := rCtx.WinningBids[bid.ImpID]; ok && b.ID == bid.ID { + if pr.NetECPM == 0 { + pr.NetECPM = models.GetNetEcpm(price, revShare) + } + + if pr.GrossECPM == 0 { + pr.GrossECPM = models.GetGrossEcpm(price) + } + + if pr.PartnerSize == "" { + pr.PartnerSize = models.GetSizeForPlatform(bid.W, bid.H, rCtx.Platform) + } + + if trackerPresent { + pr.FloorRuleValue = tracker.Tracker.PartnerInfo.FloorRuleValue + pr.FloorValue = tracker.Tracker.PartnerInfo.FloorValue + } else { + pr.FloorValue, pr.FloorRuleValue = models.GetBidLevelFloorsDetails(bidExt, impCtx, rCtx.CurrencyConversion) + } + + if nbr != nil && *nbr == openrtb3.NoBidTimeoutError { + pr.PostTimeoutBidStatus = 1 + pr.Latency1 = 0 + } + + // WinningBids contains map of imp.id against bid.id+::+uuid + if b, ok := rCtx.WinningBids[bid.ImpID]; ok && b.ID == bidIDForLookup { pr.WinningBidStaus = 1 } if len(pr.OriginalCur) == 0 { pr.OriginalCPM = float64(0) - pr.OriginalCur = "USD" + pr.OriginalCur = models.USD } if len(pr.DealID) != 0 { pr.DealChannel = models.DEFAULT_DEALCHANNEL + } else { + pr.DealID = models.DealIDAbsent } if bidExt.Prebid != nil { - // don't want default banner for nobid in wl - if bidExt.Prebid.Type != "" { - pr.Adformat = string(bidExt.Prebid.Type) - } - if bidExt.Prebid.DealTierSatisfied && bidExt.Prebid.DealPriority > 0 { pr.DealPriority = bidExt.Prebid.DealPriority } @@ -316,23 +451,13 @@ func getPartnerRecordsByImp(ao analytics.AuctionObject, rCtx *models.RequestCtx) pr.setMetaDataObject(bidExt.Prebid.Meta) } - if bidExt.Prebid.Floors != nil { - pr.FloorRule = bidExt.Prebid.Floors.FloorRule - pr.FloorRuleValue = roundToTwoDigit(bidExt.Prebid.Floors.FloorRuleValue) - if bidExt.Prebid.Floors.FloorCurrency == "USD" { - pr.FloorValue = roundToTwoDigit(bidExt.Prebid.Floors.FloorValue) - } else { - // pr.FloorValue = roundToTwoDigit(bidExt.Prebid.Floors.FloorValueUSD) - } + if len(bidExt.Prebid.BidId) > 0 { + pr.BidID = bidExt.Prebid.BidId } } - if pr.Adformat == "" && bid.AdM != "" { - pr.Adformat = models.GetAdFormat(bid.AdM) - } - - if len(bid.ADomain) != 0 { - if domain, err := ExtractDomain(bid.ADomain[0]); err == nil { + if pr.ADomain == "" && len(bid.ADomain) != 0 { + if domain, err := models.ExtractDomain(bid.ADomain[0]); err == nil { pr.ADomain = domain } } @@ -358,6 +483,8 @@ func getPartnerRecordsByImp(ao analytics.AuctionObject, rCtx *models.RequestCtx) return ipr } +// getDefaultPartnerRecordsByImp creates partnerRecord with default placeholders if req.ext.wrapper.loginfo=true. +// in future, check if this can be deprecated func getDefaultPartnerRecordsByImp(rCtx *models.RequestCtx) map[string][]PartnerRecord { ipr := make(map[string][]PartnerRecord) for impID := range rCtx.ImpBidCtx { @@ -365,6 +492,7 @@ func getDefaultPartnerRecordsByImp(rCtx *models.RequestCtx) map[string][]Partner ServerSide: 1, DefaultBidStatus: 1, PartnerSize: "0x0", + DealID: "-1", }} } return ipr diff --git a/analytics/pubmatic/logger_test.go b/analytics/pubmatic/logger_test.go new file mode 100644 index 00000000000..ae9d573b06e --- /dev/null +++ b/analytics/pubmatic/logger_test.go @@ -0,0 +1,4367 @@ +package pubmatic + +import ( + "encoding/json" + "net/http" + "net/url" + "testing" + + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v19/openrtb3" + "github.com/prebid/prebid-server/analytics" + "github.com/prebid/prebid-server/hooks/hookanalytics" + "github.com/prebid/prebid-server/hooks/hookexecution" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/util/ptrutil" + "github.com/stretchr/testify/assert" +) + +func TestGetRequestCtx(t *testing.T) { + tests := []struct { + name string + hookExecutionOutcome []hookexecution.StageOutcome + rctx *models.RequestCtx + }{ + { + name: "rctx present", + hookExecutionOutcome: []hookexecution.StageOutcome{ + { + Groups: []hookexecution.GroupOutcome{ + { + InvocationResults: []hookexecution.HookOutcome{ + { + AnalyticsTags: hookanalytics.Analytics{ + Activities: []hookanalytics.Activity{ + { + Results: []hookanalytics.Result{ + { + Values: map[string]interface{}{ + "request-ctx": &models.RequestCtx{}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + rctx: &models.RequestCtx{}, + }, + { + name: "rctx of invalid type", + hookExecutionOutcome: []hookexecution.StageOutcome{ + { + Groups: []hookexecution.GroupOutcome{ + { + InvocationResults: []hookexecution.HookOutcome{ + { + AnalyticsTags: hookanalytics.Analytics{ + Activities: []hookanalytics.Activity{ + { + Results: []hookanalytics.Result{ + { + Values: map[string]interface{}{ + "request-ctx": []string{}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + rctx: nil, + }, + { + name: "rctx absent", + hookExecutionOutcome: []hookexecution.StageOutcome{ + { + Groups: []hookexecution.GroupOutcome{ + { + InvocationResults: []hookexecution.HookOutcome{}, + }, + }, + }, + }, + rctx: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rctx := GetRequestCtx(tt.hookExecutionOutcome) + assert.Equal(t, tt.rctx, rctx, tt.name) + }) + } +} +func TestConvertNonBidToBid(t *testing.T) { + tests := []struct { + name string + nonBid openrtb_ext.NonBid + bid bidWrapper + }{ + { + name: "nonbid to bidwrapper", + nonBid: openrtb_ext.NonBid{ + StatusCode: int(openrtb3.LossBidBelowAuctionFloor), + ImpId: "imp1", + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Price: 10, + ADomain: []string{"abc.com"}, + DealID: "d1", + OriginalBidCPM: 10, + OriginalBidCur: models.USD, + OriginalBidCPMUSD: 0, + W: 10, + H: 50, + DealPriority: 1, + Video: &openrtb_ext.ExtBidPrebidVideo{ + Duration: 10, + }, + }, + }, + }, + }, + bid: bidWrapper{ + &openrtb2.Bid{ + ImpID: "imp1", + Price: 10, + ADomain: []string{"abc.com"}, + DealID: "d1", + W: 10, + H: 50, + Ext: json.RawMessage(`{"prebid":{"dealpriority":1,"video":{"duration":10,"primary_category":"","vasttagid":""}},"origbidcpm":10,"origbidcur":"USD"}`), + }, + openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidBelowAuctionFloor), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bid := convertNonBidToBidWrapper(&tt.nonBid) + assert.Equal(t, tt.bid, bid, tt.name) + }) + } +} +func TestGetDefaultPartnerRecordsByImp(t *testing.T) { + tests := []struct { + name string + rCtx *models.RequestCtx + partners map[string][]PartnerRecord + }{ + { + name: "empty ImpBidCtx", + rCtx: &models.RequestCtx{}, + partners: map[string][]PartnerRecord{}, + }, + { + name: "multiple imps", + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + "imp2": {}, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + PartnerRecord{ + ServerSide: 1, + DefaultBidStatus: 1, + PartnerSize: "0x0", + DealID: "-1", + }, + }, + "imp2": { + PartnerRecord{ + ServerSide: 1, + DefaultBidStatus: 1, + PartnerSize: "0x0", + DealID: "-1", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getDefaultPartnerRecordsByImp(tt.rCtx) + assert.Equal(t, len(tt.partners), len(partners), tt.name) + for ind := range partners { + // ignore order of elements in slice while comparison + assert.ElementsMatch(t, partners[ind], tt.partners[ind], tt.name) + } + }) + } +} +func TestGetPartnerRecordsByImp(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "adformat for default bid", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 0, + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + }, + }, + }, + Video: &openrtb2.Video{ + MinDuration: 1, + MaxDuration: 10, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + Adformat: models.Video, + }, + }, + }, + }, + { + name: "adformat for valid bid", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + AdM: "{\"native\":{\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"Lexus - Luxury vehicles company\"}},{\"id\":2,\"img\":{\"h\":150,\"url\":\"https://stagingnyc.pubmatic.com:8443//sdk/lexus_logo.png\",\"w\":150},\"required\":0},{\"id\":3,\"img\":{\"h\":428,\"url\":\"https://stagingnyc.pubmatic.com:8443//sdk/28f48244cafa0363b03899f267453fe7%20copy.png\",\"w\":214},\"required\":0},{\"data\":{\"value\":\"Goto PubMatic\"},\"id\":4,\"required\":0},{\"data\":{\"value\":\"Lexus - Luxury vehicles company\"},\"id\":5,\"required\":0},{\"data\":{\"value\":\"4\"},\"id\":6,\"required\":0}],\"imptrackers\":[\"http://phtrack.pubmatic.com/?ts=1496043362&r=84137f17-eefd-4f06-8380-09138dc616e6&i=c35b1240-a0b3-4708-afca-54be95283c61&a=130917&t=9756&au=10002949&p=&c=10014299&o=10002476&wl=10009731&ty=1\"],\"link\":{\"clicktrackers\":[\"http://ct.pubmatic.com/track?ts=1496043362&r=84137f17-eefd-4f06-8380-09138dc616e6&i=c35b1240-a0b3-4708-afca-54be95283c61&a=130917&t=9756&au=10002949&p=&c=10014299&o=10002476&wl=10009731&ty=3&url=\"],\"url\":\"http://www.lexus.com/\"},\"ver\":1}}", + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + }, + }, + }, + Native: &openrtb2.Native{}, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + Adformat: models.Native, + NetECPM: 10, + GrossECPM: 10, + }, + }, + }, + }, + { + name: "latency for partner", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + AdM: "{\"native\":{\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"Lexus - Luxury vehicles company\"}},{\"id\":2,\"img\":{\"h\":150,\"url\":\"https://stagingnyc.pubmatic.com:8443//sdk/lexus_logo.png\",\"w\":150},\"required\":0},{\"id\":3,\"img\":{\"h\":428,\"url\":\"https://stagingnyc.pubmatic.com:8443//sdk/28f48244cafa0363b03899f267453fe7%20copy.png\",\"w\":214},\"required\":0},{\"data\":{\"value\":\"Goto PubMatic\"},\"id\":4,\"required\":0},{\"data\":{\"value\":\"Lexus - Luxury vehicles company\"},\"id\":5,\"required\":0},{\"data\":{\"value\":\"4\"},\"id\":6,\"required\":0}],\"imptrackers\":[\"http://phtrack.pubmatic.com/?ts=1496043362&r=84137f17-eefd-4f06-8380-09138dc616e6&i=c35b1240-a0b3-4708-afca-54be95283c61&a=130917&t=9756&au=10002949&p=&c=10014299&o=10002476&wl=10009731&ty=1\"],\"link\":{\"clicktrackers\":[\"http://ct.pubmatic.com/track?ts=1496043362&r=84137f17-eefd-4f06-8380-09138dc616e6&i=c35b1240-a0b3-4708-afca-54be95283c61&a=130917&t=9756&au=10002949&p=&c=10014299&o=10002476&wl=10009731&ty=3&url=\"],\"url\":\"http://www.lexus.com/\"},\"ver\":1}}", + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + }, + }, + }, + Native: &openrtb2.Native{}, + }, + }, + BidderResponseTimeMillis: map[string]int{ + "pubmatic": 20, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + Adformat: models.Native, + NetECPM: 10, + GrossECPM: 10, + Latency1: 20, + }, + }, + }, + }, + { + name: "matchedimpression for partner", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + }, + }, + }, + }, + }, + MatchedImpression: map[string]int{ + "pubmatic": 1, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + NetECPM: 10, + GrossECPM: 10, + MatchedImpression: 1, + }, + }, + }, + }, + { + name: "partnersize for non-video bid", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + W: 30, + H: 50, + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + }, + }, + }, + }, + }, + Platform: models.PLATFORM_DISPLAY, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "30x50", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + NetECPM: 10, + GrossECPM: 10, + }, + }, + }, + }, + { + name: "partnersize for video bid", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + W: 30, + H: 50, + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + }, + }, + }, + }, + }, + Platform: models.PLATFORM_VIDEO, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "30x50v", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + NetECPM: 10, + GrossECPM: 10, + }, + }, + }, + }, + { + name: "dealid present, verify dealid and dealchannel", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + DealID: "pubdeal", + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "pubdeal", + DealChannel: "PMP", + ServerSide: 1, + OriginalCur: "USD", + NetECPM: 10, + GrossECPM: 10, + }, + }, + }, + }, + { + name: "log adomain field", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + ADomain: []string{ + "http://google.com", "http://yahoo.com", + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + BidId: "prebid-bid-id-1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "prebid-bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + ADomain: "google.com", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equalf(t, partners, tt.partners, tt.name) + }) + } +} +func TestGetPartnerRecordsByImpForTracker(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "prefer tracker details, avoid computation", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + Trackers: map[string]models.OWTracker{ + "bid-id-1": { + Tracker: models.Tracker{ + PartnerInfo: models.Partner{ + Adformat: models.Native, + KGPV: "kgpv", + NetECPM: 10, + GrossECPM: 12, + AdSize: "15x15", + FloorValue: 1, + FloorRuleValue: 2, + Advertiser: "sony.com", + }, + LoggerData: models.LoggerData{ + KGPSV: "kgpsv", + }, + }, + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "15x15", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + Adformat: models.Native, + NetECPM: 10, + GrossECPM: 12, + FloorValue: 1, + FloorRuleValue: 2, + ADomain: "sony.com", + KGPV: "kgpv", + KGPSV: "kgpsv", + }, + }, + }, + }, + { + name: "tracker absent, compute data", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + W: 15, + H: 15, + Price: 12, + ADomain: []string{"http://sony.com"}, + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + Trackers: map[string]models.OWTracker{}, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Floors: &openrtb_ext.ExtBidPrebidFloors{ + FloorValue: 1, + FloorRuleValue: 2, + }, + Type: models.Native, + }, + }, + }, + }, + }, + Bidders: map[string]models.PartnerData{ + "pubmatic": { + PrebidBidderCode: "pubmatic", + PartnerID: 1, + KGP: "kgp", + KGPV: "kgpv", + }, + }, + Native: &openrtb2.Native{}, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "15x15", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + Adformat: models.Native, + NetECPM: 12, + GrossECPM: 12, + FloorValue: 1, + FloorRuleValue: 2, + ADomain: "sony.com", + KGPV: "kgpv", + KGPSV: "kgpv", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equalf(t, partners, tt.partners, tt.name) + }) + } +} +func TestGetPartnerRecordsByImpForDroppedBids(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "all bids got dropped", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + DroppedBids: map[string][]openrtb2.Bid{ + "pubmatic": { + { + ID: "bid-id-1", + ImpID: "imp1", + }, + }, + "appnexus": { + { + ID: "bid-id-2", + ImpID: "imp1", + }, + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + Bidders: map[string]models.PartnerData{ + "pubmatic": { + PartnerID: 1, + PrebidBidderCode: "pubmatic", + }, + "appnexus": { + PartnerID: 2, + PrebidBidderCode: "appnexus", + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + }, + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-2", + OrigBidID: "bid-id-2", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + }, + }, + }, + }, + { + name: "1 bid got dropped, 1 bid is present in seatbid", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + DroppedBids: map[string][]openrtb2.Bid{ + "appnexus": { + { + ID: "bid-id-2", + ImpID: "imp1", + }, + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + Bidders: map[string]models.PartnerData{ + "pubmatic": { + PartnerID: 1, + PrebidBidderCode: "pubmatic", + }, + "appnexus": { + PartnerID: 2, + PrebidBidderCode: "appnexus", + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + }, + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-2", + OrigBidID: "bid-id-2", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equal(t, len(tt.partners), len(partners), tt.name) + for ind := range partners { + // ignore order of elements in slice while comparison + assert.ElementsMatch(t, partners[ind], tt.partners[ind], tt.name) + } + }) + } +} +func TestGetPartnerRecordsByImpForDefaultBids(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "no default bid", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + NetECPM: 10, + GrossECPM: 10, + }, + }, + }, + }, + { + name: "partner timeout case, default bid present is seat-bid but absent in seat-non-bid", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 0, + }, + }, + Seat: "pubmatic", + }, + }, + }, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "appnexus", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int(openrtb3.LossBidBelowAuctionFloor), + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + ID: "bid-id-2", + }, + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.NoBidTimeoutError), + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-2", + OrigBidID: "bid-id-2", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidBelowAuctionFloor), + }, + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + NetECPM: 0, + GrossECPM: 0, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.NoBidTimeoutError), + PostTimeoutBidStatus: 1, + }, + }, + }, + }, + { + name: "floor rejected bid, default bid present in seat-bid and same bid is available in seat-non-bid", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 0, + }, + }, + Seat: "pubmatic", + }, + }, + }, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "pubmatic", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int(openrtb3.LossBidBelowAuctionFloor), + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + ID: "bid-id-1", + }, + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.NoBidGeneralError), + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: "USD", + NetECPM: 0, + GrossECPM: 0, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidBelowAuctionFloor), + }, + }, + }, + }, + { + name: "slot not mapped, default bid present is seat-bid but absent in seat-non-bid", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 0, + }, + }, + Seat: "pubmatic", + }, + }, + }, + SeatNonBid: []openrtb_ext.SeatNonBid{}, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.NoBidGeneralError), + }, + }, + }, + NonMapped: map[string]struct{}{ + "pubmatic": {}, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{}, + }, + { + name: "partner throttled, default bid present is seat-bid but absent in seat-non-bid", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 0, + }, + }, + Seat: "pubmatic", + }, + }, + }, + SeatNonBid: []openrtb_ext.SeatNonBid{}, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.NoBidGeneralError), + }, + }, + }, + }, + }, + AdapterThrottleMap: map[string]struct{}{ + "pubmatic": {}, + }, + }, + }, + partners: map[string][]PartnerRecord{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equal(t, len(tt.partners), len(partners), tt.name) + for ind := range partners { + // ignore order of elements in slice while comparison + if !assert.ElementsMatch(t, partners[ind], tt.partners[ind], tt.name) { + assert.Equal(t, partners[ind], tt.partners[ind], tt.name) + } + } + }) + } +} +func TestGetPartnerRecordsByImpForSeatNonBid(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "empty seatnonbids, expect empty partnerRecord", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + SeatNonBid: []openrtb_ext.SeatNonBid{}, + }, + rCtx: &models.RequestCtx{}, + }, + partners: map[string][]PartnerRecord{}, + }, + { + name: "ImpBidCtx is must to log partner-record in logger", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "pubmatic", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int(openrtb3.LossBidBelowDealFloor), + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Price: 10, + }, + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: make(map[string]models.ImpCtx), + }, + }, + partners: map[string][]PartnerRecord{}, + }, + { + name: "log rejected non-bid", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "appnexus", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int(openrtb3.LossBidBelowAuctionFloor), + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Price: 10, + ID: "bid-id-1", + W: 10, + H: 50, + OriginalBidCPM: 10, + }, + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + Bidders: map[string]models.PartnerData{ + "appnexus": { + PartnerID: 1, + PrebidBidderCode: "appnexus", + KGP: "kgp", + KGPV: "kgpv", + }, + }, + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidBelowAuctionFloor), + }, + }, + }, + BidFloor: 10.5, + BidFloorCur: "USD", + }, + }, + PartnerConfigMap: map[int]map[string]string{ + 1: { + "rev_share": "0", + }, + }, + WinningBids: make(map[string]models.OwBid), + Platform: models.PLATFORM_APP, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + KGPV: "kgpv", + KGPSV: "kgpv", + PartnerSize: "10x50", + GrossECPM: 10, + NetECPM: 10, + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCPM: 0, + OriginalCur: models.USD, + FloorValue: 10.5, + FloorRuleValue: 10.5, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidBelowAuctionFloor), + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equal(t, tt.partners, partners, tt.name) + }) + } +} +func TestGetPartnerRecordsByImpForSeatNonBidForFloors(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "bid.ext.prebid.floors has high priority than imp.bidfloor", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "appnexus", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int(openrtb3.LossBidBelowAuctionFloor), + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Price: 10, + ID: "bid-id-1", + Floors: &openrtb_ext.ExtBidPrebidFloors{ + FloorRule: "*|*|ebay.com", + FloorRuleValue: 1, + FloorValue: 1, + FloorCurrency: models.USD, + }, + }, + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidFloor: 10.5, + BidFloorCur: "USD", + }, + }, + PartnerConfigMap: map[int]map[string]string{ + 1: {}, + }, + WinningBids: make(map[string]models.OwBid), + Platform: models.PLATFORM_APP, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + GrossECPM: 10, + NetECPM: 10, + ServerSide: 1, + OriginalCPM: 0, + OriginalCur: models.USD, + FloorValue: 1, + FloorRuleValue: 1, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidBelowAuctionFloor), + }, + }, + }, + }, + { + name: "bid.ext.prebid.floors can have 0 value", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "appnexus", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int((openrtb3.LossBidBelowAuctionFloor)), + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Price: 10, + ID: "bid-id-1", + Floors: &openrtb_ext.ExtBidPrebidFloors{ + FloorRule: "*|*|ebay.com", + FloorRuleValue: 0, + FloorValue: 0, + FloorCurrency: models.USD, + }, + }, + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidFloor: 10.5, + BidFloorCur: "USD", + }, + }, + PartnerConfigMap: map[int]map[string]string{ + 1: {}, + }, + WinningBids: make(map[string]models.OwBid), + Platform: models.PLATFORM_APP, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + GrossECPM: 10, + NetECPM: 10, + ServerSide: 1, + OriginalCPM: 0, + OriginalCur: models.USD, + FloorValue: 0, + FloorRuleValue: 0, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidBelowAuctionFloor), + }, + }, + }, + }, + { + name: "bid.ext.prebid.floors.floorRuleValue is 0 then set it to bid.ext.prebid.floors.floorRule", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "appnexus", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int((openrtb3.LossBidBelowAuctionFloor)), + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Price: 10, + ID: "bid-id-1", + Floors: &openrtb_ext.ExtBidPrebidFloors{ + FloorRule: "*|*|ebay.com", + FloorRuleValue: 0, + FloorValue: 10, + FloorCurrency: models.USD, + }, + }, + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidFloor: 10.5, + BidFloorCur: "USD", + }, + }, + PartnerConfigMap: map[int]map[string]string{ + 1: {}, + }, + WinningBids: make(map[string]models.OwBid), + Platform: models.PLATFORM_APP, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + GrossECPM: 10, + NetECPM: 10, + ServerSide: 1, + OriginalCPM: 0, + OriginalCur: models.USD, + FloorValue: 10, + FloorRuleValue: 10, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidBelowAuctionFloor), + }, + }, + }, + }, + { + name: "bid.ext.prebid.floors not set, fallback to imp.bidfloor", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "appnexus", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int(openrtb3.LossBidBelowAuctionFloor), + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Price: 10, + ID: "bid-id-1", + }, + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidFloor: 10.567, + BidFloorCur: "USD", + }, + }, + PartnerConfigMap: map[int]map[string]string{ + 1: {}, + }, + WinningBids: make(map[string]models.OwBid), + Platform: models.PLATFORM_APP, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + GrossECPM: 10, + NetECPM: 10, + ServerSide: 1, + OriginalCPM: 0, + OriginalCur: models.USD, + FloorValue: 10.57, + FloorRuleValue: 10.57, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidBelowAuctionFloor), + }, + }, + }, + }, + { + name: "currency conversion when floor value is set to imp.bidfloor", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "appnexus", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int(openrtb3.LossBidBelowAuctionFloor), + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Price: 10, + ID: "bid-id-1", + }, + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + CurrencyConversion: func(from, to string, value float64) (float64, error) { + return 1000, nil + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidFloor: 10.567, + BidFloorCur: "JPY", + }, + }, + PartnerConfigMap: map[int]map[string]string{ + 1: {}, + }, + WinningBids: make(map[string]models.OwBid), + Platform: models.PLATFORM_APP, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + GrossECPM: 10, + NetECPM: 10, + ServerSide: 1, + OriginalCPM: 0, + OriginalCur: models.USD, + FloorValue: 1000, + FloorRuleValue: 1000, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidBelowAuctionFloor), + }, + }, + }, + }, + { + name: "currency conversion when floor value is set from bid.ext.prebid.floors", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "appnexus", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int(openrtb3.LossBidBelowAuctionFloor), + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Price: 10, + ID: "bid-id-1", + Floors: &openrtb_ext.ExtBidPrebidFloors{ + FloorRule: "*|*|ebay.com", + FloorRuleValue: 1, + FloorValue: 1, + FloorCurrency: "JPY", + }, + }, + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + CurrencyConversion: func(from, to string, value float64) (float64, error) { + return 0.12, nil + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidFloor: 10.567, + BidFloorCur: "JPY", + }, + }, + PartnerConfigMap: map[int]map[string]string{ + 1: {}, + }, + WinningBids: make(map[string]models.OwBid), + Platform: models.PLATFORM_APP, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + GrossECPM: 10, + NetECPM: 10, + ServerSide: 1, + OriginalCPM: 0, + OriginalCur: models.USD, + FloorValue: 0.12, + FloorRuleValue: 0.12, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidBelowAuctionFloor), + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equal(t, tt.partners, partners, tt.name) + }) + } +} +func TestGetPartnerRecordsByImpForReserveredBidders(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "ignore prebid_ctv bidder", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "prebid_ctv", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{}, + }, + }, + partners: map[string][]PartnerRecord{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equal(t, tt.partners, partners, tt.name) + }) + } +} +func TestGetPartnerRecordsByImpForPostTimeoutBidStatus(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "update 't' when Partner Timed out", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.NoBidTimeoutError), + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + PostTimeoutBidStatus: 1, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.NoBidTimeoutError), + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equal(t, tt.partners, partners, tt.name) + }) + } +} +func TestGetPartnerRecordsByImpForBidIDCollisions(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "valid bid, impBidCtx bidID is in bidID::uuid format", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{"prebid":{"bidid":"uuid"}}`), + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1::uuid": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealPriority: 1, + DealTierSatisfied: true, + BidId: "uuid", + }, + }, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "uuid", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + NetECPM: 10, + GrossECPM: 10, + DealPriority: 1, + }, + }, + }, + }, + { + name: "valid bid, but json unmarshal fails", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{"prebid":{`), + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1::uuid": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealPriority: 1, + DealTierSatisfied: true, + BidId: "uuid", + }, + }, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + NetECPM: 10, + GrossECPM: 10, + DealPriority: 0, + }, + }, + }, + }, + { + name: "dropped bid, impBidCtx bidID is in bidID::uuid format", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{"prebid":{"bidid":"uuid"}}`), + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1::uuid": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealPriority: 1, + DealTierSatisfied: true, + }, + }, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + NetECPM: 10, + GrossECPM: 10, + DealPriority: 1, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + }, + }, + }, + }, + { + name: "default bid, impBidCtx bidID is in uuid format", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "uuid", + ImpID: "imp1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "uuid": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.NoBidTimeoutError), + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "uuid", + OrigBidID: "uuid", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.NoBidTimeoutError), + PostTimeoutBidStatus: 1, + }, + }, + }, + }, + { + name: "non bid, no bidCtx in impBidCtx", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "appnexus", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int(openrtb3.LossBidLostToDealBid), + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Price: 10, + ID: "bid-id-1", + BidId: "uuid", + }, + }, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "uuid", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + NetECPM: 10, + GrossECPM: 10, + OriginalCur: models.USD, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + }, + }, + { + name: "winning bid contains bidID in bidID::uuid format", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{"prebid":{"bidid":"uuid"}}`), + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1::uuid": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealPriority: 1, + DealTierSatisfied: true, + BidId: "uuid", + }, + }, + }, + }, + }, + }, + }, + WinningBids: map[string]models.OwBid{ + "imp1": { + ID: "bid-id-1::uuid", + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "uuid", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + NetECPM: 10, + GrossECPM: 10, + DealPriority: 1, + WinningBidStaus: 1, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equal(t, tt.partners, partners, tt.name) + }) + } +} +func TestGetPartnerRecordsByImpForBidExtFailure(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "valid bid, but bid.ext is empty", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{}`), + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1::uuid": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealPriority: 1, + DealTierSatisfied: true, + BidId: "uuid", + }, + }, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + NetECPM: 10, + GrossECPM: 10, + DealPriority: 0, + }, + }, + }, + }, + { + name: "dropped bid, bidExt unmarshal fails", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{"prebid":{`), + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1::uuid": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealPriority: 1, + DealTierSatisfied: true, + }, + }, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + NetECPM: 10, + GrossECPM: 10, + DealPriority: 0, + Nbr: nil, + }, + }, + }, + }, + { + name: "default bid, bidExt unmarshal fails", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "uuid", + ImpID: "imp1", + Ext: json.RawMessage(`{{{`), + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "uuid": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.NoBidTimeoutError), + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "uuid", + OrigBidID: "uuid", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.NoBidTimeoutError), + PostTimeoutBidStatus: 1, + }, + }, + }, + }, + { + name: "non bid, bidExt empty", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{}, + SeatNonBid: []openrtb_ext.SeatNonBid{ + { + Seat: "appnexus", + NonBid: []openrtb_ext.NonBid{ + { + ImpId: "imp1", + StatusCode: int(openrtb3.LossBidLostToDealBid), + Ext: openrtb_ext.NonBidExt{}, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "", + OrigBidID: "", + DealID: "-1", + ServerSide: 1, + NetECPM: 0, + GrossECPM: 0, + OriginalCur: models.USD, + Nbr: openwrap.GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equal(t, tt.partners, partners, tt.name) + }) + } +} +func TestGetPartnerRecordsByImpForBidExtPrebidObject(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "log metadata object", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Meta: &openrtb_ext.ExtBidPrebidMeta{ + NetworkID: 100, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + MetaData: &MetaData{ + NetworkID: 100, + }, + }, + }, + }, + }, + { + name: "dealPriority is 1 but DealTierSatisfied is false", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + DealPriority: 1, + }, + }, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + }, + }, + }, + }, + { + name: "dealPriority is 1 and DealTierSatisfied is true", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + DealPriority: 1, + }, + }, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + DealPriority: 1, + }, + }, + }, + }, + { + name: "dealPriority is 0 and DealTierSatisfied is true", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + DealPriority: 0, + }, + }, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + DealPriority: 0, + }, + }, + }, + }, + { + name: "bidExt.Prebid.Video.Duration is 0 ", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Video: &openrtb_ext.ExtBidPrebidVideo{ + Duration: 0, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + }, + }, + }, + }, + { + name: "bidExt.Prebid.Video.Duration is valid, log AdDuration", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Video: &openrtb_ext.ExtBidPrebidVideo{ + Duration: 10, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + AdDuration: ptrutil.ToPtr(10), + }, + }, + }, + }, + { + name: "override bidid by bidExt.Prebid.bidID", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + BidId: "prebid-bid-id-1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "prebid-bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equal(t, tt.partners, partners, tt.name) + }) + } +} +func TestGetPartnerRecordsByImpForRevShareAndBidCPM(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "origbidcpmusd not present", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 1.55, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + OriginalBidCPM: 1.55, + OriginalBidCur: "USD", + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + NetECPM: 1.55, + GrossECPM: 1.55, + OriginalCPM: 1.55, + OriginalCur: "USD", + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + }, + }, + }, + }, + { + name: "origbidcpmusd not present and revshare present", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 100, + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + PartnerConfigMap: map[int]map[string]string{ + 1: { + models.REVSHARE: "10", + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + OriginalBidCPM: 100, + OriginalBidCur: "USD", + }, + }, + }, + Bidders: map[string]models.PartnerData{ + "pubmatic": { + PartnerID: 1, + PrebidBidderCode: "pubmatic", + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + NetECPM: 90, + GrossECPM: 100, + OriginalCPM: 100, + OriginalCur: "USD", + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + }, + }, + }, + }, + { + name: "origbidcpmusd is present", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 1.55, + }, + }, + }, + }, + Cur: "INR", + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + OriginalBidCPM: 125.76829, + OriginalBidCur: "INR", + OriginalBidCPMUSD: 1.76829, + }, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + NetECPM: 1.77, + GrossECPM: 1.77, + OriginalCPM: 125.77, + OriginalCur: "INR", + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + }, + }, + }, + }, + { + name: "origbidcpmusd not present for non-USD bids", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 125.16829, + }, + }, + }, + }, + Cur: "INR", + }, + }, + rCtx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + OriginalBidCPM: 125.16829, + OriginalBidCur: "INR", + }, + EG: 125.16829, + EN: 125.16829, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + GrossECPM: 125.17, + NetECPM: 125.17, + OriginalCPM: 125.17, + OriginalCur: "INR", + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + }, + }, + }, + }, + { + name: "origbidcpmusd is present, revshare is present", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 100, + }, + }, + }, + }, + Cur: "INR", + }, + }, + rCtx: &models.RequestCtx{ + PartnerConfigMap: map[int]map[string]string{ + 1: { + models.REVSHARE: "10", + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + OriginalBidCPM: 200, + OriginalBidCur: "INR", + OriginalBidCPMUSD: 100, + }, + }, + }, + Bidders: map[string]models.PartnerData{ + "pubmatic": { + PrebidBidderCode: "pubmatic", + PartnerID: 1, + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + NetECPM: 90, + GrossECPM: 100, + OriginalCPM: 200, + OriginalCur: "INR", + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equal(t, tt.partners, partners, tt.name) + }) + } +} +func TestGetPartnerRecordsByImpForMarketPlaceBidders(t *testing.T) { + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + } + tests := []struct { + name string + args args + partners map[string][]PartnerRecord + }{ + { + name: "overwrite marketplace bid details", + args: args{ + ao: analytics.AuctionObject{ + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb2.Bid{ + {ID: "bid-id-1", ImpID: "imp1", Price: 1}, + }, + }, + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + {ID: "bid-id-2", ImpID: "imp1", Price: 2}, + }, + }, + { + Seat: "groupm", + Bid: []openrtb2.Bid{ + {ID: "bid-id-3", ImpID: "imp1", Price: 3}, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + MarketPlaceBidders: map[string]struct{}{ + "groupm": {}, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + Bidders: map[string]models.PartnerData{ + "appnexus": { + KGP: "apnx_kgp", + KGPV: "apnx_kgpv", + PrebidBidderCode: "appnexus", + }, + "pubmatic": { + KGP: "pubm_kgp", + KGPV: "pubm_kgpv", + PrebidBidderCode: "pubmatic", + }, + "groupm": { + KGP: "gm_kgp", + KGPV: "gm_kgpv", + PrebidBidderCode: "groupm", + }, + }, + }, + }, + }, + }, + partners: map[string][]PartnerRecord{ + "imp1": { + { + PartnerID: "appnexus", + BidderCode: "appnexus", + PartnerSize: "0x0", + BidID: "bid-id-1", + OrigBidID: "bid-id-1", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + GrossECPM: 1, + NetECPM: 1, + KGPV: "apnx_kgpv", + KGPSV: "apnx_kgpv", + }, + { + PartnerID: "pubmatic", + BidderCode: "pubmatic", + PartnerSize: "0x0", + BidID: "bid-id-2", + OrigBidID: "bid-id-2", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + GrossECPM: 2, + NetECPM: 2, + KGPV: "pubm_kgpv", + KGPSV: "pubm_kgpv", + }, + { + PartnerID: "pubmatic", + BidderCode: "groupm", + PartnerSize: "0x0", + BidID: "bid-id-3", + OrigBidID: "bid-id-3", + DealID: "-1", + ServerSide: 1, + OriginalCur: models.USD, + GrossECPM: 3, + NetECPM: 3, + KGPV: "pubm_kgpv", + KGPSV: "pubm_kgpv", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + partners := getPartnerRecordsByImp(tt.args.ao, tt.args.rCtx) + assert.Equal(t, len(tt.partners), len(partners), tt.name) + for ind := range partners { + // ignore order of elements in slice while comparison + assert.ElementsMatch(t, partners[ind], tt.partners[ind], tt.name) + } + }) + } +} +func TestGetLogAuctionObjectAsURL(t *testing.T) { + + cfg := ow.cfg + defer func() { + ow.cfg = cfg + }() + + ow.cfg.Endpoint = "http://10.172.141.11/wl" + ow.cfg.PublicEndpoint = "http://t.pubmatic.com/wl" + + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + logInfo, forRespExt bool + } + type want struct { + logger string + header http.Header + } + tests := []struct { + name string + args args + want want + }{ + { + name: "do not prepare owlogger if pubid is missing", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + Endpoint: models.EndpointV25, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: "", + header: nil, + }, + }, + { + name: "do not prepare owlogger if bidrequest is nil", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: nil, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + Endpoint: models.EndpointV25, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: "", + header: nil, + }, + }, + { + name: "do not prepare owlogger if bidrequestwrapper is nil", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: nil, + }, + rCtx: &models.RequestCtx{ + Endpoint: models.EndpointV25, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: "", + header: nil, + }, + }, + { + name: "log integration type", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + Endpoint: models.EndpointV25, + PubID: 5890, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":0,"it":"sdk"}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "log consent string", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + User: &openrtb2.User{ + Ext: json.RawMessage(`{"consent": "any-random-consent-string"}`), + }, + }, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","cns":"any-random-consent-string","sl":1,"dvc":{},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "log gdpr flag", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"gdpr":1}`), + }, + }, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","gdpr":1,"sl":1,"dvc":{},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "log device platform", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + DevicePlatform: models.DevicePlatformMobileAppAndroid, + PubID: 5890, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{"plt":5},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "log device IFA Type", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"ifa_type":"sspid"}`), + }, + }, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + DevicePlatform: models.DevicePlatformMobileAppAndroid, + PubID: 5890, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{"plt":5,"ifty":8},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "log content from site object", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Site: &openrtb2.Site{ + Content: &openrtb2.Content{ + ID: "1", + Title: "Game of thrones", + Cat: []string{"IAB-1"}, + }, + }, + }, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ct":{"id":"1","ttl":"Game of thrones","cat":["IAB-1"]},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "log content from app object", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + App: &openrtb2.App{ + Content: &openrtb2.Content{ + ID: "1", + Title: "Game of thrones", + }, + }, + }, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ct":{"id":"1","ttl":"Game of thrones"},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "log UA and IP in header", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + UA: "mozilla", + IP: "10.10.10.10", + KADUSERCookie: &http.Cookie{Name: "uids", Value: "eidsabcd"}, + PubID: 5890, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{"mozilla"}, + models.IP_HEADER: []string{"10.10.10.10"}, + }, + }, + }, + { + name: "loginfo is false", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + }, + logInfo: false, + forRespExt: true, + }, + want: want{ + logger: ow.cfg.Endpoint + `?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "responseExt.Prebid is nil so floor details not set", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{ + Ext: json.RawMessage("{}"), + }, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: ow.cfg.PublicEndpoint + `?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "set floor details", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + Trackers: map[string]models.OWTracker{ + "any-bid-id": { + Tracker: models.Tracker{ + LoggerData: models.LoggerData{ + FloorProvider: "provider-1", + }, + }, + }, + }, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: ow.cfg.PublicEndpoint + `?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":0,"fp":"provider-1"}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + logger, header := GetLogAuctionObjectAsURL(tt.args.ao, tt.args.rCtx, tt.args.logInfo, tt.args.forRespExt) + logger, _ = url.QueryUnescape(logger) + assert.Equal(t, tt.want.logger, logger, tt.name) + assert.Equal(t, tt.want.header, header, tt.name) + }) + } +} +func TestGetLogAuctionObjectAsURLForFloorType(t *testing.T) { + cfg := ow.cfg + defer func() { + ow.cfg = cfg + }() + + ow.cfg.Endpoint = "http://10.172.141.11/wl" + ow.cfg.PublicEndpoint = "http://t.pubmatic.com/wl" + + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + logInfo, forRespExt bool + } + type want struct { + logger string + header http.Header + } + tests := []struct { + name string + args args + want want + }{ + { + name: "Floor type should be soft when prebid is nil", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + NewReqExt: &models.RequestExt{ + ExtRequest: openrtb_ext.ExtRequest{}, + }, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "Floor type should be soft when prebid.floors is nil", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + NewReqExt: &models.RequestExt{ + ExtRequest: openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{}, + }, + }, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "Floor type should be soft when prebid.floors is disabled", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + NewReqExt: &models.RequestExt{ + ExtRequest: openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Enabled: ptrutil.ToPtr(false), + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(true), + }, + }, + }, + }, + }, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "Floor type should be soft when prebid.floors.enforcement is nil", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + NewReqExt: &models.RequestExt{ + ExtRequest: openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Enabled: ptrutil.ToPtr(true), + }, + }, + }, + }, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "Floor type should be soft when prebid.floors.enforcement.enforcepbs is false", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + NewReqExt: &models.RequestExt{ + ExtRequest: openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Enabled: ptrutil.ToPtr(true), + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(false), + }, + }, + }, + }, + }, + PubID: 5890, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":0}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "Floor type should be hard when prebid.floors.enforcement.enforcepbs is true", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + NewReqExt: &models.RequestExt{ + ExtRequest: openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Enabled: ptrutil.ToPtr(true), + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(true), + }, + }, + }, + }, + }, + PubID: 5890, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":1}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + logger, header := GetLogAuctionObjectAsURL(tt.args.ao, tt.args.rCtx, tt.args.logInfo, tt.args.forRespExt) + logger, _ = url.PathUnescape(logger) + assert.Equal(t, tt.want.logger, logger, tt.name) + assert.Equal(t, tt.want.header, header, tt.name) + }) + } +} +func TestGetLogAuctionObjectAsURLForFloorDetails(t *testing.T) { + cfg := ow.cfg + uuidFunc := getUUID + defer func() { + ow.cfg = cfg + getUUID = uuidFunc + }() + + getUUID = func() string { return "uuid" } + ow.cfg.Endpoint = "http://10.172.141.11/wl" + ow.cfg.PublicEndpoint = "http://t.pubmatic.com/wl" + + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + logInfo, forRespExt bool + } + type want struct { + logger string + header http.Header + } + tests := []struct { + name string + args args + want want + }{ + { + name: "set floor details from tracker when slots are absent", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + NewReqExt: &models.RequestExt{ + ExtRequest: openrtb_ext.ExtRequest{}, + }, + Trackers: map[string]models.OWTracker{ + "any-bid": { + Tracker: models.Tracker{ + FloorSkippedFlag: ptrutil.ToPtr(1), + FloorModelVersion: "model-version", + FloorSource: ptrutil.ToPtr(2), + FloorType: 0, + LoggerData: models.LoggerData{ + FloorProvider: "provider", + FloorFetchStatus: ptrutil.ToPtr(3), + }, + }, + }, + }, + }, + logInfo: true, + forRespExt: true, + }, + want: want{ + logger: `http://t.pubmatic.com/wl?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"fmv":"model-version","fsrc":2,"ft":0,"ffs":3,"fp":"provider"}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "set floor details from tracker when slots are present", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + { + ID: "imp-1", + }, + }, + }, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + NewReqExt: &models.RequestExt{ + ExtRequest: openrtb_ext.ExtRequest{}, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp-1": { + AdUnitName: "au", + SlotName: "sn", + }, + }, + Trackers: map[string]models.OWTracker{ + "any-bid": { + Tracker: models.Tracker{ + FloorSkippedFlag: ptrutil.ToPtr(1), + FloorModelVersion: "model-version", + FloorSource: ptrutil.ToPtr(2), + FloorType: 0, + LoggerData: models.LoggerData{ + FloorProvider: "provider", + FloorFetchStatus: ptrutil.ToPtr(3), + }, + }, + }, + }, + }, + logInfo: false, + forRespExt: true, + }, + want: want{ + logger: ow.cfg.Endpoint + `?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"s":[{"sid":"uuid","sn":"sn","au":"au","ps":[],"fskp":1}],"dvc":{},"fmv":"model-version","fsrc":2,"ft":0,"ffs":3,"fp":"provider"}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "set floor details from responseExt if tracker details are absent", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + { + ID: "imp-1", + }, + }, + }, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + NewReqExt: &models.RequestExt{ + ExtRequest: openrtb_ext.ExtRequest{}, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp-1": { + AdUnitName: "au", + SlotName: "sn", + }, + }, + ResponseExt: openrtb_ext.ExtBidResponse{ + Prebid: &openrtb_ext.ExtResponsePrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Skipped: ptrutil.ToPtr(true), + FetchStatus: openrtb_ext.FetchError, + Data: &openrtb_ext.PriceFloorData{ + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "model-version", + }, + }, + FloorProvider: "provider", + }, + PriceFloorLocation: openrtb_ext.FetchLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(true), + }, + }, + }, + }, + }, + logInfo: false, + forRespExt: true, + }, + want: want{ + logger: ow.cfg.Endpoint + `?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"s":[{"sid":"uuid","sn":"sn","au":"au","ps":[],"fskp":1}],"dvc":{},"fmv":"model-version","fsrc":2,"ft":1,"ffs":2,"fp":"provider"}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + logger, header := GetLogAuctionObjectAsURL(tt.args.ao, tt.args.rCtx, tt.args.logInfo, tt.args.forRespExt) + logger, _ = url.PathUnescape(logger) + assert.Equal(t, tt.want.logger, logger, tt.name) + assert.Equal(t, tt.want.header, header, tt.name) + }) + } +} +func TestSlotRecordsInGetLogAuctionObjectAsURL(t *testing.T) { + cfg := ow.cfg + uuidFunc := getUUID + defer func() { + ow.cfg = cfg + getUUID = uuidFunc + }() + + getUUID = func() string { + return "sid" + } + + ow.cfg.Endpoint = "http://10.172.141.11/wl" + ow.cfg.PublicEndpoint = "http://t.pubmatic.com/wl" + + type args struct { + ao analytics.AuctionObject + rCtx *models.RequestCtx + logInfo, forRespExt bool + } + type want struct { + logger string + header http.Header + } + tests := []struct { + name string + args args + want want + }{ + { + name: "req.Imp not mapped in ImpBidCtx", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + { + ID: "imp1", + TagID: "tagid", + }, + }, + }, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + Endpoint: models.EndpointV25, + PubID: 5890, + }, + logInfo: false, + forRespExt: true, + }, + want: want{ + logger: ow.cfg.Endpoint + `?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"dvc":{},"ft":0,"it":"sdk"}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "multi imps request", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + { + ID: "imp_1", + TagID: "tagid_1", + }, + { + ID: "imp_2", + TagID: "tagid_2", + }, + }, + }, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + Endpoint: models.EndpointV25, + ImpBidCtx: map[string]models.ImpCtx{ + "imp_1": { + SlotName: "imp_1_tagid_1", + AdUnitName: "tagid_1", + }, + "imp_2": { + AdUnitName: "tagid_2", + SlotName: "imp_2_tagid_2", + }, + }, + }, + logInfo: false, + forRespExt: true, + }, + want: want{ + logger: ow.cfg.Endpoint + `?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"s":[{"sid":"sid","sn":"imp_1_tagid_1","au":"tagid_1","ps":[]},{"sid":"sid","sn":"imp_2_tagid_2","au":"tagid_2","ps":[]}],"dvc":{},"ft":0,"it":"sdk"}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "multi imps request and one request has incomingslots", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + { + ID: "imp_1", + TagID: "tagid_1", + }, + { + ID: "imp_2", + TagID: "tagid_2", + }, + }, + }, + }, + Response: &openrtb2.BidResponse{}, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + Endpoint: models.EndpointV25, + ImpBidCtx: map[string]models.ImpCtx{ + "imp_1": { + IncomingSlots: []string{"0x0v", "100x200"}, + IsRewardInventory: ptrutil.ToPtr(int8(1)), + SlotName: "imp_1_tagid_1", + AdUnitName: "tagid_1", + }, + "imp_2": { + AdUnitName: "tagid_2", + SlotName: "imp_2_tagid_2", + }, + }, + }, + logInfo: false, + forRespExt: true, + }, + want: want{ + logger: ow.cfg.Endpoint + `?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"s":[{"sid":"sid","sn":"imp_1_tagid_1","sz":["0x0v","100x200"],"au":"tagid_1","ps":[],"rwrd":1},{"sid":"sid","sn":"imp_2_tagid_2","au":"tagid_2","ps":[]}],"dvc":{},"ft":0,"it":"sdk"}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + { + name: "multi imps request and one imp has partner record", + args: args{ + ao: analytics.AuctionObject{ + RequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + { + ID: "imp_1", + TagID: "tagid_1", + }, + { + ID: "imp_2", + TagID: "tagid_2", + }, + }, + }, + }, + Response: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp_1", + }, + }, + }, + }, + }, + }, + rCtx: &models.RequestCtx{ + PubID: 5890, + Endpoint: models.EndpointV25, + ImpBidCtx: map[string]models.ImpCtx{ + "imp_1": { + IncomingSlots: []string{"0x0v", "100x200"}, + IsRewardInventory: ptrutil.ToPtr(int8(1)), + SlotName: "imp_1_tagid_1", + AdUnitName: "tagid_1", + }, + "imp_2": { + AdUnitName: "tagid_2", + SlotName: "imp_2_tagid_2", + }, + }, + }, + logInfo: false, + forRespExt: true, + }, + want: want{ + logger: ow.cfg.Endpoint + `?json={"pubid":5890,"pid":"0","pdvid":"0","sl":1,"s":[{"sid":"sid","sn":"imp_1_tagid_1","sz":["0x0v","100x200"],"au":"tagid_1",` + + `"ps":[{"pn":"pubmatic","bc":"pubmatic","kgpv":"","kgpsv":"","psz":"0x0","af":"","eg":0,"en":0,"l1":0,"l2":0,"t":0,"wb":0,"bidid":"bid-id-1",` + + `"origbidid":"bid-id-1","di":"-1","dc":"","db":0,"ss":1,"mi":0,"ocpm":0,"ocry":"USD"}],"rwrd":1},{"sid":"sid","sn":"imp_2_tagid_2","au":"tagid_2","ps":[]}],"dvc":{},"ft":0,"it":"sdk"}&pubid=5890`, + header: http.Header{ + models.USER_AGENT_HEADER: []string{""}, + models.IP_HEADER: []string{""}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + logger, header := GetLogAuctionObjectAsURL(tt.args.ao, tt.args.rCtx, tt.args.logInfo, tt.args.forRespExt) + logger, _ = url.QueryUnescape(logger) + assert.Equal(t, tt.want.logger, logger, tt.name) + assert.Equal(t, tt.want.header, header, tt.name) + }) + } +} diff --git a/analytics/pubmatic/http_util.go b/analytics/pubmatic/mhttp/http_util.go similarity index 90% rename from analytics/pubmatic/http_util.go rename to analytics/pubmatic/mhttp/http_util.go index e2b2b2c8b0b..747ee908081 100644 --- a/analytics/pubmatic/http_util.go +++ b/analytics/pubmatic/mhttp/http_util.go @@ -1,4 +1,4 @@ -package pubmatic +package mhttp // mhttp - Multi Http Calls // mhttp like multi curl provides a wrapper interface over net/http client to @@ -72,14 +72,22 @@ func Init(maxClients int32, maxConnections, maxCalls, respTimeout int) { } timeout := time.Duration(time.Duration(respTimeout) * time.Millisecond) - for i := int32(0); i < maxClients; i++ { + for i := int32(0); i < maxHttpClients; i++ { //tr := &http.Transport{MaxIdleConnsPerHost: maxConnections, Dial: dialTimeout, ResponseHeaderTimeout: timeout} - tr := &http.Transport{DisableKeepAlives: false, MaxIdleConnsPerHost: maxConnections} + tr := &http.Transport{DisableKeepAlives: false, MaxIdleConnsPerHost: maxHttpConnections} clients[i] = &http.Client{Transport: tr, Timeout: timeout} } nextClientIndex = -1 } +type HttpCallInterface interface { + AddHeader(name, value string) + AddCookie(name, value string) + submit(wg *sync.WaitGroup) + getError() error + GetResponseBody() string +} + // Wrapper to hold both http request and response data for a single http call type HttpCall struct { //Request Section @@ -122,6 +130,11 @@ func (hc *HttpCall) GetResponseBody() string { return hc.respBody } +// API call to get error +func (hc *HttpCall) getError() error { + return hc.err +} + // API call to get the reponse body in string format func (hc *HttpCall) GetResponseHeader(hname string) string { return hc.response.Header.Get(hname) @@ -132,9 +145,14 @@ func (hc *HttpCall) GetResponseHeaders() *http.Header { return &hc.response.Header } +type MultiHttpContextInterface interface { + Execute() (vrc int, erc int) + AddHttpCall(hc HttpCallInterface) +} + // MultiHttpContext is required to hold the information about all http calls to run type MultiHttpContext struct { - hclist [MAX_HTTP_CALLS]*HttpCall + hclist [MAX_HTTP_CALLS]HttpCallInterface hccount int wg sync.WaitGroup } @@ -147,7 +165,7 @@ func NewMultiHttpContext() *MultiHttpContext { } // Add a http call to multi-http-context -func (mhc *MultiHttpContext) AddHttpCall(hc *HttpCall) { +func (mhc *MultiHttpContext) AddHttpCall(hc HttpCallInterface) { if mhc.hccount < maxHttpCalls { mhc.hclist[mhc.hccount] = hc mhc.hccount += 1 @@ -172,7 +190,7 @@ func (mhc *MultiHttpContext) Execute() (vrc int, erc int) { mhc.wg.Wait() //Wait for all go routines to finish for i := 0; i < mhc.hccount; i++ { // validate each response - if mhc.hclist[i].err == nil && mhc.hclist[i].respBody != "" { + if mhc.hclist[i].getError() == nil && mhc.hclist[i].GetResponseBody() != "" { vrc += 1 } else { erc += 1 @@ -182,7 +200,7 @@ func (mhc *MultiHttpContext) Execute() (vrc int, erc int) { } // Get all the http calls from multi-http-context -func (mhc *MultiHttpContext) GetRequestsFromMultiHttpContext() [MAX_HTTP_CALLS]*HttpCall { +func (mhc *MultiHttpContext) GetRequestsFromMultiHttpContext() [MAX_HTTP_CALLS]HttpCallInterface { return mhc.hclist } diff --git a/analytics/pubmatic/mhttp/http_util_test.go b/analytics/pubmatic/mhttp/http_util_test.go new file mode 100644 index 00000000000..381e937591e --- /dev/null +++ b/analytics/pubmatic/mhttp/http_util_test.go @@ -0,0 +1,162 @@ +package mhttp + +import ( + "testing" + + // mock_pubmatic "github.com/prebid/prebid-server/analytics/pubmatic/mock" + + "github.com/stretchr/testify/assert" +) + +func TestNewHttpCall(t *testing.T) { + type args struct { + url string + postdata string + } + type want struct { + method string + err bool + } + tests := []struct { + name string + args args + want want + }{ + { + name: "test GET method", + args: args{ + url: "http://t.pubmatic.com", + postdata: "", + }, + want: want{ + method: "GET", + }, + }, + { + name: "test POST method", + args: args{ + url: "http://t.pubmatic.com/wl", + postdata: "any-data", + }, + want: want{ + method: "POST", + }, + }, + { + name: "test invalid url", + args: args{ + url: "http://invalid-url param=12;", + postdata: "any-data", + }, + want: want{ + method: "POST", + err: true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hc, err := NewHttpCall(tt.args.url, tt.args.postdata) + assert.NotNil(t, hc, tt.name) + assert.Nil(t, err, tt.name) + if !tt.want.err { + assert.Equal(t, tt.want.method, hc.request.Method, tt.name) + } + assert.Equal(t, tt.want.err, hc.err != nil, tt.name) + }) + } +} + +func TestAddHeadersAndCookies(t *testing.T) { + type args struct { + headerKey, headerValue string + cookieKey, cookieValue string + } + + tests := []struct { + name string + args args + }{ + { + name: "test add headers and cookies", + args: args{ + headerKey: "header-key", + headerValue: "header-val", + cookieKey: "cookie-key", + cookieValue: "cookie-val", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hc, _ := NewHttpCall("t.pubmatic.com", "") + hc.AddHeader(tt.args.headerKey, tt.args.headerValue) + hc.AddCookie(tt.args.cookieKey, tt.args.cookieValue) + assert.Equal(t, tt.args.headerValue, hc.request.Header.Get(tt.args.headerKey), tt.name) + cookie, _ := hc.request.Cookie(tt.args.cookieKey) + assert.Equal(t, tt.args.cookieValue, cookie.Value, tt.name) + }) + } +} + +func TestNewMultiHttpContextAndAddHttpCall(t *testing.T) { + mhc := NewMultiHttpContext() + assert.NotNil(t, mhc) + assert.Equal(t, mhc.hccount, 0) + maxHttpCalls = 1 + mhc.AddHttpCall(&HttpCall{}) + mhc.AddHttpCall(&HttpCall{}) + mhc.AddHttpCall(&HttpCall{}) + assert.Equal(t, mhc.hccount, 1) +} + +func TestInit(t *testing.T) { + type args struct { + maxClients int32 + maxConnections, maxCalls, respTimeout int + } + type want struct { + maxHttpClients int32 + maxHttpConnections, maxHttpCalls int + } + tests := []struct { + name string + args args + want want + }{ + { + name: "valid values in accepted range", + args: args{ + maxClients: 1, + maxConnections: 2, + maxCalls: 3, + respTimeout: 10, + }, + want: want{ + maxHttpClients: 1, + maxHttpConnections: 2, + maxHttpCalls: 3, + }, + }, + { + name: "values not in the accepted range", + args: args{ + maxClients: 11111, + maxConnections: 2000, + maxCalls: 300, + respTimeout: 10000, + }, + want: want{ + maxHttpClients: 10240, + maxHttpConnections: 1024, + maxHttpCalls: 200, + }, + }, + } + for _, tt := range tests { + Init(tt.args.maxClients, tt.args.maxConnections, tt.args.maxCalls, tt.args.respTimeout) + assert.Equal(t, tt.want.maxHttpClients, maxHttpClients, tt.name) + assert.Equal(t, tt.want.maxHttpConnections, maxHttpConnections, tt.name) + assert.Equal(t, tt.want.maxHttpCalls, maxHttpCalls, tt.name) + } +} diff --git a/analytics/pubmatic/mhttp/mock/mock.go b/analytics/pubmatic/mhttp/mock/mock.go new file mode 100644 index 00000000000..9f4256b5706 --- /dev/null +++ b/analytics/pubmatic/mhttp/mock/mock.go @@ -0,0 +1,149 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/PubMatic-OpenWrap/prebid-server/analytics/pubmatic/mhttp (interfaces: HttpCallInterface,MultiHttpContextInterface) + +// Package mock_mhttp is a generated GoMock package. +package mock_mhttp + +import ( + gomock "github.com/golang/mock/gomock" + mhttp "github.com/prebid/prebid-server/analytics/pubmatic/mhttp" + reflect "reflect" + sync "sync" +) + +// MockHttpCallInterface is a mock of HttpCallInterface interface +type MockHttpCallInterface struct { + ctrl *gomock.Controller + recorder *MockHttpCallInterfaceMockRecorder +} + +// MockHttpCallInterfaceMockRecorder is the mock recorder for MockHttpCallInterface +type MockHttpCallInterfaceMockRecorder struct { + mock *MockHttpCallInterface +} + +// NewMockHttpCallInterface creates a new mock instance +func NewMockHttpCallInterface(ctrl *gomock.Controller) *MockHttpCallInterface { + mock := &MockHttpCallInterface{ctrl: ctrl} + mock.recorder = &MockHttpCallInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockHttpCallInterface) EXPECT() *MockHttpCallInterfaceMockRecorder { + return m.recorder +} + +// AddCookie mocks base method +func (m *MockHttpCallInterface) AddCookie(arg0, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddCookie", arg0, arg1) +} + +// AddCookie indicates an expected call of AddCookie +func (mr *MockHttpCallInterfaceMockRecorder) AddCookie(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddCookie", reflect.TypeOf((*MockHttpCallInterface)(nil).AddCookie), arg0, arg1) +} + +// AddHeader mocks base method +func (m *MockHttpCallInterface) AddHeader(arg0, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddHeader", arg0, arg1) +} + +// AddHeader indicates an expected call of AddHeader +func (mr *MockHttpCallInterfaceMockRecorder) AddHeader(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddHeader", reflect.TypeOf((*MockHttpCallInterface)(nil).AddHeader), arg0, arg1) +} + +// GetResponseBody mocks base method +func (m *MockHttpCallInterface) GetResponseBody() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetResponseBody") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetResponseBody indicates an expected call of GetResponseBody +func (mr *MockHttpCallInterfaceMockRecorder) GetResponseBody() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetResponseBody", reflect.TypeOf((*MockHttpCallInterface)(nil).GetResponseBody)) +} + +// getError mocks base method +func (m *MockHttpCallInterface) getError() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "getError") + ret0, _ := ret[0].(error) + return ret0 +} + +// getError indicates an expected call of getError +func (mr *MockHttpCallInterfaceMockRecorder) getError() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getError", reflect.TypeOf((*MockHttpCallInterface)(nil).getError)) +} + +// submit mocks base method +func (m *MockHttpCallInterface) submit(arg0 *sync.WaitGroup) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "submit", arg0) +} + +// submit indicates an expected call of submit +func (mr *MockHttpCallInterfaceMockRecorder) submit(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "submit", reflect.TypeOf((*MockHttpCallInterface)(nil).submit), arg0) +} + +// MockMultiHttpContextInterface is a mock of MultiHttpContextInterface interface +type MockMultiHttpContextInterface struct { + ctrl *gomock.Controller + recorder *MockMultiHttpContextInterfaceMockRecorder +} + +// MockMultiHttpContextInterfaceMockRecorder is the mock recorder for MockMultiHttpContextInterface +type MockMultiHttpContextInterfaceMockRecorder struct { + mock *MockMultiHttpContextInterface +} + +// NewMockMultiHttpContextInterface creates a new mock instance +func NewMockMultiHttpContextInterface(ctrl *gomock.Controller) *MockMultiHttpContextInterface { + mock := &MockMultiHttpContextInterface{ctrl: ctrl} + mock.recorder = &MockMultiHttpContextInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockMultiHttpContextInterface) EXPECT() *MockMultiHttpContextInterfaceMockRecorder { + return m.recorder +} + +// AddHttpCall mocks base method +func (m *MockMultiHttpContextInterface) AddHttpCall(arg0 mhttp.HttpCallInterface) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddHttpCall", arg0) +} + +// AddHttpCall indicates an expected call of AddHttpCall +func (mr *MockMultiHttpContextInterfaceMockRecorder) AddHttpCall(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddHttpCall", reflect.TypeOf((*MockMultiHttpContextInterface)(nil).AddHttpCall), arg0) +} + +// Execute mocks base method +func (m *MockMultiHttpContextInterface) Execute() (int, int) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute") + ret0, _ := ret[0].(int) + ret1, _ := ret[1].(int) + return ret0, ret1 +} + +// Execute indicates an expected call of Execute +func (mr *MockMultiHttpContextInterfaceMockRecorder) Execute() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockMultiHttpContextInterface)(nil).Execute)) +} diff --git a/analytics/pubmatic/models.go b/analytics/pubmatic/models.go deleted file mode 100644 index fdf80f12018..00000000000 --- a/analytics/pubmatic/models.go +++ /dev/null @@ -1,133 +0,0 @@ -package pubmatic - -import ( - "encoding/json" - "fmt" - "math" - "net/url" - "regexp" - "strconv" - "strings" - - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" -) - -const ( - //constant for adformat - Banner = "banner" - Video = "video" - Native = "native" - - REVSHARE = "rev_share" - BID_PRECISION = 2 -) - -func GetSize(width, height int64) string { - return fmt.Sprintf("%dx%d", width, height) -} - -// CreatePartnerKey returns key with partner appended -func CreatePartnerKey(partner, key string) string { - if partner == "" { - return key - } - return key + "_" + partner -} - -// GetAdFormat gets adformat from creative(adm) of the bid -func GetAdFormat(adm string) string { - adFormat := Banner - videoRegex, _ := regexp.Compile(" 0 { + partnerRecord.MetaData = &MetaData{ + NetworkID: meta.NetworkID, + AdvertiserID: meta.AdvertiserID, + PrimaryCategoryID: meta.PrimaryCategoryID, + AgencyID: meta.AgencyID, + DemandSource: meta.DemandSource, + SecondaryCategoryIDs: meta.SecondaryCategoryIDs, + } + } + //NOTE : We Don't get following Data points in Response, whenever got from translator, + //they can be populated. + //partnerRecord.MetaData.NetworkName = meta.NetworkName + //partnerRecord.MetaData.AdvertiserName = meta.AdvertiserName + //partnerRecord.MetaData.AgencyName = meta.AgencyName + //partnerRecord.MetaData.BrandName = meta.BrandName + //partnerRecord.MetaData.BrandID = meta.BrandID + //partnerRecord.MetaData.DChain = meta.DChain (type is json.RawMessage) } diff --git a/analytics/pubmatic/record_test.go b/analytics/pubmatic/record_test.go new file mode 100644 index 00000000000..9a07a2ce62a --- /dev/null +++ b/analytics/pubmatic/record_test.go @@ -0,0 +1,514 @@ +package pubmatic + +import ( + "encoding/json" + "testing" + + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/util/ptrutil" + "github.com/stretchr/testify/assert" +) + +func TestLogDeviceObject(t *testing.T) { + type args struct { + ortbBidRequest *openrtb2.BidRequest + rctx *models.RequestCtx + } + + type want struct { + device Device + } + + tests := []struct { + name string + args args + want want + }{ + { + name: `Nil request`, + args: args{ + rctx: &models.RequestCtx{ + DevicePlatform: models.DevicePlatformDesktop, + }, + }, + want: want{ + device: Device{ + Platform: models.DevicePlatformDesktop, + }, + }, + }, + { + name: `Empty uaFromHTTPReq`, + args: args{ + ortbBidRequest: &openrtb2.BidRequest{}, + rctx: &models.RequestCtx{ + DevicePlatform: models.DevicePlatformMobileWeb, + }, + }, + want: want{ + device: Device{ + Platform: models.DevicePlatformMobileWeb, + }, + }, + }, + { + name: `Invalid device ext`, + args: args{ + ortbBidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`invalid ext`), + }, + }, + rctx: &models.RequestCtx{ + DevicePlatform: models.DevicePlatformMobileWeb, + }, + }, + want: want{ + device: Device{ + Platform: 0, + }, + }, + }, + { + name: `IFA Type key absent`, + args: args{ + ortbBidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"anykey":"anyval"}`), + }, + }, + rctx: &models.RequestCtx{ + DevicePlatform: models.DevicePlatformMobileWeb, + }, + }, + want: want{ + device: Device{ + Platform: models.DevicePlatformMobileWeb, + }, + }, + }, + { + name: `Invalid data type for ifa_type key`, + args: args{ + ortbBidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"ifa_type": 123}`)}, + }, + rctx: &models.RequestCtx{ + DevicePlatform: models.DevicePlatformMobileWeb, + }, + }, + want: want{ + device: Device{ + Platform: models.DevicePlatformMobileWeb, + }, + }, + }, + { + name: `ifa_type missing in DeviceIFATypeID mapping`, + args: args{ + ortbBidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"ifa_type": "anything"}`), + }, + }, + rctx: &models.RequestCtx{ + DevicePlatform: models.DevicePlatformMobileWeb, + }, + }, + want: want{ + device: Device{ + Platform: models.DevicePlatformMobileWeb, + IFAType: ptrutil.ToPtr(0), + }, + }, + }, + { + name: `Case insensitive ifa_type`, + args: args{ + ortbBidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"ifa_type": "DpId"}`), + }, + }, + rctx: &models.RequestCtx{ + DevicePlatform: models.DevicePlatformMobileWeb, + }, + }, + want: want{ + device: Device{ + Platform: models.DevicePlatformMobileWeb, + IFAType: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeDPID]), + }, + }, + }, + { + name: `Valid ifa_type`, + args: args{ + ortbBidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"ifa_type": "sessionid"}`), + }, + }, + rctx: &models.RequestCtx{ + DevicePlatform: models.DevicePlatformMobileWeb, + }, + }, + want: want{ + device: Device{ + Platform: models.DevicePlatformMobileWeb, + IFAType: ptrutil.ToPtr(models.DeviceIFATypeID[models.DeviceIFATypeSESSIONID]), + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + wlog := &WloggerRecord{} + wlog.logDeviceObject(tt.args.rctx, tt.args.ortbBidRequest) + assert.Equal(t, tt.want.device, wlog.Device) + }) + } +} + +func TestLogIntegrationType(t *testing.T) { + tests := []struct { + name string + endpoint string + integrationType string + }{ + { + name: "sdk", + endpoint: models.EndpointV25, + integrationType: models.TypeSDK, + }, + { + name: "amp", + endpoint: models.EndpointAMP, + integrationType: models.TypeAmp, + }, + { + name: "ctv-vast", + endpoint: models.EndpointVAST, + integrationType: models.TypeTag, + }, + { + name: "ctv-ortb", + endpoint: models.EndpointORTB, + integrationType: models.TypeS2S, + }, + { + name: "ctv-json", + endpoint: models.EndpointJson, + integrationType: models.TypeInline, + }, + { + name: "openrtb-video", + endpoint: models.EndpointVideo, + integrationType: models.TypeInline, + }, + { + name: "invalid", + endpoint: "invalid", + integrationType: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + wlog := WloggerRecord{} + wlog.logIntegrationType(tt.endpoint) + assert.Equal(t, tt.integrationType, wlog.IntegrationType, tt.name) + }) + } +} + +func TestLogFloorType(t *testing.T) { + tests := []struct { + name string + prebidExt *openrtb_ext.ExtRequestPrebid + floorType int + }{ + { + name: "Nil prebidExt", + prebidExt: nil, + floorType: models.SoftFloor, + }, + { + name: "Nil prebidExt.Floors", + prebidExt: &openrtb_ext.ExtRequestPrebid{}, + floorType: models.SoftFloor, + }, + { + name: "Nil prebidExt.Floors.Enabled", + prebidExt: &openrtb_ext.ExtRequestPrebid{ + Floors: &openrtb_ext.PriceFloorRules{}, + }, + floorType: models.SoftFloor, + }, + { + name: "false prebidExt.Floors.Enabled", + prebidExt: &openrtb_ext.ExtRequestPrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Enabled: ptrutil.ToPtr(false), + }, + }, + floorType: models.SoftFloor, + }, + { + name: "Nil prebidExt.Floors.Enabled.Enforcement", + prebidExt: &openrtb_ext.ExtRequestPrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Enabled: ptrutil.ToPtr(true), + Enforcement: nil, + }, + }, + floorType: models.SoftFloor, + }, + { + name: "Nil prebidExt.Floors.Enabled.Enforcement.EnforcePBS", + prebidExt: &openrtb_ext.ExtRequestPrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Enabled: ptrutil.ToPtr(true), + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: nil, + }, + }, + }, + floorType: models.SoftFloor, + }, + { + name: "false prebidExt.Floors.Enabled.Enforcement.EnforcePBS", + prebidExt: &openrtb_ext.ExtRequestPrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Enabled: ptrutil.ToPtr(true), + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(false), + }, + }, + }, + floorType: models.SoftFloor, + }, + { + name: "true prebidExt.Floors.Enabled.Enforcement.EnforcePBS", + prebidExt: &openrtb_ext.ExtRequestPrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Enabled: ptrutil.ToPtr(true), + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(true), + }, + }, + }, + floorType: models.HardFloor, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + wlog := WloggerRecord{} + wlog.logFloorType(tt.prebidExt) + assert.Equal(t, tt.floorType, wlog.FloorType, tt.name) + }) + } +} + +func TestLogContentObject(t *testing.T) { + type args struct { + content *openrtb2.Content + } + tests := []struct { + name string + args args + want *Content + }{ + { + name: "Empty", + args: args{}, + want: nil, + }, + { + name: "OnlyID", + args: args{ + content: &openrtb2.Content{ + ID: "ID", + }, + }, + want: &Content{ + ID: "ID", + }, + }, + { + name: "OnlyEpisode", + args: args{ + content: &openrtb2.Content{ + Episode: 123, + }, + }, + want: &Content{ + Episode: 123, + }, + }, + { + name: "OnlyTitle", + args: args{ + content: &openrtb2.Content{ + Title: "Title", + }, + }, + want: &Content{ + Title: "Title", + }, + }, + { + name: "OnlySeries", + args: args{ + content: &openrtb2.Content{ + Series: "Series", + }, + }, + want: &Content{ + Series: "Series", + }, + }, + { + name: "OnlySeason", + args: args{ + content: &openrtb2.Content{ + Season: "Season", + }, + }, + want: &Content{ + Season: "Season", + }, + }, + { + name: "OnlyCat", + args: args{ + content: &openrtb2.Content{ + Cat: []string{"CAT-1"}, + }, + }, + want: &Content{ + Cat: []string{"CAT-1"}, + }, + }, + { + name: "AllPresent", + args: args{ + content: &openrtb2.Content{ + ID: "ID", + Episode: 123, + Title: "Title", + Series: "Series", + Season: "Season", + Cat: []string{"CAT-1"}, + }, + }, + want: &Content{ + ID: "ID", + Episode: 123, + Title: "Title", + Series: "Series", + Season: "Season", + Cat: []string{"CAT-1"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + wlog := &WloggerRecord{} + wlog.logContentObject(tt.args.content) + assert.Equal(t, tt.want, wlog.Content) + }) + } +} + +func TestSetMetaDataObject(t *testing.T) { + type args struct { + meta *openrtb_ext.ExtBidPrebidMeta + partnerRecord *PartnerRecord + } + tests := []struct { + name string + args args + partnerRecord *PartnerRecord + }{ + { + name: "NetworkID 0, AdvertiserID 0, SecondaryCategoryIDs size 0", + args: args{ + meta: &openrtb_ext.ExtBidPrebidMeta{ + NetworkID: 0, + AdvertiserID: 0, + SecondaryCategoryIDs: []string{}, + }, + partnerRecord: &PartnerRecord{ + PartnerID: "pubmatic", + }, + }, + partnerRecord: &PartnerRecord{ + PartnerID: "pubmatic", + }, + }, + { + name: "NetworkID other than 0", + args: args{ + meta: &openrtb_ext.ExtBidPrebidMeta{ + NetworkID: 10, + AdvertiserID: 0, + }, + partnerRecord: &PartnerRecord{ + PartnerID: "pubmatic", + }, + }, + partnerRecord: &PartnerRecord{ + PartnerID: "pubmatic", + MetaData: &MetaData{ + NetworkID: 10, + }, + }, + }, + { + name: "AdvertiserID other than 0", + args: args{ + meta: &openrtb_ext.ExtBidPrebidMeta{ + NetworkID: 0, + AdvertiserID: 10, + }, + partnerRecord: &PartnerRecord{ + PartnerID: "pubmatic", + }, + }, + partnerRecord: &PartnerRecord{ + PartnerID: "pubmatic", + MetaData: &MetaData{ + AdvertiserID: 10, + }, + }, + }, + { + name: "SecondaryCategoryIDs size other than 0", + args: args{ + meta: &openrtb_ext.ExtBidPrebidMeta{ + NetworkID: 0, + AdvertiserID: 0, + SecondaryCategoryIDs: []string{"cat1"}, + }, + partnerRecord: &PartnerRecord{ + PartnerID: "pubmatic", + }, + }, + partnerRecord: &PartnerRecord{ + PartnerID: "pubmatic", + MetaData: &MetaData{ + SecondaryCategoryIDs: []string{"cat1"}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.args.partnerRecord.setMetaDataObject(tt.args.meta) + assert.Equal(t, tt.partnerRecord, tt.args.partnerRecord, tt.name) + }) + } +} diff --git a/exchange/exchange.go b/exchange/exchange.go index 6cce21d4753..6b5fbc487f6 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -713,7 +713,6 @@ func (e *exchange) getAllBids( adapterExtra := make(map[openrtb_ext.BidderName]*seatResponseExtra, len(bidderRequests)) chBids := make(chan *bidResponseWrapper, len(bidderRequests)) extraRespInfo := extraAuctionResponseInfo{} - bidIDsCollision := false e.me.RecordOverheadTime(metrics.MakeBidderRequests, time.Since(pbsRequestStartTime)) @@ -809,16 +808,17 @@ func (e *exchange) getAllBids( } //but we need to add all bidders data to adapterExtra to have metrics and other metadata adapterExtra[brw.bidder] = brw.adapterExtra - - if !extraRespInfo.bidsFound && adapterBids[brw.bidder] != nil && len(adapterBids[brw.bidder].Bids) > 0 { + } + for _, adapterBid := range adapterBids { + if len(adapterBid.Bids) > 0 { extraRespInfo.bidsFound = true - bidIDsCollision = recordAdaptorDuplicateBidIDs(e.me, adapterBids) + bidIDsCollision := recordAdaptorDuplicateBidIDs(e.me, adapterBids) + if bidIDsCollision { + e.me.RecordRequestHavingDuplicateBidID() + } + break } } - if bidIDsCollision { - // record this request count this request if bid collision is detected - e.me.RecordRequestHavingDuplicateBidID() - } return adapterBids, adapterExtra, extraRespInfo } diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 4dee6bbbfc5..c4d6e70f638 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -2777,12 +2777,13 @@ func TestCategoryMapping(t *testing.T) { Ext: openrtb_ext.NonBidExt{ Prebid: openrtb_ext.ExtResponseNonBidPrebid{ Bid: openrtb_ext.NonBidObject{ - Price: 40.0000, - Cat: cats4, - W: 1, - H: 1, - OriginalBidCPM: 40, - OriginalBidCur: "USD", + Price: 40.0000, + Cat: cats4, + W: 1, + H: 1, + OriginalBidCPM: 40, + OriginalBidCur: "USD", + OriginalBidCPMUSD: 40, ID: "bid_id4", Type: openrtb_ext.BidTypeVideo, @@ -2927,12 +2928,13 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) { Ext: openrtb_ext.NonBidExt{ Prebid: openrtb_ext.ExtResponseNonBidPrebid{ Bid: openrtb_ext.NonBidObject{ - Price: 30.0000, - Cat: cats3, - W: 1, - H: 1, - OriginalBidCPM: 30, - OriginalBidCur: "USD", + Price: 30.0000, + Cat: cats3, + W: 1, + H: 1, + OriginalBidCPM: 30, + OriginalBidCur: "USD", + OriginalBidCPMUSD: 30, ID: "bid_id3", Type: openrtb_ext.BidTypeVideo, @@ -3396,12 +3398,13 @@ func TestBidRejectionErrors(t *testing.T) { Ext: openrtb_ext.NonBidExt{ Prebid: openrtb_ext.ExtResponseNonBidPrebid{ Bid: openrtb_ext.NonBidObject{ - Price: 10.0000, - Cat: []string{}, - W: 1, - H: 1, - OriginalBidCPM: 10, - OriginalBidCur: "USD", + Price: 10.0000, + Cat: []string{}, + W: 1, + H: 1, + OriginalBidCPM: 10, + OriginalBidCur: "USD", + OriginalBidCPMUSD: 10, ID: "bid_id1", Type: openrtb_ext.BidTypeVideo, @@ -3435,12 +3438,13 @@ func TestBidRejectionErrors(t *testing.T) { Ext: openrtb_ext.NonBidExt{ Prebid: openrtb_ext.ExtResponseNonBidPrebid{ Bid: openrtb_ext.NonBidObject{ - Price: 10.0000, - Cat: []string{"IAB1-1"}, - W: 1, - H: 1, - OriginalBidCPM: 10, - OriginalBidCur: "USD", + Price: 10.0000, + Cat: []string{"IAB1-1"}, + W: 1, + H: 1, + OriginalBidCPM: 10, + OriginalBidCur: "USD", + OriginalBidCPMUSD: 10, ID: "bid_id1", Type: openrtb_ext.BidTypeVideo, @@ -3474,12 +3478,13 @@ func TestBidRejectionErrors(t *testing.T) { Ext: openrtb_ext.NonBidExt{ Prebid: openrtb_ext.ExtResponseNonBidPrebid{ Bid: openrtb_ext.NonBidObject{ - Price: 10.0000, - Cat: []string{"IAB1-1"}, - W: 1, - H: 1, - OriginalBidCPM: 10, - OriginalBidCur: "USD", + Price: 10.0000, + Cat: []string{"IAB1-1"}, + W: 1, + H: 1, + OriginalBidCPM: 10, + OriginalBidCur: "USD", + OriginalBidCPMUSD: 10, ID: "bid_id1", Type: openrtb_ext.BidTypeVideo, @@ -3515,12 +3520,13 @@ func TestBidRejectionErrors(t *testing.T) { Ext: openrtb_ext.NonBidExt{ Prebid: openrtb_ext.ExtResponseNonBidPrebid{ Bid: openrtb_ext.NonBidObject{ - Price: 10.0000, - Cat: []string{"IAB1-1"}, - W: 1, - H: 1, - OriginalBidCPM: 10, - OriginalBidCur: "USD", + Price: 10.0000, + Cat: []string{"IAB1-1"}, + W: 1, + H: 1, + OriginalBidCPM: 10, + OriginalBidCur: "USD", + OriginalBidCPMUSD: 10, ID: "bid_id1", Type: openrtb_ext.BidTypeVideo, diff --git a/exchange/seat_non_bids.go b/exchange/seat_non_bids.go index 6eeca596afb..ed675d9706b 100644 --- a/exchange/seat_non_bids.go +++ b/exchange/seat_non_bids.go @@ -44,6 +44,7 @@ func (snb *nonBids) addBid(bid *entities.PbsOrtbBid, nonBidReason int, seat stri Video: bid.BidVideo, BidId: bid.GeneratedBidID, Floors: bid.BidFloors, + OriginalBidCPMUSD: bid.OriginalBidCPMUSD, }}, }, } diff --git a/go.mod b/go.mod index 7a8abb2f726..71d1d3b6b84 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( github.com/go-sql-driver/mysql v1.7.0 github.com/golang/mock v1.6.0 github.com/satori/go.uuid v1.2.0 + golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 ) require ( @@ -78,8 +79,9 @@ require ( github.com/subosito/gotenv v1.3.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/yudai/pp v2.0.1+incompatible // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/sys v0.5.0 // indirect + golang.org/x/sys v0.14.0 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.66.4 // indirect diff --git a/go.sum b/go.sum index 8086862d444..2530cd440cf 100644 --- a/go.sum +++ b/go.sum @@ -29,310 +29,57 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go/accessapproval v1.5.0 h1:/nTivgnV/n1CaAeo+ekGexTYUsKEU9jUVkoY5359+3Q= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accesscontextmanager v1.4.0 h1:CFhNhU7pcD11cuDkQdrE6PQJgv0EXNKNv06jIzbLlCU= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/aiplatform v1.27.0 h1:DBi3Jk9XjCJ4pkkLM4NqKgj3ozUL1wq4l+d3/jTGXAI= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/analytics v0.12.0 h1:NKw6PpQi6V1O+KsjuTd+bhip9d0REYu4NevC45vtGp8= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/apigateway v1.4.0 h1:IIoXKR7FKrEAQhMTz5hK2wiDz2WNFHS7eVr/L1lE/rM= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigeeconnect v1.4.0 h1:AONoTYJviyv1vS4IkvWzq69gEVdvHx35wKXc+e6wjZQ= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/appengine v1.5.0 h1:lmG+O5oaR9xNwaRBwE2XoMhwQHsHql5IoiGr1ptdDwU= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/area120 v0.6.0 h1:TCMhwWEWhCn8d44/Zs7UCICTWje9j3HuV6nVGMjdpYw= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/artifactregistry v1.9.0 h1:3d0LRAU1K6vfqCahhl9fx2oGHcq+s5gftdix4v8Ibrc= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/asset v1.10.0 h1:aCrlaLGJWTODJX4G56ZYzJefITKEWNfbjjtHSzWpxW0= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/assuredworkloads v1.9.0 h1:hhIdCOowsT1GG5eMCIA0OwK6USRuYTou/1ZeNxCSRtA= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/automl v1.8.0 h1:BMioyXSbg7d7xLibn47cs0elW6RT780IUWr42W8rp2Q= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/baremetalsolution v0.4.0 h1:g9KO6SkakcYPcc/XjAzeuUrEOXlYPnMpuiaywYaGrmQ= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/batch v0.4.0 h1:1jvEBY55OH4Sd2FxEXQfxGExFWov1A/IaRe+Z5Z71Fw= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/beyondcorp v0.3.0 h1:w+4kThysgl0JiKshi2MKDCg2NZgOyqOI0wq2eBZyrzA= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigquery v1.44.0 h1:Wi4dITi+cf9VYp4VH2T9O41w0kCW0uQTELq2Z6tukN0= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/billing v1.7.0 h1:Xkii76HWELHwBtkQVZvqmSo9GTr0O+tIbRNnMcGdlg4= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/binaryauthorization v1.4.0 h1:pL70vXWn9TitQYXBWTK2abHl2JHLwkFRjYw6VflRqEA= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/certificatemanager v1.4.0 h1:tzbR4UHBbgsewMWUD93JHi8EBi/gHBoSAcY1/sThFGk= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/channel v1.9.0 h1:pNuUlZx0Jb0Ts9P312bmNMuH5IiFWIR4RUtLb70Ke5s= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/cloudbuild v1.4.0 h1:TAAmCmAlOJ4uNBu6zwAjwhyl/7fLHHxIEazVhr3QBbQ= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/clouddms v1.4.0 h1:UhzHIlgFfMr6luVYVNydw/pl9/U5kgtjCMJHnSvoVws= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/cloudtasks v1.8.0 h1:faUiUgXjW8yVZ7XMnKHKm1WE4OldPBUWWfIRN/3z1dc= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/compute v1.15.1 h1:7UGq3QknM33pw5xATlpzeoomNxsacIVvTqTTvbfajmE= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/contactcenterinsights v1.4.0 h1:tTQLI/ZvguUf9Hv+36BkG2+/PeC8Ol1q4pBW+tgCx0A= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/container v1.7.0 h1:nbEK/59GyDRKKlo1SqpohY1TK8LmJ2XNcvS9Gyom2A0= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/containeranalysis v0.6.0 h1:2824iym832ljKdVpCBnpqm5K94YT/uHTVhNF+dRTXPI= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/datacatalog v1.8.0 h1:6kZ4RIOW/uT7QWC5SfPfq/G8sYzr/v+UOmOAxy4Z1TE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/dataflow v0.7.0 h1:CW3541Fm7KPTyZjJdnX6NtaGXYFn5XbFC5UcjgALKvU= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataform v0.5.0 h1:vLwowLF2ZB5J5gqiZCzv076lDI/Rd7zYQQFu5XO1PSg= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/datafusion v1.5.0 h1:j5m2hjWovTZDTQak4MJeXAR9yN7O+zMfULnjGw/OOLg= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datalabeling v0.6.0 h1:dp8jOF21n/7jwgo/uuA0RN8hvLcKO4q6s/yvwevs2ZM= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/dataplex v1.4.0 h1:cNxeA2DiWliQGi21kPRqnVeQ5xFhNoEjPRt1400Pm8Y= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataproc v1.8.0 h1:gVOqNmElfa6n/ccG/QDlfurMWwrK3ezvy2b2eDoCmS0= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataqna v0.6.0 h1:gx9jr41ytcA3dXkbbd409euEaWtofCVXYBvJz3iYm18= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastore v1.10.0 h1:4siQRf4zTiAVt/oeH4GureGkApgb2vtPQAtOmhpqQwE= -cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastream v1.5.0 h1:PgIgbhedBtYBU6POGXFMn2uSl9vpqubc3ewTNdcU8Mk= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/deploy v1.5.0 h1:kI6dxt8Ml0is/x7YZjLveTvR7YPzXAUD/8wQZ2nH5zA= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/dialogflow v1.19.0 h1:HYHVOkoxQ9bSfNIelSZYNAtUi4CeSrCnROyOsbOqPq8= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dlp v1.7.0 h1:9I4BYeJSVKoSKgjr70fLdRDumqcUeVmHV4fd5f9LR6Y= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/documentai v1.10.0 h1:jfq09Fdjtnpnmt/MLyf6A3DM3ynb8B2na0K+vSXvpFM= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/domains v0.7.0 h1:pu3JIgC1rswIqi5romW0JgNO6CTUydLYX8zyjiAvO1c= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/edgecontainer v0.2.0 h1:hd6J2n5dBBRuAqnNUEsKWrp6XNPKsaxwwIyzOPZTokk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.4.0 h1:b6csrQXCHKQmfo9h3dG/pHyoEh+fQG1Yg78a53LAviY= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/eventarc v1.8.0 h1:AgCqrmMMIcel5WWKkzz5EkCUKC3Rl5LNMMYsS+LvsI0= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/filestore v1.4.0 h1:yjKOpzvqtDmL5AXbKttLc8j0hL20kuC1qPdy5HPcxp0= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= -cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/functions v1.9.0 h1:35tgv1fQOtvKqH/uxJMzX3w6usneJ0zXpsFr9KAVhNE= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/gaming v1.8.0 h1:97OAEQtDazAJD7yh/kvQdSCQuTKdR0O+qWAJBZJ4xiA= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gkebackup v0.3.0 h1:4K+jiv4ocqt1niN8q5Imd8imRoXBHTrdnJVt/uFFxF4= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkeconnect v0.6.0 h1:zAcvDa04tTnGdu6TEZewaLN2tdMtUOJJ7fEceULjguA= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkehub v0.10.0 h1:JTcTaYQRGsVm+qkah7WzHb6e9sf1C0laYdRPn9aN+vg= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkemulticloud v0.4.0 h1:8F1NhJj8ucNj7lK51UZMtAjSWTgP1zO18XF6vkfiPPU= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gsuiteaddons v1.4.0 h1:TGT2oGmO5q3VH6SjcrlgPUWI0njhYv4kywLm6jag0to= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iap v1.5.0 h1:BGEXovwejOCt1zDk8hXq0bOhhRu9haXKWXXXp2B4wBM= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/ids v1.2.0 h1:LncHK4HHucb5Du310X8XH9/ICtMwZ2PCfK0ScjWiJoY= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/iot v1.4.0 h1:Y9+oZT9jD4GUZzORXTU45XsnQrhxmDT+TFbPil6pRVQ= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/language v1.8.0 h1:3Wa+IUMamL4JH3Zd3cDZUHpwyqplTACt6UZKRD2eCL4= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/lifesciences v0.6.0 h1:tIqhivE2LMVYkX0BLgG7xL64oNpDaFFI7teunglt1tI= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/logging v1.6.1 h1:ZBsZK+JG+oCDT+vaxwqF2egKNRjz8soXiS6Xv79benI= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/managedidentities v1.4.0 h1:3Kdajn6X25yWQFhFCErmKSYTSvkEd3chJROny//F1A0= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/maps v0.1.0 h1:kLReRbclTgJefw2fcCbdLPLhPj0U6UUWN10ldG8sdOU= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/mediatranslation v0.6.0 h1:qAJzpxmEX+SeND10Y/4868L5wfZpo4Y3BIEnIieP4dk= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/memcache v1.7.0 h1:yLxUzJkZVSH2kPaHut7k+7sbIBFpvSh1LW9qjM2JDjA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/metastore v1.8.0 h1:3KcShzqWdqxrDEXIBWpYJpOOrgpDj+HlBi07Grot49Y= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/monitoring v1.8.0 h1:c9riaGSPQ4dUKWB+M1Fl0N+iLxstMbCktdEwYSPGDvA= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/networkconnectivity v1.7.0 h1:BVdIKaI68bihnXGdCVL89Jsg9kq2kg+II30fjVqo62E= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkmanagement v1.5.0 h1:mDHA3CDW00imTvC5RW6aMGsD1bH+FtKwZm/52BxaiMg= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networksecurity v0.6.0 h1:qDEX/3sipg9dS5JYsAY+YvgTjPR63cozzAWop8oZS94= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/notebooks v1.5.0 h1:AC8RPjNvel3ExgXjO1YOAz+teg9+j+89TNxa7pIZfww= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/optimization v1.2.0 h1:7PxOq9VTT7TMib/6dMoWpMvWS2E4dJEvtYzjvBreaec= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/orchestration v1.4.0 h1:39d6tqvNjd/wsSub1Bn4cEmrYcet5Ur6xpaN+SxOxtY= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orgpolicy v1.5.0 h1:erF5PHqDZb6FeFrUHiYj2JK2BMhsk8CyAg4V4amJ3rE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/osconfig v1.10.0 h1:NO0RouqCOM7M2S85Eal6urMSSipWwHU8evzwS+siqUI= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/oslogin v1.7.0 h1:pKGDPfeZHDybtw48WsnVLjoIPMi9Kw62kUE5TXCLCN4= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/phishingprotection v0.6.0 h1:OrwHLSRSZyaiOt3tnY33dsKSedxbMzsXvqB21okItNQ= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/policytroubleshooter v1.4.0 h1:NQklJuOUoz1BPP+Epjw81COx7IISWslkZubz/1i0UN8= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/privatecatalog v0.6.0 h1:Vz86uiHCtNGm1DeC32HeG2VXmOq5JRYA3VRPf8ZEcSg= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/pubsub v1.27.1 h1:q+J/Nfr6Qx4RQeu3rJcnN48SNC0qzlYzSeqkPq93VHs= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsublite v1.5.0 h1:iqrD8vp3giTb7hI1q4TQQGj77cj8zzgmMPsTZtLnprM= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0 h1:UqzFfb/WvhwXGDF1eQtdHLrmni+iByZXY4h3w9Kdyv8= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recommendationengine v0.6.0 h1:6w+WxPf2LmUEqX0YyvfCoYb8aBYOcbIV25Vg6R0FLGw= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommender v1.8.0 h1:9kMZQGeYfcOD/RtZfcNKGKtoex3DdoB4zRgYU/WaIwE= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/redis v1.10.0 h1:/zTwwBKIAD2DEWTrXZp8WD9yD/gntReF/HkPssVYd0U= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/resourcemanager v1.4.0 h1:NDao6CHMwEZIaNsdWy+tuvHaavNeGP06o1tgrR0kLvU= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcesettings v1.4.0 h1:eTzOwB13WrfF0kuzG2ZXCfB3TLunSHBur4s+HFU6uSM= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/retail v1.11.0 h1:N9fa//ecFUOEPsW/6mJHfcapPV0wBSwIUwpVZB7MQ3o= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/run v0.3.0 h1:AWPuzU7Xtaj3Jf+QarDWIs6AJ5hM1VFQ+F6Q+VZ6OT4= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/scheduler v1.7.0 h1:K/mxOewgHGeKuATUJNGylT75Mhtjmx1TOkKukATqMT8= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/secretmanager v1.9.0 h1:xE6uXljAC1kCR8iadt9+/blg1fvSbmenlsDN4fT9gqw= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/security v1.10.0 h1:KSKzzJMyUoMRQzcz7azIgqAUqxo7rmQ5rYvimMhikqg= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/securitycenter v1.16.0 h1:QTVtk/Reqnx2bVIZtJKm1+mpfmwRwymmNvlaFez7fQY= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/servicecontrol v1.5.0 h1:ImIzbOu6y4jL6ob65I++QzvqgFaoAKgHOG+RU9/c4y8= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicedirectory v1.7.0 h1:f7M8IMcVzO3T425AqlZbP3yLzeipsBHtRza8vVFYMhQ= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicemanagement v1.5.0 h1:TpkCO5M7dhKSy1bKUD9o/sSEW/U1Gtx7opA1fsiMx0c= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/serviceusage v1.4.0 h1:b0EwJxPJLpavSljMQh0RcdHsUrr5DQ+Nelt/3BAs5ro= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/shell v1.4.0 h1:b1LFhFBgKsG252inyhtmsUUZwchqSz3WTvAIf3JFo4g= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/spanner v1.41.0 h1:NvdTpRwf7DTegbfFdPjAWyD7bOVu0VeMqcvR9aCQCAc= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/speech v1.9.0 h1:yK0ocnFH4Wsf0cMdUyndJQ/hPv02oTJOxzi6AgpBy4s= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0 h1:6RRlFMv1omScs6iq2hfE3IvgE+l6RfJPampq8UZc5TU= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storagetransfer v1.6.0 h1:fUe3OydbbvHcAYp07xY+2UpH4AermGbmnm7qdEj3tGE= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/talent v1.4.0 h1:MrekAGxLqAeAol4Sc0allOVqUGO8j+Iim8NMvpiD7tM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/texttospeech v1.5.0 h1:ccPiHgTewxgyAeCWgQWvZvrLmbfQSFABTMAfrSPLPyY= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/tpu v1.4.0 h1:ztIdKoma1Xob2qm6QwNh4Xi9/e7N3IfvtwG5AcNsj1g= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/trace v1.4.0 h1:qO9eLn2esajC9sxpqp1YKX37nXC3L4BfGnPS0Cx9dYo= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/translate v1.4.0 h1:AOYOH3MspzJ/bH1YXzB+xTE8fMpn3mwhLjugwGXvMPI= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/video v1.9.0 h1:ttlvO4J5c1VGq6FkHqWPD/aH6PfdxujHt+muTJlW1Zk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/videointelligence v1.9.0 h1:RPFgVVXbI2b5vnrciZjtsUgpNKVtHO/WIyXUhEfuMhA= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/vision/v2 v2.5.0 h1:TQHxRqvLMi19azwm3qYuDbEzZWmiKJNTpGbkNsfRCik= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vmmigration v1.3.0 h1:A2Tl2ZmwMRpvEmhV2ibISY85fmQR+Y5w9a0PlRz5P3s= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmwareengine v0.1.0 h1:JMPZaOT/gIUxVlTqSl/QQ32Y2k+r0stNeM1NSqhVP9o= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vpcaccess v1.5.0 h1:woHXXtnW8b9gLFdWO9HLPalAddBQ9V4LT+1vjKwR3W8= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/webrisk v1.7.0 h1:ypSnpGlJnZSXbN9a13PDmAYvVekBLnGKxQ3Q9SMwnYY= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/websecurityscanner v1.4.0 h1:y7yIFg/h/mO+5Y5aCOtVAnpGUOgqCH5rXQ2Oc8Oq2+g= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/workflows v1.9.0 h1:7Chpin9p50NTU8Tb7qk+I11U/IwVXmDhEoSsdccvInE= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20231102070946-3c5a3bc1dff5 h1:nK2YP3aS8+5dwc5cMJ8IxI0Sh7yfd858LKmcvwfkOUo= git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20231102070946-3c5a3bc1dff5/go.mod h1:dgTumQ6/KYeLbpWq3HVOaqkZos6Q0QGwZmQmEIhQ3To= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/IABTechLab/adscert v0.34.0 h1:UNM2gMfRPGUbv3KDiLJmy2ajaVCfF3jWqgVKkz8wBu8= github.com/IABTechLab/adscert v0.34.0/go.mod h1:pCLd3Up1kfTrH6kYFUGGeavxIc1f6Tvvj8yJeFRb7mA= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PubMatic-OpenWrap/etree v1.0.2-0.20210129100623-8f30cfecf9f4 h1:EhiijwjoKTx7FVP8p2wwC/z4n5l4c8l2CGmsrFv2uhI= github.com/PubMatic-OpenWrap/etree v1.0.2-0.20210129100623-8f30cfecf9f4/go.mod h1:5Y8qgcuDoy3XXG907UXkGnVTwihF16rXyJa4zRT7hOE= github.com/PubMatic-OpenWrap/prebid-openrtb/v19 v19.0.0-20230517094918-56ce40c97ced h1:a4dslMnlBKJTTgBuKKKPT4V43/cespgaVd1y0TO0b4M= github.com/PubMatic-OpenWrap/prebid-openrtb/v19 v19.0.0-20230517094918-56ce40c97ced/go.mod h1:jK+/g4Dh5vOnNl0Nh7isbZlub29aJYyrtoBkjmhzTIg= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alitto/pond v1.8.2 h1:k0k3GIE7CFLW/kyMJj5DDKLFg1VH09l8skZqg/yJNng= github.com/alitto/pond v1.8.2/go.mod h1:CmvIIGd5jKLasGI3D87qDkQxjzChdKMmnXMg3fG6M6Q= -github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.36.29 h1:lM1G3AF1+7vzFm0n7hfH8r2+750BTo+6Lo6FtPB7kzk= github.com/aws/aws-sdk-go v1.36.29/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= @@ -342,15 +89,11 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -358,39 +101,26 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chasex/glog v0.0.0-20160217080310-c62392af379c h1:eXqCBUHfmjbeDqcuvzjsd+bM6A+bnwo5N9FVbV6m5/s= github.com/chasex/glog v0.0.0-20160217080310-c62392af379c/go.mod h1:omJZNg0Qu76bxJd+ExohVo8uXzNcGOk2bv7vel460xk= -github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b h1:ACGZRIr7HsgBKHsueQ1yM4WaVaXh21ynwqsF8M8tXhA= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coocood/freecache v1.2.1 h1:/v1CqMq45NFH9mp/Pt142reundeBM0dVUD3osQBeu/U= github.com/coocood/freecache v1.2.1/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -406,57 +136,38 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= -github.com/envoyproxy/go-control-plane v0.10.3 h1:xdCVXxEe0Y3FQith+0cj2irwZudqGYvecuLB1HtdexY= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-ldap/ldap v3.0.2+incompatible h1:kD5HQcAzlQ7yrhfn+h+MSABeAy/jAJhvIJ/QDllP44g= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31 h1:28FVBuwkwowZMjbA7M0wXsI6t3PYulRTMio3SO+eKCM= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= @@ -464,7 +175,6 @@ github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0L github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -495,10 +205,8 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -514,14 +222,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -537,145 +241,92 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= github.com/google/tink/go v1.6.1/go.mod h1:IGW53kTgag+st5yPhKKwJ6u2l+SSp5/v9XF7spovjlY= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= -github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= -github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cRWgnE= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.3.0 h1:8+567mCcFDnS5ADl7lrpxPMWiFCElyUEeW0gtj34fMA= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hashicorp/serf v0.9.7 h1:hkdgbqizGQHuU5IPqYM1JdSMV8nKfpuOnZYXssk9muY= -github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hashicorp/vault/api v1.0.4 h1:j08Or/wryXT4AcHj1oCbMd7IijXcKzYUGw59LGu9onU= github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= -github.com/hashicorp/vault/sdk v0.1.13 h1:mOEPeOhT7jl0J4AMl1E705+BcmeRs1VmKNb9F0sMLy8= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lyft/protoc-gen-star v0.5.3 h1:zSGLzsUew8RT+ZKPHc3jnf8XLaVyHzTcAFBzHtCNR20= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= @@ -697,20 +348,15 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -721,19 +367,15 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -744,7 +386,6 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug= github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= @@ -753,19 +394,16 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prebid/go-gdpr v1.12.0 h1:OrjQ7Uc+lCRYaOirQ48jjG/PBMvZsKNAaRTgzxN6iZ0= github.com/prebid/go-gdpr v1.12.0/go.mod h1:mPZAdkRxn+iuSjaUuJAi9+0SppBOdM1PCzv/55UH3pY= @@ -799,34 +437,24 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/sagikazarmark/crypt v0.6.0 h1:REOEXCs/NFY/1jOCEouMuT4zEniE5YoXbvpC5X/TLF8= -github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= @@ -835,7 +463,6 @@ github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfA github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -862,7 +489,6 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/vrischmann/go-metrics-influxdb v0.1.1 h1:xneKFRjsS4BiVYvAKaM/rOlXYd1pGHksnES0ECCJLgo= github.com/vrischmann/go-metrics-influxdb v0.1.1/go.mod h1:q7YC8bFETCYopXRMtUvQQdLaoVhpsEwvQS2zZEYCqg8= @@ -877,38 +503,25 @@ github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FB github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc= -go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg= -go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= -go.etcd.io/etcd/client/v2 v2.305.4 h1:Dcx3/MYyfKcPNLpR4VVQUP5KgYrBeJtktBwEKkw08Ao= -go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU= -go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4= -go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -935,10 +548,10 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -951,10 +564,8 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= @@ -966,8 +577,6 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1035,8 +644,6 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1047,7 +654,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1131,12 +737,10 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1151,7 +755,6 @@ golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1209,14 +812,10 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1250,15 +849,12 @@ google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdr google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -google.golang.org/api v0.81.0 h1:o8WF5AvfidafWbFjsRyupxyEQJNUWxLZJCK5NXrxZZ8= -google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1361,7 +957,6 @@ google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5 google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1378,24 +973,19 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1418,11 +1008,7 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/modules/moduledeps/deps.go b/modules/moduledeps/deps.go index dc1e96ee0b0..5b917a9ab33 100644 --- a/modules/moduledeps/deps.go +++ b/modules/moduledeps/deps.go @@ -4,13 +4,15 @@ import ( "net/http" "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/currency" metricsCfg "github.com/prebid/prebid-server/metrics/config" ) // ModuleDeps provides dependencies that custom modules may need for hooks execution. // Additional dependencies can be added here if modules need something more. type ModuleDeps struct { - HTTPClient *http.Client - MetricsCfg *config.Metrics - MetricsRegistry metricsCfg.MetricsRegistry + HTTPClient *http.Client + MetricsCfg *config.Metrics + MetricsRegistry metricsCfg.MetricsRegistry + CurrencyConversion currency.Conversions } diff --git a/modules/pubmatic/openwrap/adunitconfig/common.go b/modules/pubmatic/openwrap/adunitconfig/common.go index 1354dc201cc..35a6d3ff674 100644 --- a/modules/pubmatic/openwrap/adunitconfig/common.go +++ b/modules/pubmatic/openwrap/adunitconfig/common.go @@ -4,13 +4,12 @@ import ( "encoding/json" "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/bidderparams" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" ) func selectSlot(rCtx models.RequestCtx, h, w int64, tagid, div, source string) (slotAdUnitConfig *adunitconfig.AdConfig, slotName string, isRegex bool, matchedRegex string) { - slotName = bidderparams.GenerateSlotName(h, w, rCtx.AdUnitConfig.ConfigPattern, tagid, div, rCtx.Source) + slotName = models.GenerateSlotName(h, w, rCtx.AdUnitConfig.ConfigPattern, tagid, div, rCtx.Source) if slotAdUnitConfig, ok := rCtx.AdUnitConfig.Config[strings.ToLower(slotName)]; ok { return slotAdUnitConfig, slotName, false, "" diff --git a/modules/pubmatic/openwrap/adunitconfig/utils.go b/modules/pubmatic/openwrap/adunitconfig/utils.go index 646e9b7dfde..fc1a81fb717 100644 --- a/modules/pubmatic/openwrap/adunitconfig/utils.go +++ b/modules/pubmatic/openwrap/adunitconfig/utils.go @@ -2,7 +2,6 @@ package adunitconfig import ( "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/bidderparams" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" "github.com/prebid/prebid-server/util/ptrutil" @@ -19,7 +18,7 @@ func GetMatchedSlotName(rCtx models.RequestCtx, imp openrtb2.Imp, impExt models. div = impExt.Wrapper.Div } - slotName := bidderparams.GenerateSlotName(height, width, rCtx.AdUnitConfig.ConfigPattern, tagID, div, rCtx.Source) + slotName := models.GenerateSlotName(height, width, rCtx.AdUnitConfig.ConfigPattern, tagID, div, rCtx.Source) var ok bool slotAdUnitConfig, ok = rCtx.AdUnitConfig.Config[slotName] diff --git a/modules/pubmatic/openwrap/auctionresponsehook.go b/modules/pubmatic/openwrap/auctionresponsehook.go index 35e8db508e2..09abd3bd8a8 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook.go +++ b/modules/pubmatic/openwrap/auctionresponsehook.go @@ -7,6 +7,7 @@ import ( "time" "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v19/openrtb3" "github.com/prebid/prebid-server/hooks/hookanalytics" "github.com/prebid/prebid-server/hooks/hookstage" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adunitconfig" @@ -48,8 +49,6 @@ func (m OpenWrap) handleAuctionResponseHook( m.metricEngine.RecordPublisherResponseTimeStats(rctx.PubIDStr, int(time.Since(time.Unix(rctx.StartTime, 0)).Milliseconds())) }() - RecordPublisherPartnerNoCookieStats(rctx) - // cache rctx for analytics result.AnalyticsTags = hookanalytics.Analytics{ Activities: []hookanalytics.Activity{ @@ -70,6 +69,7 @@ func (m OpenWrap) handleAuctionResponseHook( // return result, nil // } + anyDealTierSatisfyingBid := false winningBids := make(map[string]models.OwBid, 0) for _, seatBid := range payload.BidResponse.SeatBid { for _, bid := range seatBid.Bid { @@ -115,7 +115,7 @@ func (m OpenWrap) handleAuctionResponseHook( bidExt.CreativeType = string(bidExt.Prebid.Type) } if bidExt.CreativeType == "" { - bidExt.CreativeType = models.GetAdFormat(bid.AdM) + bidExt.CreativeType = models.GetCreativeType(&bid, bidExt, &impCtx) } // set response netecpm and logger/tracker en @@ -166,6 +166,9 @@ func (m OpenWrap) handleAuctionResponseHook( bidDealTierSatisfied := false if bidExt.Prebid != nil { bidDealTierSatisfied = bidExt.Prebid.DealTierSatisfied + if bidDealTierSatisfied { + anyDealTierSatisfyingBid = true // found at least one bid which satisfies dealTier + } } owbid := models.OwBid{ @@ -173,11 +176,23 @@ func (m OpenWrap) handleAuctionResponseHook( NetEcpm: bidExt.NetECPM, BidDealTierSatisfied: bidDealTierSatisfied, } - wbid, ok := winningBids[bid.ImpID] - if !ok || isNewWinningBid(owbid, wbid, rctx.SupportDeals) { + wbid, oldWinBidFound := winningBids[bid.ImpID] + if !oldWinBidFound || isNewWinningBid(&owbid, &wbid, rctx.SupportDeals) { winningBids[bid.ImpID] = owbid } + // update NonBr codes for current bid + if owbid.Nbr != nil { + bidExt.Nbr = owbid.Nbr + } + + // if current bid is winner then update NonBr code for earlier winning bid + if winningBids[bid.ImpID].ID == owbid.ID && oldWinBidFound { + winBidCtx := rctx.ImpBidCtx[bid.ImpID].BidCtx[wbid.ID] + winBidCtx.BidExt.Nbr = wbid.Nbr + rctx.ImpBidCtx[bid.ImpID].BidCtx[wbid.ID] = winBidCtx + } + // cache for bid details for logger and tracker if impCtx.BidCtx == nil { impCtx.BidCtx = make(map[string]models.BidCtx) @@ -196,6 +211,23 @@ func (m OpenWrap) handleAuctionResponseHook( m.metricEngine.RecordNobidErrPrebidServerResponse(rctx.PubIDStr) } + /* + At this point of time, + 1. For price-based auction (request with supportDeals = false), + all rejected bids will have NonBR code as LossLostToHigherBid which is expected. + 2. For request with supportDeals = true : + 2.1) If all bids are non-deal-bids (bidExt.Prebid.DealTierSatisfied = false) + then NonBR code for them will be LossLostToHigherBid which is expected. + 2.2) If one of the bid is deal-bid (bidExt.Prebid.DealTierSatisfied = true) + expectation: + all rejected non-deal bids should have NonBR code as LossLostToDealBid + all rejected deal-bids should have NonBR code as LossLostToHigherBid + addLostToDealBidNonBRCode function will make sure that above expectation are met. + */ + if anyDealTierSatisfyingBid { + addLostToDealBidNonBRCode(&rctx) + } + droppedBids, warnings := addPWTTargetingForBid(rctx, payload.BidResponse) if len(droppedBids) != 0 { rctx.DroppedBids = droppedBids @@ -212,7 +244,8 @@ func (m OpenWrap) handleAuctionResponseHook( } } - rctx.DefaultBids = m.addDefaultBids(rctx, payload.BidResponse, &responseExt) + rctx.ResponseExt = responseExt + rctx.DefaultBids = m.addDefaultBids(&rctx, payload.BidResponse, responseExt) rctx.Trackers = tracker.CreateTrackers(rctx, payload.BidResponse) @@ -222,7 +255,9 @@ func (m OpenWrap) handleAuctionResponseHook( } // TODO: PBS-Core should pass the hostcookie for module to usersync.ParseCookieFromRequest() - if matchedImpression := getMatchedImpression(rctx); matchedImpression != nil { + rctx.MatchedImpression = getMatchedImpression(rctx) + matchedImpression, err := json.Marshal(rctx.MatchedImpression) + if err == nil { responseExt.OwMatchedImpression = matchedImpression } @@ -233,22 +268,16 @@ func (m OpenWrap) handleAuctionResponseHook( if rctx.LogInfoFlag == 1 { responseExt.OwLogInfo = &openrtb_ext.OwLogInfo{ // Logger: openwrap.GetLogAuctionObjectAsURL(ao, true, true), updated done later - Tracker: tracker.GetTrackerInfo(rctx), + Tracker: tracker.GetTrackerInfo(rctx, responseExt), } } + // add seat-non-bids in the bidresponse only request.ext.prebid.returnallbidstatus is true if rctx.ReturnAllBidStatus { - // prepare seat-non-bids and add them in the response-ext rctx.SeatNonBids = prepareSeatNonBids(rctx) addSeatNonBidsInResponseExt(rctx, &responseExt) } - var err error - rctx.ResponseExt, err = json.Marshal(responseExt) - if err != nil { - result.Errors = append(result.Errors, "failed to marshal response.ext err: "+err.Error()) - } - if rctx.Debug { rCtxBytes, _ := json.Marshal(rctx) result.DebugMessages = append(result.DebugMessages, string(rCtxBytes)) @@ -267,8 +296,13 @@ func (m OpenWrap) handleAuctionResponseHook( return ap, err } + var responseExtjson json.RawMessage + responseExtjson, err = json.Marshal(responseExt) + if err != nil { + result.Errors = append(result.Errors, "failed to marshal response.ext err: "+err.Error()) + } ap.BidResponse, err = m.applyDefaultBids(rctx, ap.BidResponse) - ap.BidResponse.Ext = rctx.ResponseExt + ap.BidResponse.Ext = responseExtjson resetBidIdtoOriginal(ap.BidResponse) return ap, err @@ -342,19 +376,26 @@ func (m *OpenWrap) updateORTBV25Response(rctx models.RequestCtx, bidResponse *op } // isNewWinningBid calculates if the new bid (nbid) will win against the current winning bid (wbid) given preferDeals. -func isNewWinningBid(bid, wbid models.OwBid, preferDeals bool) bool { +func isNewWinningBid(bid, wbid *models.OwBid, preferDeals bool) bool { if preferDeals { //only wbid has deal if wbid.BidDealTierSatisfied && !bid.BidDealTierSatisfied { + bid.Nbr = GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid) return false } //only bid has deal if !wbid.BidDealTierSatisfied && bid.BidDealTierSatisfied { + wbid.Nbr = GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid) return true } } //both have deal or both do not have deal - return bid.NetEcpm > wbid.NetEcpm + if bid.NetEcpm > wbid.NetEcpm { + wbid.Nbr = GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid) + return true + } + bid.Nbr = GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid) + return false } func getPlatformName(platform string) string { diff --git a/modules/pubmatic/openwrap/auctionresponsehook_test.go b/modules/pubmatic/openwrap/auctionresponsehook_test.go index a5ec0cf9bae..2f98e480edb 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook_test.go +++ b/modules/pubmatic/openwrap/auctionresponsehook_test.go @@ -8,9 +8,11 @@ import ( "github.com/golang/mock/gomock" "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v19/openrtb3" "github.com/prebid/prebid-server/hooks/hookstage" 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/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -125,6 +127,945 @@ func TestSeatNonBidsInHandleAuctionResponseHook(t *testing.T) { } } +func TestNonBRCodesInHandleAuctionResponseHook(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + type args struct { + ctx context.Context + moduleCtx hookstage.ModuleInvocationContext + payload hookstage.AuctionResponsePayload + } + type want struct { + impBidCtx map[string]models.ImpCtx + } + tests := []struct { + name string + args args + want want + getMetricsEngine func() *mock_metrics.MockMetricsEngine + }{ + { + name: "single bid and supportdeal is false", + args: args{ + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + StartTime: time.Now().UnixMilli(), + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + }, + PubIDStr: "5890", + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 5, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + }, + getMetricsEngine: func() (me *mock_metrics.MockMetricsEngine) { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "pubmatic") + return mockEngine + }, + want: want{ + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 5, + EN: 5, + }, + }, + }, + }, + }, + }, + { + name: "test auction between 3 bids when supportdeal is false and no bid satisfies dealTier", + args: args{ + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + StartTime: time.Now().UnixMilli(), + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + }, + PubIDStr: "5890", + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 5, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "pubmatic", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-2", + ImpID: "imp1", + Price: 20, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "appnexus", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-3", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "rubicon", + }, + }, + }, + }, + }, + getMetricsEngine: func() (me *mock_metrics.MockMetricsEngine) { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "pubmatic") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "appnexus") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "rubicon") + return mockEngine + }, + want: want{ + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 5, + EN: 5, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 20, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 20, + EN: 20, + }, + "bid-id-3": { + BidExt: models.BidExt{ + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 10, + EN: 10, + }, + }, + }, + }, + }, + }, + { + name: "test auction between 3 bids when supportdeal is false and all bids satisfies dealTier", + args: args{ + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + StartTime: time.Now().UnixMilli(), + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + }, + PubIDStr: "5890", + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 5, + Ext: json.RawMessage(`{"prebid":{"dealtiersatisfied":true}}`), + }, + }, + Seat: "pubmatic", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-2", + ImpID: "imp1", + Price: 20, + Ext: json.RawMessage(`{"prebid":{"dealtiersatisfied":true}}`), + }, + }, + Seat: "appnexus", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-3", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{"prebid":{"dealtiersatisfied":true}}`), + }, + }, + Seat: "rubicon", + }, + }, + }, + }, + }, + getMetricsEngine: func() (me *mock_metrics.MockMetricsEngine) { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "pubmatic") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "appnexus") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "rubicon") + return mockEngine + }, + want: want{ + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + Targeting: map[string]string{}, + }, + }, + }, + EG: 5, + EN: 5, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 20, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + Targeting: map[string]string{}, + }, + }, + }, + EG: 20, + EN: 20, + }, + "bid-id-3": { + BidExt: models.BidExt{ + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + Targeting: map[string]string{}, + }, + }, + }, + EG: 10, + EN: 10, + }, + }, + }, + }, + }, + }, + { + name: "single bid and supportdeal is true", + args: args{ + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + StartTime: time.Now().UnixMilli(), + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + }, + PubIDStr: "5890", + SupportDeals: true, + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 5, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "pubmatic", + }, + }, + }, + }, + }, + getMetricsEngine: func() (me *mock_metrics.MockMetricsEngine) { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "pubmatic") + return mockEngine + }, + want: want{ + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 5, + EN: 5, + }, + }, + }, + }, + }, + }, + { + name: "auction between 3 bids when supportdeal is true and no bid satisfies dealTier", + args: args{ + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + StartTime: time.Now().UnixMilli(), + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + }, + PubIDStr: "5890", + SupportDeals: true, + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 20, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "pubmatic", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-2", + ImpID: "imp1", + Price: 5, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "appnexus", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-3", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "rubicon", + }, + }, + }, + }, + }, + getMetricsEngine: func() (me *mock_metrics.MockMetricsEngine) { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "pubmatic") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "appnexus") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "rubicon") + return mockEngine + }, + want: want{ + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + NetECPM: 20, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 20, + EN: 20, + }, + "bid-id-2": { + BidExt: models.BidExt{ + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 5, + EN: 5, + }, + "bid-id-3": { + BidExt: models.BidExt{ + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 10, + EN: 10, + }, + }, + }, + }, + }, + }, + { + name: "auction between 3 bids when supportdeal is true and only middle bid satisfies dealTier", + args: args{ + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + StartTime: time.Now().UnixMilli(), + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + }, + PubIDStr: "5890", + SupportDeals: true, + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 20, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "pubmatic", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-2", + ImpID: "imp1", + Price: 5, + Ext: json.RawMessage(`{"prebid":{"dealtiersatisfied":true}}`), + }, + }, + Seat: "appnexus", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-3", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "rubicon", + }, + }, + }, + }, + }, + getMetricsEngine: func() (me *mock_metrics.MockMetricsEngine) { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "pubmatic") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "appnexus") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "rubicon") + return mockEngine + }, + want: want{ + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + NetECPM: 20, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 20, + EN: 20, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + Targeting: map[string]string{}, + }, + }, + }, + EG: 5, + EN: 5, + }, + "bid-id-3": { + BidExt: models.BidExt{ + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 10, + EN: 10, + }, + }, + }, + }, + }, + }, + { + name: "auction between 3 bids when supportdeal is true and only last bid satisfies dealTier", + args: args{ + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + StartTime: time.Now().UnixMilli(), + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + }, + PubIDStr: "5890", + SupportDeals: true, + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 20, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "pubmatic", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-2", + ImpID: "imp1", + Price: 5, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "appnexus", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-3", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{"prebid":{"dealtiersatisfied":true}}`), + }, + }, + Seat: "rubicon", + }, + }, + }, + }, + }, + getMetricsEngine: func() (me *mock_metrics.MockMetricsEngine) { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "pubmatic") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "appnexus") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "rubicon") + return mockEngine + }, + want: want{ + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + NetECPM: 20, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 20, + EN: 20, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 5, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 5, + EN: 5, + }, + "bid-id-3": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + Targeting: map[string]string{}, + }, + }, + NetECPM: 10, + }, + EG: 10, + EN: 10, + }, + }, + }, + }, + }, + }, + { + name: "auction between 3 bids when supportdeal is true and only first bid satisfies dealTier", + args: args{ + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + StartTime: time.Now().UnixMilli(), + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + }, + PubIDStr: "5890", + SupportDeals: true, + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 20, + Ext: json.RawMessage(`{"prebid":{"dealtiersatisfied":true}}`), + }, + }, + Seat: "pubmatic", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-2", + ImpID: "imp1", + Price: 5, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "appnexus", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-3", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{}`), + }, + }, + Seat: "rubicon", + }, + }, + }, + }, + }, + getMetricsEngine: func() (me *mock_metrics.MockMetricsEngine) { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "pubmatic") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "appnexus") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "rubicon") + return mockEngine + }, + want: want{ + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + Targeting: map[string]string{}, + }, + }, + NetECPM: 20, + }, + EG: 20, + EN: 20, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 5, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 5, + EN: 5, + }, + "bid-id-3": { + BidExt: models.BidExt{ + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Targeting: map[string]string{}, + }, + }, + }, + EG: 10, + EN: 10, + }, + }, + }, + }, + }, + }, + { + name: "auction between 3 bids when supportdeal is true and all bids satisfies dealTier", + args: args{ + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + StartTime: time.Now().UnixMilli(), + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": {}, + }, + PubIDStr: "5890", + SupportDeals: true, + }, + }, + }, + payload: hookstage.AuctionResponsePayload{ + BidResponse: &openrtb2.BidResponse{ + Cur: "USD", + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-1", + ImpID: "imp1", + Price: 20, + Ext: json.RawMessage(`{"prebid":{"dealtiersatisfied":true}}`), + }, + }, + Seat: "pubmatic", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-2", + ImpID: "imp1", + Price: 5, + Ext: json.RawMessage(`{"prebid":{"dealtiersatisfied":true}}`), + }, + }, + Seat: "appnexus", + }, + { + Bid: []openrtb2.Bid{ + { + ID: "bid-id-3", + ImpID: "imp1", + Price: 10, + Ext: json.RawMessage(`{"prebid":{"dealtiersatisfied":true}}`), + }, + }, + Seat: "rubicon", + }, + }, + }, + }, + }, + getMetricsEngine: func() (me *mock_metrics.MockMetricsEngine) { + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockEngine.EXPECT().RecordPublisherResponseTimeStats("5890", gomock.Any()) + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "pubmatic") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "appnexus") + mockEngine.EXPECT().RecordPlatformPublisherPartnerResponseStats("", "5890", "rubicon") + return mockEngine + }, + want: want{ + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + Targeting: map[string]string{}, + }, + }, + NetECPM: 20, + }, + EG: 20, + EN: 20, + }, + "bid-id-2": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + Targeting: map[string]string{}, + }, + }, + NetECPM: 5, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + }, + EG: 5, + EN: 5, + }, + "bid-id-3": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + Targeting: map[string]string{}, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + NetECPM: 10, + }, + EG: 10, + EN: 10, + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := OpenWrap{ + metricEngine: tt.getMetricsEngine(), + } + hookResult, _ := o.handleAuctionResponseHook(tt.args.ctx, tt.args.moduleCtx, tt.args.payload) + mutations := hookResult.ChangeSet.Mutations() + assert.NotEmpty(t, mutations, tt.name) + rctxInterface := hookResult.AnalyticsTags.Activities[0].Results[0].Values["request-ctx"] + rctx := rctxInterface.(*models.RequestCtx) + assert.Equal(t, tt.want.impBidCtx, rctx.ImpBidCtx, tt.name) + }) + } +} + func TestResetBidIdtoOriginal(t *testing.T) { type args struct { bidResponse *openrtb2.BidResponse diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go index 3256cda3a29..3705c424683 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook.go +++ b/modules/pubmatic/openwrap/beforevalidationhook.go @@ -67,6 +67,10 @@ func (m OpenWrap) handleBeforeValidationHook( } rCtx.PubID = pubID rCtx.PubIDStr = strconv.Itoa(pubID) + rCtx.Source, rCtx.Origin = getSourceAndOrigin(payload.BidRequest) + rCtx.PageURL = getPageURL(payload.BidRequest) + rCtx.Platform = getPlatformFromRequest(payload.BidRequest) + rCtx.DevicePlatform = GetDevicePlatform(rCtx, payload.BidRequest) if rCtx.UidCookie == nil { m.metricEngine.RecordUidsCookieNotPresentErrorStats(rCtx.PubIDStr, rCtx.ProfileIDStr) @@ -80,6 +84,7 @@ func (m OpenWrap) handleBeforeValidationHook( result.Errors = append(result.Errors, err.Error()) return result, err } + rCtx.NewReqExt = requestExt rCtx.ReturnAllBidStatus = requestExt.Prebid.ReturnAllBidStatus // TODO: verify preference of request.test vs queryParam test ++ this check is only for the CTV requests @@ -103,8 +108,11 @@ func (m OpenWrap) handleBeforeValidationHook( } rCtx.PartnerConfigMap = partnerConfigMap // keep a copy at module level as well - rCtx.Platform = rCtx.GetVersionLevelKey(models.PLATFORM_KEY) - if rCtx.Platform == "" { + if ver, err := strconv.Atoi(models.GetVersionLevelPropertyFromPartnerConfig(partnerConfigMap, models.DisplayVersionID)); err == nil { + rCtx.DisplayVersionID = ver + } + platform := rCtx.GetVersionLevelKey(models.PLATFORM_KEY) + if platform == "" { result.NbrCode = nbr.InvalidPlatform err = errors.New("failed to get platform data") result.Errors = append(result.Errors, err.Error()) @@ -112,12 +120,10 @@ func (m OpenWrap) handleBeforeValidationHook( m.metricEngine.RecordPublisherInvalidProfileImpressions(rCtx.PubIDStr, rCtx.ProfileIDStr, len(payload.BidRequest.Imp)) return result, err } - - rCtx.PageURL = getPageURL(payload.BidRequest) + rCtx.Platform = platform rCtx.DevicePlatform = GetDevicePlatform(rCtx, payload.BidRequest) rCtx.SendAllBids = isSendAllBids(rCtx) - rCtx.Source, rCtx.Origin = getSourceAndOrigin(payload.BidRequest) - rCtx.TMax = m.setTimeout(rCtx) + rCtx.TMax = m.setTimeout(rCtx, payload.BidRequest) m.metricEngine.RecordPublisherRequests(rCtx.Endpoint, rCtx.PubIDStr, rCtx.Platform) @@ -215,7 +221,19 @@ func (m OpenWrap) handleBeforeValidationHook( div = impExt.Wrapper.Div } + // reuse the existing impExt instead of allocating a new one + reward := impExt.Reward + if reward != nil { + impExt.Prebid.IsRewardedInventory = reward + } + // if imp.ext.data.pbadslot is absent then set it to tagId + if len(impExt.Data.PbAdslot) == 0 { + impExt.Data.PbAdslot = imp.TagID + } + incomingSlots := getIncomingSlots(imp) + slotName := getSlotName(imp.TagID, impExt) + adUnitName := getAdunitName(imp.TagID, impExt) var videoAdUnitCtx, bannerAdUnitCtx models.AdUnitCtx if rCtx.AdUnitConfig != nil { @@ -236,7 +254,10 @@ func (m OpenWrap) handleBeforeValidationHook( disabledSlots++ rCtx.ImpBidCtx[imp.ID] = models.ImpCtx{ // for wrapper logger sz - IncomingSlots: incomingSlots, + IncomingSlots: incomingSlots, + AdUnitName: adUnitName, + SlotName: slotName, + IsRewardInventory: reward, } continue } @@ -286,7 +307,10 @@ func (m OpenWrap) handleBeforeValidationHook( result.Errors = append(result.Errors, fmt.Sprintf("no bidder params found for imp:%s partner: %s", imp.ID, prebidBidderCode)) nonMapped[bidderCode] = struct{}{} m.metricEngine.RecordPartnerConfigErrors(rCtx.PubIDStr, rCtx.ProfileIDStr, bidderCode, models.PartnerErrSlotNotMapped) - continue + + if prebidBidderCode != string(openrtb_ext.BidderPubmatic) && prebidBidderCode != string(models.BidderPubMaticSecondaryAlias) { + continue + } } m.metricEngine.RecordPlatformPublisherPartnerReqStats(rCtx.Platform, rCtx.PubIDStr, bidderCode) @@ -329,13 +353,6 @@ func (m OpenWrap) handleBeforeValidationHook( impExt.Prebid.Bidder[bidder] = meta.Params } - // reuse the existing impExt instead of allocating a new one - reward := impExt.Reward - - if reward != nil { - impExt.Prebid.IsRewardedInventory = reward - } - impExt.Wrapper = nil impExt.Reward = nil impExt.Bidder = nil @@ -351,6 +368,8 @@ func (m OpenWrap) handleBeforeValidationHook( TagID: imp.TagID, Div: div, IsRewardInventory: reward, + BidFloor: imp.BidFloor, + BidFloorCur: imp.BidFloorCur, Type: slotType, Banner: imp.Banner != nil, Video: imp.Video, @@ -360,6 +379,8 @@ func (m OpenWrap) handleBeforeValidationHook( BidCtx: make(map[string]models.BidCtx), NewExt: json.RawMessage(newImpExt), IsAdPodRequest: isAdPodRequest, + SlotName: slotName, + AdUnitName: adUnitName, } } @@ -425,21 +446,21 @@ func (m OpenWrap) handleBeforeValidationHook( // similar to impExt, reuse the existing requestExt to avoid additional memory requests requestExt.Wrapper = nil requestExt.Bidder = nil - rCtx.NewReqExt, err = json.Marshal(requestExt) - if err != nil { - result.Errors = append(result.Errors, "failed to update request.ext "+err.Error()) - } if rCtx.Debug { newImp, _ := json.Marshal(rCtx.ImpBidCtx) result.DebugMessages = append(result.DebugMessages, "new imp: "+string(newImp)) - result.DebugMessages = append(result.DebugMessages, "new request.ext: "+string(rCtx.NewReqExt)) + newReqExt, _ := json.Marshal(rCtx.NewReqExt) + result.DebugMessages = append(result.DebugMessages, "new request.ext: "+string(newReqExt)) } result.ChangeSet.AddMutation(func(ep hookstage.BeforeValidationRequestPayload) (hookstage.BeforeValidationRequestPayload, error) { rctx := moduleCtx.ModuleContext["rctx"].(models.RequestCtx) var err error ep.BidRequest, err = m.applyProfileChanges(rctx, ep.BidRequest) + if err != nil { + result.Errors = append(result.Errors, "failed to apply profile changes: "+err.Error()) + } return ep, err }, hookstage.MutationUpdate, "request-body-with-profile-data") @@ -508,8 +529,13 @@ func (m *OpenWrap) applyProfileChanges(rctx models.RequestCtx, bidRequest *openr bidRequest.App.Content.Language = getValidLanguage(bidRequest.App.Content.Language) } - bidRequest.Ext = rctx.NewReqExt - return bidRequest, nil + var err error + var requestExtjson json.RawMessage + if rctx.NewReqExt != nil { + requestExtjson, err = json.Marshal(rctx.NewReqExt) + bidRequest.Ext = requestExtjson + } + return bidRequest, err } func (m *OpenWrap) applyVideoAdUnitConfig(rCtx models.RequestCtx, imp *openrtb2.Imp) { @@ -522,13 +548,17 @@ func (m *OpenWrap) applyVideoAdUnitConfig(rCtx models.RequestCtx, imp *openrtb2. return } + impBidCtx := rCtx.ImpBidCtx[imp.ID] if imp.BidFloor == 0 && adUnitCfg.BidFloor != nil { imp.BidFloor = *adUnitCfg.BidFloor + impBidCtx.BidFloor = imp.BidFloor } if len(imp.BidFloorCur) == 0 && adUnitCfg.BidFloorCur != nil { imp.BidFloorCur = *adUnitCfg.BidFloorCur + impBidCtx.BidFloorCur = imp.BidFloorCur } + rCtx.ImpBidCtx[imp.ID] = impBidCtx if adUnitCfg.Exp != nil { imp.Exp = int64(*adUnitCfg.Exp) @@ -669,13 +699,17 @@ func (m *OpenWrap) applyBannerAdUnitConfig(rCtx models.RequestCtx, imp *openrtb2 return } + impBidCtx := rCtx.ImpBidCtx[imp.ID] if imp.BidFloor == 0 && adUnitCfg.BidFloor != nil { imp.BidFloor = *adUnitCfg.BidFloor + impBidCtx.BidFloor = imp.BidFloor } if len(imp.BidFloorCur) == 0 && adUnitCfg.BidFloorCur != nil { imp.BidFloorCur = *adUnitCfg.BidFloorCur + impBidCtx.BidFloorCur = imp.BidFloorCur } + rCtx.ImpBidCtx[imp.ID] = impBidCtx if adUnitCfg.Exp != nil { imp.Exp = int64(*adUnitCfg.Exp) @@ -691,6 +725,57 @@ func (m *OpenWrap) applyBannerAdUnitConfig(rCtx models.RequestCtx, imp *openrtb2 } } +/* +getSlotName will return slot name according to below priority + 1. imp.ext.gpid + 2. imp.tagid + 3. imp.ext.data.pbadslot + 4. imp.ext.prebid.storedrequest.id +*/ +func getSlotName(tagId string, impExt *models.ImpExtension) string { + if impExt == nil { + return tagId + } + + if len(impExt.GpId) > 0 { + return impExt.GpId + } + + if len(tagId) > 0 { + return tagId + } + + if len(impExt.Data.PbAdslot) > 0 { + return impExt.Data.PbAdslot + } + + var storeReqId string + if impExt.Prebid.StoredRequest != nil { + storeReqId = impExt.Prebid.StoredRequest.ID + } + + return storeReqId +} + +/* +getAdunitName will return adunit name according to below priority + 1. imp.ext.data.adserver.adslot if imp.ext.data.adserver.name == "gam" + 2. imp.ext.data.pbadslot + 3. imp.tagid +*/ +func getAdunitName(tagId string, impExt *models.ImpExtension) string { + if impExt == nil { + return tagId + } + if impExt.Data.AdServer != nil && impExt.Data.AdServer.Name == models.GamAdServer && impExt.Data.AdServer.AdSlot != "" { + return impExt.Data.AdServer.AdSlot + } + if len(impExt.Data.PbAdslot) > 0 { + return impExt.Data.PbAdslot + } + return tagId +} + func getDomainFromUrl(pageUrl string) string { u, err := url.Parse(pageUrl) if err != nil { @@ -768,15 +853,20 @@ func updateAliasGVLIds(aliasgvlids map[string]uint16, bidderCode string, partner } // setTimeout - This utility returns timeout applicable for a profile -func (m OpenWrap) setTimeout(rCtx models.RequestCtx) int64 { +func (m OpenWrap) setTimeout(rCtx models.RequestCtx, req *openrtb2.BidRequest) int64 { var auctionTimeout int64 - //check for ssTimeout in the partner config - ssTimeout := models.GetVersionLevelPropertyFromPartnerConfig(rCtx.PartnerConfigMap, models.SSTimeoutKey) - if ssTimeout != "" { - ssTimeoutDB, err := strconv.Atoi(ssTimeout) - if err == nil { - auctionTimeout = int64(ssTimeoutDB) + // BidRequest.TMax has highest priority + if req.TMax != 0 { + auctionTimeout = req.TMax + } else { + //check for ssTimeout in the partner config + ssTimeout := models.GetVersionLevelPropertyFromPartnerConfig(rCtx.PartnerConfigMap, models.SSTimeoutKey) + if ssTimeout != "" { + ssTimeoutDB, err := strconv.Atoi(ssTimeout) + if err == nil { + auctionTimeout = int64(ssTimeoutDB) + } } } @@ -861,11 +951,11 @@ func getPubID(bidRequest openrtb2.BidRequest) (pubID int, err error) { func getTagID(imp openrtb2.Imp, impExt *models.ImpExtension) string { //priority for tagId is imp.ext.gpid > imp.TagID > imp.ext.data.pbadslot - if impExt.Gpid != "" { - if idx := strings.Index(impExt.Gpid, "#"); idx != -1 { - return impExt.Gpid[:idx] + if impExt.GpId != "" { + if idx := strings.Index(impExt.GpId, "#"); idx != -1 { + return impExt.GpId[:idx] } - return impExt.Gpid + return impExt.GpId } else if imp.TagID != "" { return imp.TagID } diff --git a/modules/pubmatic/openwrap/beforevalidationhook_test.go b/modules/pubmatic/openwrap/beforevalidationhook_test.go index e6922046ce0..0dd6f688739 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook_test.go +++ b/modules/pubmatic/openwrap/beforevalidationhook_test.go @@ -299,7 +299,8 @@ func TestOpenWrap_setTimeout(t *testing.T) { metricEngine metrics.MetricsEngine } type args struct { - rCtx models.RequestCtx + rCtx models.RequestCtx + bidRequest *openrtb2.BidRequest } tests := []struct { name string @@ -307,6 +308,54 @@ func TestOpenWrap_setTimeout(t *testing.T) { args args want int64 }{ + { + name: "Highest_priority_to_request_tmax_parameter", + args: args{ + rCtx: models.RequestCtx{ + PartnerConfigMap: map[int]map[string]string{ + -1: { + "ssTimeout": "250", + }, + }, + }, + bidRequest: &openrtb2.BidRequest{ + TMax: 220, + }, + }, + fields: fields{ + cfg: config.Config{ + Timeout: config.Timeout{ + MinTimeout: 200, + MaxTimeout: 300, + }, + }, + }, + want: 220, + }, + { + name: "tmax_parameter_less_than_minTimeout", + args: args{ + rCtx: models.RequestCtx{ + PartnerConfigMap: map[int]map[string]string{ + -1: { + "ssTimeout": "250", + }, + }, + }, + bidRequest: &openrtb2.BidRequest{ + TMax: 10, + }, + }, + fields: fields{ + cfg: config.Config{ + Timeout: config.Timeout{ + MinTimeout: 200, + MaxTimeout: 300, + }, + }, + }, + want: 200, + }, { name: "ssTimeout_greater_than_minTimeout_and_less_than_maxTimeout", args: args{ @@ -317,6 +366,7 @@ func TestOpenWrap_setTimeout(t *testing.T) { }, }, }, + bidRequest: &openrtb2.BidRequest{}, }, fields: fields{ cfg: config.Config{ @@ -338,6 +388,7 @@ func TestOpenWrap_setTimeout(t *testing.T) { }, }, }, + bidRequest: &openrtb2.BidRequest{}, }, fields: fields{ cfg: config.Config{ @@ -359,6 +410,7 @@ func TestOpenWrap_setTimeout(t *testing.T) { }, }, }, + bidRequest: &openrtb2.BidRequest{}, }, fields: fields{ cfg: config.Config{ @@ -380,6 +432,7 @@ func TestOpenWrap_setTimeout(t *testing.T) { }, }, }, + bidRequest: &openrtb2.BidRequest{}, }, fields: fields{ cfg: config.Config{ @@ -406,6 +459,7 @@ func TestOpenWrap_setTimeout(t *testing.T) { }, }, }, + bidRequest: &openrtb2.BidRequest{}, }, fields: fields{ cfg: config.Config{ @@ -433,6 +487,7 @@ func TestOpenWrap_setTimeout(t *testing.T) { }, }, }, + bidRequest: &openrtb2.BidRequest{}, }, fields: fields{ cfg: config.Config{ @@ -460,6 +515,7 @@ func TestOpenWrap_setTimeout(t *testing.T) { }, }, }, + bidRequest: &openrtb2.BidRequest{}, }, fields: fields{ cfg: config.Config{ @@ -479,7 +535,7 @@ func TestOpenWrap_setTimeout(t *testing.T) { cache: tt.fields.cache, metricEngine: tt.fields.metricEngine, } - got := m.setTimeout(tt.args.rCtx) + got := m.setTimeout(tt.args.rCtx, tt.args.bidRequest) assert.Equal(t, tt.want, got) }) } @@ -959,11 +1015,15 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { rCtx models.RequestCtx imp *openrtb2.Imp } + type want struct { + rCtx models.RequestCtx + imp *openrtb2.Imp + } tests := []struct { name string fields fields args args - want *openrtb2.Imp + want want }{ { name: "imp.video_is_nil", @@ -972,8 +1032,10 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { Video: nil, }, }, - want: &openrtb2.Imp{ - Video: nil, + want: want{ + imp: &openrtb2.Imp{ + Video: nil, + }, }, }, { @@ -993,9 +1055,20 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { Video: &openrtb2.Video{}, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Video: &openrtb2.Video{}, + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Video: &openrtb2.Video{}, + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + VideoAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: nil, + }, + }, + }, + }, }, }, { @@ -1020,11 +1093,27 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { Video: &openrtb2.Video{}, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Video: &openrtb2.Video{}, - BidFloor: 2.0, - BidFloorCur: "USD", + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Video: &openrtb2.Video{}, + BidFloor: 2.0, + BidFloorCur: "USD", + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + VideoAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: &adunitconfig.AdConfig{ + BidFloor: ptrutil.ToPtr(2.0), + BidFloorCur: ptrutil.ToPtr("USD"), + }, + }, + BidFloor: 2, + BidFloorCur: "USD", + }, + }, + }, }, }, { @@ -1046,10 +1135,23 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { Video: &openrtb2.Video{}, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Video: &openrtb2.Video{}, - Exp: 10, + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Video: &openrtb2.Video{}, + Exp: 10, + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + VideoAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: &adunitconfig.AdConfig{ + Exp: ptrutil.ToPtr(10), + }, + }, + }, + }, + }, }, }, { @@ -1074,11 +1176,24 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { }, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Video: &openrtb2.Video{ - W: 200, - H: 300, + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Video: &openrtb2.Video{ + W: 200, + H: 300, + }, + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + VideoAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: &adunitconfig.AdConfig{ + Video: nil, + }, + }, + }, + }, }, }, }, @@ -1106,9 +1221,24 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { }, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Video: nil, + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Video: nil, + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + VideoAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: &adunitconfig.AdConfig{ + Video: &adunitconfig.Video{ + Enabled: ptrutil.ToPtr(false), + }, + }, + }, + }, + }, + }, }, }, { @@ -1162,35 +1292,80 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { Video: &openrtb2.Video{}, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Video: &openrtb2.Video{ - W: 640, - H: 480, - MinDuration: 10, - MaxDuration: 40, - Skip: ptrutil.ToPtr(int8(1)), - SkipMin: 5, - SkipAfter: 10, - Plcmt: 1, - Placement: 1, - MinBitRate: 100, - MaxBitRate: 200, - MaxExtended: 50, - Linearity: 1, - Protocol: 1, - Sequence: 2, - BoxingAllowed: 1, - PlaybackEnd: 2, - MIMEs: []string{"mimes"}, - API: []adcom1.APIFramework{1, 2}, - Delivery: []adcom1.DeliveryMethod{1, 2}, - PlaybackMethod: []adcom1.PlaybackMethod{1, 2}, - BAttr: []adcom1.CreativeAttribute{1, 2}, - StartDelay: ptrutil.ToPtr(adcom1.StartDelay(2)), - Protocols: []adcom1.MediaCreativeSubtype{1, 2}, - Pos: ptrutil.ToPtr(adcom1.PlacementPosition(1)), - CompanionType: []adcom1.CompanionType{1, 2}, + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Video: &openrtb2.Video{ + W: 640, + H: 480, + MinDuration: 10, + MaxDuration: 40, + Skip: ptrutil.ToPtr(int8(1)), + SkipMin: 5, + SkipAfter: 10, + Plcmt: 1, + Placement: 1, + MinBitRate: 100, + MaxBitRate: 200, + MaxExtended: 50, + Linearity: 1, + Protocol: 1, + Sequence: 2, + BoxingAllowed: 1, + PlaybackEnd: 2, + MIMEs: []string{"mimes"}, + API: []adcom1.APIFramework{1, 2}, + Delivery: []adcom1.DeliveryMethod{1, 2}, + PlaybackMethod: []adcom1.PlaybackMethod{1, 2}, + BAttr: []adcom1.CreativeAttribute{1, 2}, + StartDelay: ptrutil.ToPtr(adcom1.StartDelay(2)), + Protocols: []adcom1.MediaCreativeSubtype{1, 2}, + Pos: ptrutil.ToPtr(adcom1.PlacementPosition(1)), + CompanionType: []adcom1.CompanionType{1, 2}, + }, + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + VideoAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: &adunitconfig.AdConfig{ + Video: &adunitconfig.Video{ + Enabled: ptrutil.ToPtr(true), + Config: &adunitconfig.VideoConfig{ + Video: openrtb2.Video{ + MinDuration: 10, + MaxDuration: 40, + Skip: ptrutil.ToPtr(int8(1)), + SkipMin: 5, + SkipAfter: 10, + Plcmt: 1, + Placement: 1, + MinBitRate: 100, + MaxBitRate: 200, + MaxExtended: 50, + Linearity: 1, + Protocol: 1, + W: 640, + H: 480, + Sequence: 2, + BoxingAllowed: 1, + PlaybackEnd: 2, + MIMEs: []string{"mimes"}, + API: []adcom1.APIFramework{1, 2}, + Delivery: []adcom1.DeliveryMethod{1, 2}, + PlaybackMethod: []adcom1.PlaybackMethod{1, 2}, + BAttr: []adcom1.CreativeAttribute{1, 2}, + StartDelay: ptrutil.ToPtr(adcom1.StartDelay(2)), + Protocols: []adcom1.MediaCreativeSubtype{1, 2}, + Pos: ptrutil.ToPtr(adcom1.PlacementPosition(1)), + CompanionType: []adcom1.CompanionType{1, 2}, + }, + }, + }, + }, + }, + }, + }, }, }, }, @@ -1232,16 +1407,40 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { }, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Video: &openrtb2.Video{ - W: 640, - H: 480, - MinDuration: 20, - MaxDuration: 60, - Skip: ptrutil.ToPtr(int8(2)), - SkipMin: 10, - SkipAfter: 20, + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Video: &openrtb2.Video{ + W: 640, + H: 480, + MinDuration: 20, + MaxDuration: 60, + Skip: ptrutil.ToPtr(int8(2)), + SkipMin: 10, + SkipAfter: 20, + }, + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + VideoAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: &adunitconfig.AdConfig{ + Video: &adunitconfig.Video{ + Enabled: ptrutil.ToPtr(true), + Config: &adunitconfig.VideoConfig{ + Video: openrtb2.Video{ + MinDuration: 10, + MaxDuration: 40, + Skip: ptrutil.ToPtr(int8(1)), + SkipMin: 5, + SkipAfter: 10, + }, + }, + }, + }, + }, + }, + }, }, }, }, @@ -1254,7 +1453,8 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { metricEngine: tt.fields.metricEngine, } m.applyVideoAdUnitConfig(tt.args.rCtx, tt.args.imp) - assert.Equal(t, tt.args.imp, tt.want, "Imp video is not upadted as expected from adunit config") + assert.Equal(t, tt.args.imp, tt.want.imp, "Imp video is not upadted as expected from adunit config") + assert.Equal(t, tt.args.rCtx, tt.want.rCtx, "rctx is not upadted as expected from adunit config") }) } } @@ -1269,11 +1469,15 @@ func TestOpenWrap_applyBannerAdUnitConfig(t *testing.T) { rCtx models.RequestCtx imp *openrtb2.Imp } + type want struct { + rCtx models.RequestCtx + imp *openrtb2.Imp + } tests := []struct { name string fields fields args args - want *openrtb2.Imp + want want }{ { name: "imp.banner_is_nil", @@ -1282,8 +1486,10 @@ func TestOpenWrap_applyBannerAdUnitConfig(t *testing.T) { Banner: nil, }, }, - want: &openrtb2.Imp{ - Banner: nil, + want: want{ + imp: &openrtb2.Imp{ + Banner: nil, + }, }, }, { @@ -1303,9 +1509,20 @@ func TestOpenWrap_applyBannerAdUnitConfig(t *testing.T) { Banner: &openrtb2.Banner{}, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Banner: &openrtb2.Banner{}, + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Banner: &openrtb2.Banner{}, + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + BannerAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: nil, + }, + }, + }, + }, }, }, { @@ -1330,11 +1547,27 @@ func TestOpenWrap_applyBannerAdUnitConfig(t *testing.T) { Banner: &openrtb2.Banner{}, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Banner: &openrtb2.Banner{}, - BidFloor: 2.0, - BidFloorCur: "USD", + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Banner: &openrtb2.Banner{}, + BidFloor: 2.0, + BidFloorCur: "USD", + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + BannerAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: &adunitconfig.AdConfig{ + BidFloor: ptrutil.ToPtr(2.0), + BidFloorCur: ptrutil.ToPtr("USD"), + }, + }, + BidFloor: 2, + BidFloorCur: "USD", + }, + }, + }, }, }, { @@ -1356,10 +1589,23 @@ func TestOpenWrap_applyBannerAdUnitConfig(t *testing.T) { Banner: &openrtb2.Banner{}, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Banner: &openrtb2.Banner{}, - Exp: 10, + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Banner: &openrtb2.Banner{}, + Exp: 10, + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + BannerAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: &adunitconfig.AdConfig{ + Exp: ptrutil.ToPtr(10), + }, + }, + }, + }, + }, }, }, { @@ -1384,11 +1630,24 @@ func TestOpenWrap_applyBannerAdUnitConfig(t *testing.T) { }, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Banner: &openrtb2.Banner{ - W: ptrutil.ToPtr[int64](200), - H: ptrutil.ToPtr[int64](300), + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Banner: &openrtb2.Banner{ + W: ptrutil.ToPtr[int64](200), + H: ptrutil.ToPtr[int64](300), + }, + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + BannerAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: &adunitconfig.AdConfig{ + Banner: nil, + }, + }, + }, + }, }, }, }, @@ -1416,9 +1675,24 @@ func TestOpenWrap_applyBannerAdUnitConfig(t *testing.T) { }, }, }, - want: &openrtb2.Imp{ - ID: "testImp", - Banner: nil, + want: want{ + imp: &openrtb2.Imp{ + ID: "testImp", + Banner: nil, + }, + rCtx: models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "testImp": { + BannerAdUnitCtx: models.AdUnitCtx{ + AppliedSlotAdUnitConfig: &adunitconfig.AdConfig{ + Banner: &adunitconfig.Banner{ + Enabled: ptrutil.ToPtr(false), + }, + }, + }, + }, + }, + }, }, }, } @@ -1430,7 +1704,8 @@ func TestOpenWrap_applyBannerAdUnitConfig(t *testing.T) { metricEngine: tt.fields.metricEngine, } m.applyBannerAdUnitConfig(tt.args.rCtx, tt.args.imp) - assert.Equal(t, tt.args.imp, tt.want, "Imp banner is not upadted as expected from adunit config") + assert.Equal(t, tt.args.imp, tt.want.imp, "Imp banner is not upadted as expected from adunit config") + assert.Equal(t, tt.args.rCtx, tt.want.rCtx, "rctx is not upadted as expected from adunit config") }) } } @@ -2204,7 +2479,7 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { }, }, }, - bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","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"}}}`), + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","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","bidfloor":4.3,"bidfloorcur":"USD","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, @@ -2287,6 +2562,223 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { } } +func TestGetSlotName(t *testing.T) { + type args struct { + tagId string + impExt *models.ImpExtension + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Slot_name_from_gpid", + args: args{ + tagId: "some-tagid", + impExt: &models.ImpExtension{ + GpId: "some-gpid", + }, + }, + want: "some-gpid", + }, + { + name: "Slot_name_from_tagid", + args: args{ + tagId: "some-tagid", + impExt: &models.ImpExtension{ + Data: openrtb_ext.ExtImpData{ + PbAdslot: "some-pbadslot", + }, + }, + }, + want: "some-tagid", + }, + { + name: "Slot_name_from_pbadslot", + args: args{ + tagId: "", + impExt: &models.ImpExtension{ + Data: openrtb_ext.ExtImpData{ + PbAdslot: "some-pbadslot", + }, + }, + }, + want: "some-pbadslot", + }, + { + name: "Slot_name_from_stored_request_id", + args: args{ + tagId: "", + impExt: &models.ImpExtension{ + Prebid: openrtb_ext.ExtImpPrebid{ + StoredRequest: &openrtb_ext.ExtStoredRequest{ + ID: "stored-req-id", + }, + }, + }, + }, + want: "stored-req-id", + }, + { + name: "imp_ext_nil_slot_name_from_tag_id", + args: args{ + tagId: "some-tagid", + impExt: nil, + }, + want: "some-tagid", + }, + { + name: "empty_slot_name", + args: args{ + tagId: "", + impExt: &models.ImpExtension{}, + }, + want: "", + }, + { + name: "all_level_information_is_present_slot_name_picked_by_preference", + args: args{ + tagId: "some-tagid", + impExt: &models.ImpExtension{ + GpId: "some-gpid", + Data: openrtb_ext.ExtImpData{ + PbAdslot: "some-pbadslot", + }, + Prebid: openrtb_ext.ExtImpPrebid{ + StoredRequest: &openrtb_ext.ExtStoredRequest{ + ID: "stored-req-id", + }, + }, + }, + }, + want: "some-gpid", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getSlotName(tt.args.tagId, tt.args.impExt) + assert.Equal(t, tt.want, got, tt.name) + }) + } +} + +func TestGetAdunitName(t *testing.T) { + type args struct { + tagId string + impExt *models.ImpExtension + } + tests := []struct { + name string + args args + want string + }{ + { + name: "adunit_from_adserver_slot", + args: args{ + tagId: "some-tagid", + impExt: &models.ImpExtension{ + Data: openrtb_ext.ExtImpData{ + PbAdslot: "some-pbadslot", + AdServer: &openrtb_ext.ExtImpDataAdServer{ + Name: models.GamAdServer, + AdSlot: "gam-unit", + }, + }, + }, + }, + want: "gam-unit", + }, + { + name: "adunit_from_pbadslot", + args: args{ + tagId: "some-tagid", + impExt: &models.ImpExtension{ + Data: openrtb_ext.ExtImpData{ + PbAdslot: "some-pbadslot", + AdServer: &openrtb_ext.ExtImpDataAdServer{ + Name: models.GamAdServer, + AdSlot: "", + }, + }, + }, + }, + want: "some-pbadslot", + }, + { + name: "adunit_from_pbadslot_when_gam_is_absent", + args: args{ + tagId: "some-tagid", + impExt: &models.ImpExtension{ + Data: openrtb_ext.ExtImpData{ + PbAdslot: "some-pbadslot", + AdServer: &openrtb_ext.ExtImpDataAdServer{ + Name: "freewheel", + AdSlot: "freewheel-unit", + }, + }, + }, + }, + want: "some-pbadslot", + }, + { + name: "adunit_from_TagId", + args: args{ + tagId: "some-tagid", + impExt: &models.ImpExtension{ + Data: openrtb_ext.ExtImpData{ + PbAdslot: "", + AdServer: &openrtb_ext.ExtImpDataAdServer{ + Name: models.GamAdServer, + AdSlot: "", + }, + }, + }, + }, + want: "some-tagid", + }, + { + name: "adunit_from_TagId_imp_ext_nil", + args: args{ + tagId: "some-tagid", + impExt: nil, + }, + want: "some-tagid", + }, + { + name: "adunit_from_TagId_imp_ext_nil", + args: args{ + tagId: "some-tagid", + impExt: &models.ImpExtension{}, + }, + want: "some-tagid", + }, + { + name: "all_level_information_is_present_adunit_name_picked_by_preference", + args: args{ + tagId: "some-tagid", + impExt: &models.ImpExtension{ + GpId: "some-gpid", + Data: openrtb_ext.ExtImpData{ + PbAdslot: "some-pbadslot", + AdServer: &openrtb_ext.ExtImpDataAdServer{ + Name: models.GamAdServer, + AdSlot: "gam-unit", + }, + }, + }, + }, + want: "gam-unit", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getAdunitName(tt.args.tagId, tt.args.impExt) + assert.Equal(t, tt.want, got, tt.name) + }) + } +} + func TestGetTagID(t *testing.T) { type args struct { imp openrtb2.Imp @@ -2310,7 +2802,7 @@ func TestGetTagID(t *testing.T) { args: args{ imp: openrtb2.Imp{}, impExt: &models.ImpExtension{ - Gpid: "/7578294/adunit1", + GpId: "/7578294/adunit1", }, }, want: "/7578294/adunit1", @@ -2342,7 +2834,7 @@ func TestGetTagID(t *testing.T) { args: args{ imp: openrtb2.Imp{}, impExt: &models.ImpExtension{ - Gpid: "/7578294/adunit123", + GpId: "/7578294/adunit123", Data: openrtb_ext.ExtImpData{ PbAdslot: "/7578294/adunit", }, @@ -2357,7 +2849,7 @@ func TestGetTagID(t *testing.T) { TagID: "/7578294/adunit", }, impExt: &models.ImpExtension{ - Gpid: "/7578294/adunit123", + GpId: "/7578294/adunit123", }, }, want: "/7578294/adunit123", @@ -2383,7 +2875,7 @@ func TestGetTagID(t *testing.T) { TagID: "/7578294/adunit", }, impExt: &models.ImpExtension{ - Gpid: "/7578294/adunit123", + GpId: "/7578294/adunit123", Data: openrtb_ext.ExtImpData{ PbAdslot: "/7578294/adunit12345", }, @@ -2398,7 +2890,7 @@ func TestGetTagID(t *testing.T) { TagID: "/7578294/adunit", }, impExt: &models.ImpExtension{ - Gpid: "/43743431/DMDemo#Div1", + GpId: "/43743431/DMDemo#Div1", Data: openrtb_ext.ExtImpData{ PbAdslot: "/7578294/adunit12345", }, diff --git a/modules/pubmatic/openwrap/bidderparams/common.go b/modules/pubmatic/openwrap/bidderparams/common.go index abd514eab9c..ea7a4bd303a 100644 --- a/modules/pubmatic/openwrap/bidderparams/common.go +++ b/modules/pubmatic/openwrap/bidderparams/common.go @@ -72,7 +72,7 @@ func getSlotMeta(rctx models.RequestCtx, cache cache.Cache, bidRequest openrtb2. var slots []string for _, format := range hw { // TODO fix the param sequence. make it consistent. HxW - slot := GenerateSlotName(format[0], format[1], kgp, imp.TagID, div, rctx.Source) + slot := models.GenerateSlotName(format[0], format[1], kgp, imp.TagID, div, rctx.Source) if slot != "" { slots = append(slots, slot) // NYC_TODO: break at i=0 for pubmatic? @@ -83,43 +83,6 @@ func getSlotMeta(rctx models.RequestCtx, cache cache.Cache, bidRequest openrtb2. return slots, slotMap, slotMappingInfo, hw } -// Harcode would be the optimal. We could make it configurable like _AU_@_W_x_H_:%s@%dx%d entries in pbs.yaml -// mysql> SELECT DISTINCT key_gen_pattern FROM wrapper_mapping_template; -// +----------------------+ -// | key_gen_pattern | -// +----------------------+ -// | _AU_@_W_x_H_ | -// | _DIV_@_W_x_H_ | -// | _W_x_H_@_W_x_H_ | -// | _DIV_ | -// | _AU_@_DIV_@_W_x_H_ | -// | _AU_@_SRC_@_VASTTAG_ | -// +----------------------+ -// 6 rows in set (0.21 sec) -func GenerateSlotName(h, w int64, kgp, tagid, div, src string) string { - // func (H, W, Div), no need to validate, will always be non-nil - switch kgp { - case "_AU_": // adunitconfig - return tagid - case "_DIV_": - return div - case "_AU_@_W_x_H_": - return fmt.Sprintf("%s@%dx%d", tagid, w, h) - case "_DIV_@_W_x_H_": - return fmt.Sprintf("%s@%dx%d", div, w, h) - case "_W_x_H_@_W_x_H_": - return fmt.Sprintf("%dx%d@%dx%d", w, h, w, h) - case "_AU_@_DIV_@_W_x_H_": - return fmt.Sprintf("%s@%s@%dx%d", tagid, div, w, h) - case "_AU_@_SRC_@_VASTTAG_": - return fmt.Sprintf("%s@%s@_VASTTAG_", tagid, src) //TODO check where/how _VASTTAG_ is updated - default: - // TODO: check if we need to fallback to old generic flow (below) - // Add this cases in a map and read it from yaml file - } - return "" -} - /* formSlotForDefaultMapping: In this method, we are removing wxh from the kgp because pubmatic adapter sets wxh that we send in imp.ext.pubmatic.adslot as primary size while calling translator. diff --git a/modules/pubmatic/openwrap/bidderparams/common_test.go b/modules/pubmatic/openwrap/bidderparams/common_test.go index d560fd99c05..20aad471933 100644 --- a/modules/pubmatic/openwrap/bidderparams/common_test.go +++ b/modules/pubmatic/openwrap/bidderparams/common_test.go @@ -11,149 +11,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestGenerateSlotName(t *testing.T) { - type args struct { - h int64 - w int64 - kgp string - tagid string - div string - src string - } - tests := []struct { - name string - args args - want string - }{ - { - name: "_AU_", - args: args{ - h: 100, - w: 200, - kgp: "_AU_", - tagid: "/15671365/Test_Adunit", - div: "Div1", - src: "test.com", - }, - want: "/15671365/Test_Adunit", - }, - { - name: "_DIV_", - args: args{ - h: 100, - w: 200, - kgp: "_DIV_", - tagid: "/15671365/Test_Adunit", - div: "Div1", - src: "test.com", - }, - want: "Div1", - }, - { - name: "_AU_", - args: args{ - h: 100, - w: 200, - kgp: "_AU_", - tagid: "/15671365/Test_Adunit", - div: "Div1", - src: "test.com", - }, - want: "/15671365/Test_Adunit", - }, - { - name: "_AU_@_W_x_H_", - args: args{ - h: 100, - w: 200, - kgp: "_AU_@_W_x_H_", - tagid: "/15671365/Test_Adunit", - div: "Div1", - src: "test.com", - }, - want: "/15671365/Test_Adunit@200x100", - }, - { - name: "_DIV_@_W_x_H_", - args: args{ - h: 100, - w: 200, - kgp: "_DIV_@_W_x_H_", - tagid: "/15671365/Test_Adunit", - div: "Div1", - src: "test.com", - }, - want: "Div1@200x100", - }, - { - name: "_W_x_H_@_W_x_H_", - args: args{ - h: 100, - w: 200, - kgp: "_W_x_H_@_W_x_H_", - tagid: "/15671365/Test_Adunit", - div: "Div1", - src: "test.com", - }, - want: "200x100@200x100", - }, - { - name: "_AU_@_DIV_@_W_x_H_", - args: args{ - h: 100, - w: 200, - kgp: "_AU_@_DIV_@_W_x_H_", - tagid: "/15671365/Test_Adunit", - div: "Div1", - src: "test.com", - }, - want: "/15671365/Test_Adunit@Div1@200x100", - }, - { - name: "_AU_@_SRC_@_VASTTAG_", - args: args{ - h: 100, - w: 200, - kgp: "_AU_@_SRC_@_VASTTAG_", - tagid: "/15671365/Test_Adunit", - div: "Div1", - src: "test.com", - }, - want: "/15671365/Test_Adunit@test.com@_VASTTAG_", - }, - { - name: "empty_kgp", - args: args{ - h: 100, - w: 200, - kgp: "", - tagid: "/15671365/Test_Adunit", - div: "Div1", - src: "test.com", - }, - want: "", - }, - { - name: "random_kgp", - args: args{ - h: 100, - w: 200, - kgp: "fjkdfhk", - tagid: "/15671365/Test_Adunit", - div: "Div1", - src: "test.com", - }, - want: "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := GenerateSlotName(tt.args.h, tt.args.w, tt.args.kgp, tt.args.tagid, tt.args.div, tt.args.src) - assert.Equal(t, tt.want, got) - }) - } -} - func TestGetSlotMeta(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/modules/pubmatic/openwrap/bidderparams/pubmatic.go b/modules/pubmatic/openwrap/bidderparams/pubmatic.go index cf440a14a35..e9438277240 100644 --- a/modules/pubmatic/openwrap/bidderparams/pubmatic.go +++ b/modules/pubmatic/openwrap/bidderparams/pubmatic.go @@ -93,7 +93,7 @@ func PreparePubMaticParamsV25(rctx models.RequestCtx, cache cache.Cache, bidRequ div = impExt.Wrapper.Div } unmappedKPG := getDefaultMappingKGP(kgp) - extImpPubMatic.AdSlot = GenerateSlotName(0, 0, unmappedKPG, imp.TagID, div, rctx.Source) + extImpPubMatic.AdSlot = models.GenerateSlotName(0, 0, unmappedKPG, imp.TagID, div, rctx.Source) if len(slots) != 0 { // reuse this field for wt and wl in combination with isRegex matchedPattern = slots[0] } diff --git a/modules/pubmatic/openwrap/defaultbids.go b/modules/pubmatic/openwrap/defaultbids.go index b8a6feede94..e4a1a14994b 100644 --- a/modules/pubmatic/openwrap/defaultbids.go +++ b/modules/pubmatic/openwrap/defaultbids.go @@ -5,13 +5,15 @@ import ( "strconv" "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v19/openrtb3" "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adunitconfig" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/openrtb_ext" + uuid "github.com/satori/go.uuid" ) -func (m *OpenWrap) addDefaultBids(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse, bidResponseExt *openrtb_ext.ExtBidResponse) map[string]map[string][]openrtb2.Bid { +func (m *OpenWrap) addDefaultBids(rctx *models.RequestCtx, bidResponse *openrtb2.BidResponse, bidResponseExt openrtb_ext.ExtBidResponse) map[string]map[string][]openrtb2.Bid { // responded bidders per impression seatBids := make(map[string]map[string]struct{}, len(bidResponse.SeatBid)) for _, seatBid := range bidResponse.SeatBid { @@ -37,29 +39,35 @@ func (m *OpenWrap) addDefaultBids(rctx models.RequestCtx, bidResponse *openrtb2. defaultBids := make(map[string]map[string][]openrtb2.Bid, 0) for impID, impCtx := range rctx.ImpBidCtx { for bidder := range impCtx.Bidders { - noBid := false - if bidders, ok := seatBids[impID]; ok { - if _, ok := bidders[bidder]; !ok { - noBid = true + if bidders, ok := seatBids[impID]; ok { // bid found for impID + if _, ok := bidders[bidder]; ok { // bid found for seat + continue } - } else { - noBid = true } - if noBid { - if defaultBids[impID] == nil { - defaultBids[impID] = make(map[string][]openrtb2.Bid) - } + if defaultBids[impID] == nil { + defaultBids[impID] = make(map[string][]openrtb2.Bid) + } - defaultBids[impID][bidder] = append(defaultBids[impID][bidder], openrtb2.Bid{ - ID: impID, - ImpID: impID, - Ext: newNoBidExt(rctx, impID), - }) + uuid := uuid.NewV4().String() + bidExt := newDefaultBidExt(*rctx, impID, bidder, bidResponseExt) + bidExtJson, _ := json.Marshal(bidExt) - // record error stats for each bidder - m.recordErrorStats(rctx, bidResponseExt, bidder) + defaultBids[impID][bidder] = append(defaultBids[impID][bidder], openrtb2.Bid{ + ID: uuid, + ImpID: impID, + Ext: bidExtJson, + }) + + // create bidCtx because we need it for owlogger + rctx.ImpBidCtx[impID].BidCtx[uuid] = models.BidCtx{ + BidExt: models.BidExt{ + Nbr: bidExt.Nbr, + }, } + + // record error stats for each bidder + m.recordErrorStats(*rctx, bidResponseExt, bidder) } } @@ -70,11 +78,15 @@ func (m *OpenWrap) addDefaultBids(rctx models.RequestCtx, bidResponse *openrtb2. defaultBids[impID] = make(map[string][]openrtb2.Bid) } + bidExt := newDefaultBidExt(*rctx, impID, bidder, bidResponseExt) + bidExtJson, _ := json.Marshal(bidExt) + // no need to create impBidCtx since we dont log partner-throttled bid in owlogger + defaultBids[impID][bidder] = []openrtb2.Bid{ { - ID: impID, + ID: uuid.NewV4().String(), ImpID: impID, - Ext: newNoBidExt(rctx, impID), + Ext: bidExtJson, }, } } @@ -87,11 +99,15 @@ func (m *OpenWrap) addDefaultBids(rctx models.RequestCtx, bidResponse *openrtb2. defaultBids[impID] = make(map[string][]openrtb2.Bid) } + bidExt := newDefaultBidExt(*rctx, impID, bidder, bidResponseExt) + bidExtJson, _ := json.Marshal(bidExt) + // no need to create impBidCtx since we dont log slot-not-mapped bid in owlogger + defaultBids[impID][bidder] = []openrtb2.Bid{ { - ID: impID, + ID: uuid.NewV4().String(), ImpID: impID, - Ext: newNoBidExt(rctx, impID), + Ext: bidExtJson, }, } } @@ -100,9 +116,28 @@ func (m *OpenWrap) addDefaultBids(rctx models.RequestCtx, bidResponse *openrtb2. return defaultBids } -func newNoBidExt(rctx models.RequestCtx, impID string) json.RawMessage { +// getNonBRCodeFromBidRespExt maps the error-code present in prebid partner response with standard nonBR code +func getNonBRCodeFromBidRespExt(bidder string, bidResponseExt openrtb_ext.ExtBidResponse) *openrtb3.NonBidStatusCode { + errs := bidResponseExt.Errors[openrtb_ext.BidderName(bidder)] + if len(errs) == 0 { + return GetNonBidStatusCodePtr(openrtb3.NoBidGeneral) + } + + switch errs[0].Code { + case errortypes.TimeoutErrorCode: + return GetNonBidStatusCodePtr(openrtb3.NoBidTimeoutError) + case errortypes.UnknownErrorCode: + return GetNonBidStatusCodePtr(openrtb3.NoBidGeneralError) + default: + return GetNonBidStatusCodePtr(openrtb3.NoBidGeneralError) + } +} + +func newDefaultBidExt(rctx models.RequestCtx, impID, bidder string, bidResponseExt openrtb_ext.ExtBidResponse) *models.BidExt { + bidExt := models.BidExt{ NetECPM: 0, + Nbr: getNonBRCodeFromBidRespExt(bidder, bidResponseExt), } if rctx.ClientConfigFlag == 1 { if cc := adunitconfig.GetClientConfigForMediaType(rctx, impID, "banner"); cc != nil { @@ -124,13 +159,7 @@ func newNoBidExt(rctx models.RequestCtx, impID string) json.RawMessage { bidExt.RefreshInterval = n } } - - newBidExt, err := json.Marshal(bidExt) - if err != nil { - return nil - } - - return json.RawMessage(newBidExt) + return &bidExt } func (m *OpenWrap) applyDefaultBids(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) (*openrtb2.BidResponse, error) { @@ -159,8 +188,7 @@ func (m *OpenWrap) applyDefaultBids(rctx models.RequestCtx, bidResponse *openrtb return bidResponse, nil } - -func (m *OpenWrap) recordErrorStats(rctx models.RequestCtx, bidResponseExt *openrtb_ext.ExtBidResponse, bidder string) { +func (m *OpenWrap) recordErrorStats(rctx models.RequestCtx, bidResponseExt openrtb_ext.ExtBidResponse, bidder string) { responseError := models.PartnerErrNoBid diff --git a/modules/pubmatic/openwrap/defaultbids_test.go b/modules/pubmatic/openwrap/defaultbids_test.go new file mode 100644 index 00000000000..714a7731e14 --- /dev/null +++ b/modules/pubmatic/openwrap/defaultbids_test.go @@ -0,0 +1,87 @@ +package openwrap + +import ( + "testing" + + "github.com/prebid/openrtb/v19/openrtb3" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestGetNonBRCodeFromBidRespExt(t *testing.T) { + type args struct { + bidder string + bidResponseExt openrtb_ext.ExtBidResponse + } + tests := []struct { + name string + args args + nbr *openrtb3.NonBidStatusCode + }{ + { + name: "bidResponseExt.Errors_is_empty", + args: args{ + bidder: "pubmatic", + bidResponseExt: openrtb_ext.ExtBidResponse{ + Errors: nil, + }, + }, + nbr: GetNonBidStatusCodePtr(openrtb3.NoBidGeneral), + }, + { + name: "invalid_partner_err", + args: args{ + bidder: "pubmatic", + bidResponseExt: openrtb_ext.ExtBidResponse{ + Errors: map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage{ + "pubmatic": { + { + Code: 0, + }, + }, + }, + }, + }, + nbr: GetNonBidStatusCodePtr(openrtb3.NoBidGeneralError), + }, + { + name: "unknown_partner_err", + args: args{ + bidder: "pubmatic", + bidResponseExt: openrtb_ext.ExtBidResponse{ + Errors: map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage{ + "pubmatic": { + { + Code: errortypes.UnknownErrorCode, + }, + }, + }, + }, + }, + nbr: GetNonBidStatusCodePtr(openrtb3.NoBidGeneralError), + }, + { + name: "partner_timeout_err", + args: args{ + bidder: "pubmatic", + bidResponseExt: openrtb_ext.ExtBidResponse{ + Errors: map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage{ + "pubmatic": { + { + Code: errortypes.TimeoutErrorCode, + }, + }, + }, + }, + }, + nbr: GetNonBidStatusCodePtr(openrtb3.NoBidTimeoutError), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + nbr := getNonBRCodeFromBidRespExt(tt.args.bidder, tt.args.bidResponseExt) + assert.Equal(t, tt.nbr, nbr, tt.name) + }) + } +} diff --git a/modules/pubmatic/openwrap/entrypointhook.go b/modules/pubmatic/openwrap/entrypointhook.go index a4c25a460f6..cc5d76cd479 100644 --- a/modules/pubmatic/openwrap/entrypointhook.go +++ b/modules/pubmatic/openwrap/entrypointhook.go @@ -117,6 +117,7 @@ func (m OpenWrap) handleEntrypointHook( UA: payload.Request.Header.Get("User-Agent"), ProfileID: requestExtWrapper.ProfileId, DisplayID: requestExtWrapper.VersionId, + DisplayVersionID: requestExtWrapper.VersionId, LogInfoFlag: requestExtWrapper.LogInfoFlag, SupportDeals: requestExtWrapper.SupportDeals, ABTestConfig: requestExtWrapper.ABTestConfig, @@ -136,8 +137,17 @@ func (m OpenWrap) handleEntrypointHook( ProfileIDStr: strconv.Itoa(requestExtWrapper.ProfileId), Endpoint: endpoint, MetricsEngine: m.metricEngine, + DCName: m.cfg.Server.DCName, SeatNonBids: make(map[string][]openrtb_ext.NonBid), ParsedUidCookie: usersync.ReadCookie(payload.Request, usersync.Base64Decoder{}, &config.HostCookie{}), + TMax: m.cfg.Timeout.MaxTimeout, + CurrencyConversion: func(from, to string, value float64) (float64, error) { + rate, err := m.currencyConversion.GetRate(from, to) + if err == nil { + return value * rate, nil + } + return 0, err + }, } // only http.ErrNoCookie is returned, we can ignore it diff --git a/modules/pubmatic/openwrap/entrypointhook_test.go b/modules/pubmatic/openwrap/entrypointhook_test.go index a763e1f6618..1ca4b25b06b 100644 --- a/modules/pubmatic/openwrap/entrypointhook_test.go +++ b/modules/pubmatic/openwrap/entrypointhook_test.go @@ -100,6 +100,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { "rctx": models.RequestCtx{ ProfileID: 5890, DisplayID: 1, + DisplayVersionID: 1, SSAuction: -1, Debug: true, UA: "go-test", @@ -162,6 +163,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { "rctx": models.RequestCtx{ ProfileID: 5890, DisplayID: 1, + DisplayVersionID: 1, SSAuction: -1, Debug: true, UA: "go-test", @@ -240,15 +242,16 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { want: hookstage.HookResult[hookstage.EntrypointPayload]{ ModuleContext: hookstage.ModuleContext{ "rctx": models.RequestCtx{ - ProfileID: 43563, - PubID: 0, - PubIDStr: "", - DisplayID: 1, - SSAuction: -1, - Debug: true, - UA: "go-test", - IP: "127.0.0.1", - IsCTVRequest: false, + ProfileID: 43563, + PubID: 0, + PubIDStr: "", + DisplayID: 1, + DisplayVersionID: 1, + SSAuction: -1, + Debug: true, + UA: "go-test", + IP: "127.0.0.1", + IsCTVRequest: false, UidCookie: &http.Cookie{ Name: "uids", Value: `eyJ0ZW1wVUlEcyI6eyIzM2Fjcm9zcyI6eyJ1aWQiOiIxMTkxNzkxMDk5Nzc2NjEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTo0My4zODg4Nzk5NVoifSwiYWRmIjp7InVpZCI6IjgwNDQ2MDgzMzM3Nzg4MzkwNzgiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMS4wMzMwNTQ3MjdaIn0sImFka2VybmVsIjp7InVpZCI6IkE5MTYzNTAwNzE0OTkyOTMyOTkwIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuMzczMzg1NjYyWiJ9LCJhZGtlcm5lbEFkbiI6eyJ1aWQiOiJBOTE2MzUwMDcxNDk5MjkzMjk5MCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEzLjQzNDkyNTg5NloifSwiYWRtaXhlciI6eyJ1aWQiOiIzNjZhMTdiMTJmMjI0ZDMwOGYzZTNiOGRhOGMzYzhhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjU5MjkxNDgwMVoifSwiYWRueHMiOnsidWlkIjoiNDE5Mjg5ODUzMDE0NTExOTMiLCJleHBpcmVzIjoiMjAyMy0wMS0xOFQwOTo1MzowOC44MjU0NDI2NzZaIn0sImFqYSI6eyJ1aWQiOiJzMnN1aWQ2RGVmMFl0bjJveGQ1aG9zS1AxVmV3IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTMuMjM5MTc2MDU0WiJ9LCJlcGxhbm5pbmciOnsidWlkIjoiQUoxRjBTOE5qdTdTQ0xWOSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjkyOTk2MDQ3M1oifSwiZ2Ftb3NoaSI6eyJ1aWQiOiJndXNyXzM1NmFmOWIxZDhjNjQyYjQ4MmNiYWQyYjdhMjg4MTYxIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuNTI0MTU3MjI1WiJ9LCJncmlkIjp7InVpZCI6IjRmYzM2MjUwLWQ4NTItNDU5Yy04NzcyLTczNTZkZTE3YWI5NyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE0LjY5NjMxNjIyN1oifSwiZ3JvdXBtIjp7InVpZCI6IjdENzVEMjVGLUZBQzktNDQzRC1CMkQxLUIxN0ZFRTExRTAyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjM5LjIyNjIxMjUzMloifSwiaXgiOnsidWlkIjoiWW9ORlNENlc5QkphOEh6eEdtcXlCUUFBXHUwMDI2Mjk3IiwiZXhwaXJlcyI6IjIwMjMtMDUtMzFUMDc6NTM6MzguNTU1ODI3MzU0WiJ9LCJqaXhpZSI6eyJ1aWQiOiI3MzY3MTI1MC1lODgyLTExZWMtYjUzOC0xM2FjYjdhZjBkZTQiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi4xOTEwOTk3MzJaIn0sImxvZ2ljYWQiOnsidWlkIjoiQVZ4OVROQS11c25pa3M4QURzTHpWa3JvaDg4QUFBR0JUREh0UUEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS40NTUxNDk2MTZaIn0sIm1lZGlhbmV0Ijp7InVpZCI6IjI5Nzg0MjM0OTI4OTU0MTAwMDBWMTAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMy42NzIyMTUxMjhaIn0sIm1naWQiOnsidWlkIjoibTU5Z1hyN0xlX1htIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTcuMDk3MDAxNDcxWiJ9LCJuYW5vaW50ZXJhY3RpdmUiOnsidWlkIjoiNmFlYzhjMTAzNzlkY2I3ODQxMmJjODBiNmRkOWM5NzMxNzNhYjdkNzEyZTQzMWE1YTVlYTcwMzRlNTZhNThhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE2LjcxNDgwNzUwNVoifSwib25ldGFnIjp7InVpZCI6IjdPelZoVzFOeC1LOGFVak1HMG52NXVNYm5YNEFHUXZQbnVHcHFrZ3k0ckEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS4xNDE3NDEyNjJaIn0sIm9wZW54Ijp7InVpZCI6IjVkZWNlNjIyLTBhMjMtMGRhYi0zYTI0LTVhNzcwMTBlNDU4MiIsImV4cGlyZXMiOiIyMDIzLTA1LTMxVDA3OjUyOjQ3LjE0MDQxNzM2M1oifSwicHVibWF0aWMiOnsidWlkIjoiN0Q3NUQyNUYtRkFDOS00NDNELUIyRDEtQjE3RkVFMTFFMDI3IiwiZXhwaXJlcyI6IjIwMjItMTAtMzFUMDk6MTQ6MjUuNzM3MjU2ODk5WiJ9LCJyaWNoYXVkaWVuY2UiOnsidWlkIjoiY2I2YzYzMjAtMzNlMi00Nzc0LWIxNjAtMXp6MTY1NDg0MDc0OSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjUyNTA3NDE4WiJ9LCJzbWFydHlhZHMiOnsidWlkIjoiMTJhZjE1ZTQ0ZjAwZDA3NjMwZTc0YzQ5MTU0Y2JmYmE0Zjg0N2U4ZDRhMTU0YzhjM2Q1MWY1OGNmNzJhNDYyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjgyNTAzMTg4NFoifSwic21pbGV3YW50ZWQiOnsidWlkIjoiZGQ5YzNmZTE4N2VmOWIwOWNhYTViNzExNDA0YzI4MzAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNC4yNTU2MDkzNjNaIn0sInN5bmFjb3JtZWRpYSI6eyJ1aWQiOiJHRFBSIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuOTc5NTgzNDM4WiJ9LCJ0cmlwbGVsaWZ0Ijp7InVpZCI6IjcwMjE5NzUwNTQ4MDg4NjUxOTQ2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA4Ljk4OTY3MzU3NFoifSwidmFsdWVpbXByZXNzaW9uIjp7InVpZCI6IjlkMDgxNTVmLWQ5ZmUtNGI1OC04OThlLWUyYzU2MjgyYWIzZSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjA2NzgzOTE2NFoifSwidmlzeCI6eyJ1aWQiOiIyN2UwYWMzYy1iNDZlLTQxYjMtOTkyYy1mOGQyNzE0OTQ5NWUiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi45ODk1MjM1NzNaIn0sInlpZWxkbGFiIjp7InVpZCI6IjY5NzE0ZDlmLWZiMDAtNGE1Zi04MTljLTRiZTE5MTM2YTMyNSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjExLjMwMzAyNjYxNVoifSwieWllbGRtbyI6eyJ1aWQiOiJnOTZjMmY3MTlmMTU1MWIzMWY2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjExMDUyODYwOVoifSwieWllbGRvbmUiOnsidWlkIjoiMmE0MmZiZDMtMmM3MC00ZWI5LWIxYmQtMDQ2OTY2NTBkOTQ4IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuMzE4MzMzOTM5WiJ9LCJ6ZXJvY2xpY2tmcmF1ZCI6eyJ1aWQiOiJiOTk5NThmZS0yYTg3LTJkYTQtOWNjNC05NjFmZDExM2JlY2UiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNS43MTk1OTQ1NjZaIn19LCJiZGF5IjoiMjAyMi0wNS0xN1QwNjo0ODozOC4wMTc5ODgyMDZaIn0=`, @@ -298,15 +301,16 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { want: hookstage.HookResult[hookstage.EntrypointPayload]{ ModuleContext: hookstage.ModuleContext{ "rctx": models.RequestCtx{ - ProfileID: 43563, - PubID: 0, - PubIDStr: "", - DisplayID: 1, - SSAuction: -1, - Debug: true, - UA: "go-test", - IP: "127.0.0.1", - IsCTVRequest: false, + ProfileID: 43563, + PubID: 0, + PubIDStr: "", + DisplayID: 1, + DisplayVersionID: 1, + SSAuction: -1, + Debug: true, + UA: "go-test", + IP: "127.0.0.1", + IsCTVRequest: false, UidCookie: &http.Cookie{ Name: "uids", Value: `eyJ0ZW1wVUlEcyI6eyIzM2Fjcm9zcyI6eyJ1aWQiOiIxMTkxNzkxMDk5Nzc2NjEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTo0My4zODg4Nzk5NVoifSwiYWRmIjp7InVpZCI6IjgwNDQ2MDgzMzM3Nzg4MzkwNzgiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMS4wMzMwNTQ3MjdaIn0sImFka2VybmVsIjp7InVpZCI6IkE5MTYzNTAwNzE0OTkyOTMyOTkwIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuMzczMzg1NjYyWiJ9LCJhZGtlcm5lbEFkbiI6eyJ1aWQiOiJBOTE2MzUwMDcxNDk5MjkzMjk5MCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEzLjQzNDkyNTg5NloifSwiYWRtaXhlciI6eyJ1aWQiOiIzNjZhMTdiMTJmMjI0ZDMwOGYzZTNiOGRhOGMzYzhhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjU5MjkxNDgwMVoifSwiYWRueHMiOnsidWlkIjoiNDE5Mjg5ODUzMDE0NTExOTMiLCJleHBpcmVzIjoiMjAyMy0wMS0xOFQwOTo1MzowOC44MjU0NDI2NzZaIn0sImFqYSI6eyJ1aWQiOiJzMnN1aWQ2RGVmMFl0bjJveGQ1aG9zS1AxVmV3IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTMuMjM5MTc2MDU0WiJ9LCJlcGxhbm5pbmciOnsidWlkIjoiQUoxRjBTOE5qdTdTQ0xWOSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjkyOTk2MDQ3M1oifSwiZ2Ftb3NoaSI6eyJ1aWQiOiJndXNyXzM1NmFmOWIxZDhjNjQyYjQ4MmNiYWQyYjdhMjg4MTYxIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuNTI0MTU3MjI1WiJ9LCJncmlkIjp7InVpZCI6IjRmYzM2MjUwLWQ4NTItNDU5Yy04NzcyLTczNTZkZTE3YWI5NyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE0LjY5NjMxNjIyN1oifSwiZ3JvdXBtIjp7InVpZCI6IjdENzVEMjVGLUZBQzktNDQzRC1CMkQxLUIxN0ZFRTExRTAyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjM5LjIyNjIxMjUzMloifSwiaXgiOnsidWlkIjoiWW9ORlNENlc5QkphOEh6eEdtcXlCUUFBXHUwMDI2Mjk3IiwiZXhwaXJlcyI6IjIwMjMtMDUtMzFUMDc6NTM6MzguNTU1ODI3MzU0WiJ9LCJqaXhpZSI6eyJ1aWQiOiI3MzY3MTI1MC1lODgyLTExZWMtYjUzOC0xM2FjYjdhZjBkZTQiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi4xOTEwOTk3MzJaIn0sImxvZ2ljYWQiOnsidWlkIjoiQVZ4OVROQS11c25pa3M4QURzTHpWa3JvaDg4QUFBR0JUREh0UUEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS40NTUxNDk2MTZaIn0sIm1lZGlhbmV0Ijp7InVpZCI6IjI5Nzg0MjM0OTI4OTU0MTAwMDBWMTAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMy42NzIyMTUxMjhaIn0sIm1naWQiOnsidWlkIjoibTU5Z1hyN0xlX1htIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTcuMDk3MDAxNDcxWiJ9LCJuYW5vaW50ZXJhY3RpdmUiOnsidWlkIjoiNmFlYzhjMTAzNzlkY2I3ODQxMmJjODBiNmRkOWM5NzMxNzNhYjdkNzEyZTQzMWE1YTVlYTcwMzRlNTZhNThhMCIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjE2LjcxNDgwNzUwNVoifSwib25ldGFnIjp7InVpZCI6IjdPelZoVzFOeC1LOGFVak1HMG52NXVNYm5YNEFHUXZQbnVHcHFrZ3k0ckEiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OTowOS4xNDE3NDEyNjJaIn0sIm9wZW54Ijp7InVpZCI6IjVkZWNlNjIyLTBhMjMtMGRhYi0zYTI0LTVhNzcwMTBlNDU4MiIsImV4cGlyZXMiOiIyMDIzLTA1LTMxVDA3OjUyOjQ3LjE0MDQxNzM2M1oifSwicHVibWF0aWMiOnsidWlkIjoiN0Q3NUQyNUYtRkFDOS00NDNELUIyRDEtQjE3RkVFMTFFMDI3IiwiZXhwaXJlcyI6IjIwMjItMTAtMzFUMDk6MTQ6MjUuNzM3MjU2ODk5WiJ9LCJyaWNoYXVkaWVuY2UiOnsidWlkIjoiY2I2YzYzMjAtMzNlMi00Nzc0LWIxNjAtMXp6MTY1NDg0MDc0OSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjUyNTA3NDE4WiJ9LCJzbWFydHlhZHMiOnsidWlkIjoiMTJhZjE1ZTQ0ZjAwZDA3NjMwZTc0YzQ5MTU0Y2JmYmE0Zjg0N2U4ZDRhMTU0YzhjM2Q1MWY1OGNmNzJhNDYyNyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjgyNTAzMTg4NFoifSwic21pbGV3YW50ZWQiOnsidWlkIjoiZGQ5YzNmZTE4N2VmOWIwOWNhYTViNzExNDA0YzI4MzAiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNC4yNTU2MDkzNjNaIn0sInN5bmFjb3JtZWRpYSI6eyJ1aWQiOiJHRFBSIiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MDkuOTc5NTgzNDM4WiJ9LCJ0cmlwbGVsaWZ0Ijp7InVpZCI6IjcwMjE5NzUwNTQ4MDg4NjUxOTQ2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA4Ljk4OTY3MzU3NFoifSwidmFsdWVpbXByZXNzaW9uIjp7InVpZCI6IjlkMDgxNTVmLWQ5ZmUtNGI1OC04OThlLWUyYzU2MjgyYWIzZSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjA5LjA2NzgzOTE2NFoifSwidmlzeCI6eyJ1aWQiOiIyN2UwYWMzYy1iNDZlLTQxYjMtOTkyYy1mOGQyNzE0OTQ5NWUiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxMi45ODk1MjM1NzNaIn0sInlpZWxkbGFiIjp7InVpZCI6IjY5NzE0ZDlmLWZiMDAtNGE1Zi04MTljLTRiZTE5MTM2YTMyNSIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjExLjMwMzAyNjYxNVoifSwieWllbGRtbyI6eyJ1aWQiOiJnOTZjMmY3MTlmMTU1MWIzMWY2MyIsImV4cGlyZXMiOiIyMDIyLTA2LTI0VDA1OjU5OjEwLjExMDUyODYwOVoifSwieWllbGRvbmUiOnsidWlkIjoiMmE0MmZiZDMtMmM3MC00ZWI5LWIxYmQtMDQ2OTY2NTBkOTQ4IiwiZXhwaXJlcyI6IjIwMjItMDYtMjRUMDU6NTk6MTAuMzE4MzMzOTM5WiJ9LCJ6ZXJvY2xpY2tmcmF1ZCI6eyJ1aWQiOiJiOTk5NThmZS0yYTg3LTJkYTQtOWNjNC05NjFmZDExM2JlY2UiLCJleHBpcmVzIjoiMjAyMi0wNi0yNFQwNTo1OToxNS43MTk1OTQ1NjZaIn19LCJiZGF5IjoiMjAyMi0wNS0xN1QwNjo0ODozOC4wMTc5ODgyMDZaIn0=`, @@ -458,6 +462,7 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { "rctx": models.RequestCtx{ ProfileID: 5890, DisplayID: 1, + DisplayVersionID: 1, SSAuction: -1, Debug: true, UA: "go-test", @@ -516,10 +521,10 @@ func TestOpenWrap_handleEntrypointHook(t *testing.T) { assert.Len(t, gotRctx.LoggerImpressionID, 36) gotRctx.LoggerImpressionID = "" } - gotRctx.ParsedUidCookie = nil // ignore parsed cookies + gotRctx.ParsedUidCookie = nil // ignore parsed cookies + gotRctx.CurrencyConversion = nil // ignore currency-conversion got.ModuleContext["rctx"] = gotRctx } - assert.Equal(t, got, tt.want) }) } diff --git a/modules/pubmatic/openwrap/logger.go b/modules/pubmatic/openwrap/logger.go index 70004f14913..e273c7d6764 100644 --- a/modules/pubmatic/openwrap/logger.go +++ b/modules/pubmatic/openwrap/logger.go @@ -1,6 +1,7 @@ package openwrap import ( + "encoding/json" "fmt" "github.com/prebid/openrtb/v19/openrtb2" @@ -36,10 +37,14 @@ func getIncomingSlots(imp openrtb2.Imp) []string { func getDefaultImpBidCtx(request openrtb2.BidRequest) map[string]models.ImpCtx { impBidCtx := make(map[string]models.ImpCtx) for _, imp := range request.Imp { - incomingSlots := getIncomingSlots(imp) + impExt := &models.ImpExtension{} + json.Unmarshal(imp.Ext, impExt) impBidCtx[imp.ID] = models.ImpCtx{ - IncomingSlots: incomingSlots, + IncomingSlots: getIncomingSlots(imp), + AdUnitName: getAdunitName(imp.TagID, impExt), + SlotName: getSlotName(imp.TagID, impExt), + IsRewardInventory: impExt.Reward, } } return impBidCtx diff --git a/modules/pubmatic/openwrap/matchedimpression.go b/modules/pubmatic/openwrap/matchedimpression.go index e7179857f09..4c6b3e11ce5 100644 --- a/modules/pubmatic/openwrap/matchedimpression.go +++ b/modules/pubmatic/openwrap/matchedimpression.go @@ -1,35 +1,42 @@ package openwrap import ( - "encoding/json" - + "github.com/golang/glog" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" ) -func getMatchedImpression(rctx models.RequestCtx) json.RawMessage { +func getMatchedImpression(rctx models.RequestCtx) map[string]int { cookieFlagMap := make(map[string]int) - for _, partnerConfig := range rctx.PartnerConfigMap { // TODO: original code deos not handle throttled partners + for _, partnerConfig := range rctx.PartnerConfigMap { if partnerConfig[models.SERVER_SIDE_FLAG] != "1" { continue } + syncerMap := models.SyncerMap partnerName := partnerConfig[models.PREBID_PARTNER_NAME] syncerCode := adapters.ResolveOWBidder(partnerName) - status := 0 - if uid, _, _ := rctx.ParsedUidCookie.GetUID(syncerCode); uid != "" { - status = 1 - } - cookieFlagMap[partnerConfig[models.BidderCode]] = status - } + matchedImpression := 0 - matchedImpression, err := json.Marshal(cookieFlagMap) - if err != nil { - return nil - } + syncer := syncerMap[syncerCode] + if syncer == nil { + glog.V(3).Infof("Invalid bidder code passed to ParseRequestCookies: %s ", partnerName) + } else { + uid, _, _ := rctx.ParsedUidCookie.GetUID(syncer.Key()) - return json.RawMessage(matchedImpression) + // Added flag in map for Cookie is present + // we are not considering if the cookie is active + if uid != "" { + matchedImpression = 1 + } + } + cookieFlagMap[partnerConfig[models.BidderCode]] = matchedImpression + if matchedImpression == 0 { + rctx.MetricsEngine.RecordPublisherPartnerNoCookieStats(rctx.PubIDStr, partnerConfig[models.BidderCode]) + } + } + return cookieFlagMap } diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go index 94e439773bf..372df5cfad0 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go @@ -52,6 +52,8 @@ type Metrics struct { dbQueryError *prometheus.CounterVec + loggerFailure *prometheus.CounterVec + //TODO -should we add "prefix" in metrics-name to differentiate it from prebid-core ? // sshb temporary @@ -241,6 +243,12 @@ func newMetrics(cfg *config.PrometheusMetrics, promRegistry *prometheus.Registry []string{queryTypeLabel, pubIDLabel, profileIDLabel}, ) + metrics.loggerFailure = newCounter(cfg, promRegistry, + "logger_send_failed", + "Count of failures to send the logger to analytics endpoint at publisher and profile level", + []string{pubIDLabel, profileIDLabel}, + ) + newSSHBMetrics(&metrics, cfg, promRegistry) return &metrics @@ -435,8 +443,13 @@ func (m *Metrics) RecordDBQueryFailure(queryType, publisher, profile string) { }).Inc() } -// TODO- record logger failure using prebid-core's metric-engine -func (m *Metrics) RecordPublisherWrapperLoggerFailure(publisher, profile, version string) {} +// RecordPublisherWrapperLoggerFailure to record count of owlogger failures +func (m *Metrics) RecordPublisherWrapperLoggerFailure(publisher, profile, version string) { + m.loggerFailure.With(prometheus.Labels{ + pubIDLabel: publisher, + profileIDLabel: profile, + }).Inc() +} // TODO - really need ? func (m *Metrics) RecordPBSAuctionRequestsStats() {} diff --git a/modules/pubmatic/openwrap/models/constants.go b/modules/pubmatic/openwrap/models/constants.go index 58f67e9cfb5..8922673983d 100755 --- a/modules/pubmatic/openwrap/models/constants.go +++ b/modules/pubmatic/openwrap/models/constants.go @@ -337,11 +337,23 @@ const ( Device = "device" DeviceType = "deviceType" + //constant for native tracker + EventTrackers = "eventtrackers" + ImpTrackers = "imptrackers" + Event = "event" + Methods = "methods" + EventValue = "1" + MethodValue = "1" + //constants for Universal Pixel PixelTypeUrl = "url" PixelTypeJS = "js" PixelPosAbove = "above" PixelPosBelow = "below" + + //floor types + SoftFloor = 0 + HardFloor = 1 ) const ( @@ -382,8 +394,12 @@ var ( VASTErrorResponse = `%v` //TrackerCallWrap TrackerCallWrap = `
` + //Tracker Format for Native + NativeTrackerMacro = `{"event":1,"method":1,"url":"${trackerUrl}"}` //TrackerCallWrapOMActive for Open Measurement in In-App Banner TrackerCallWrapOMActive = `` + //Universal Pixel Macro + UniversalPixelMacroForUrl = `
` ) // LogOnlyWinBidArr is an array containing Partners who only want winning bids to be logged @@ -432,7 +448,7 @@ const ( // constants for query_type label in stats const ( - PartnerConfigQuery = "GetParterConfig" + PartnerConfigQuery = "GetPartnerConfig" WrapperSlotMappingsQuery = "GetWrapperSlotMappingsQuery" WrapperLiveVersionSlotMappings = "GetWrapperLiveVersionSlotMappings" AdunitConfigQuery = "GetAdunitConfigQuery" @@ -447,6 +463,15 @@ const ( //PMSlotToMappings = "GetPMSlotToMappings" ) +// constants for owlogger Integration Type +const ( + TypeTag = "tag" + TypeInline = "inline" + TypeAmp = "amp" + TypeSDK = "sdk" + TypeS2S = "s2s" +) + // constants to accept request-test value type testValue = int8 @@ -458,3 +483,9 @@ const ( Success = "success" Failure = "failure" ) + +// constants for imp.Ext.Data fields +const ( + Pbadslot = "pbadslot" + GamAdServer = "gam" +) diff --git a/modules/pubmatic/openwrap/models/openwrap.go b/modules/pubmatic/openwrap/models/openwrap.go index 77f689e0cf2..ce1650f67b5 100644 --- a/modules/pubmatic/openwrap/models/openwrap.go +++ b/modules/pubmatic/openwrap/models/openwrap.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v19/openrtb3" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" "github.com/prebid/prebid-server/openrtb_ext" @@ -12,16 +13,26 @@ import ( ) type RequestCtx struct { - PubID, ProfileID, DisplayID, VersionID int - SSAuction int - SummaryDisable int - LogInfoFlag int - SSAI string - PartnerConfigMap map[int]map[string]string - SupportDeals bool - Platform string - LoggerImpressionID string - ClientConfigFlag int + // PubID is the publisher id retrieved from request + PubID int + // ProfileID is the value received in profileid field in wrapper object. + ProfileID int + // DisplayID is the value received in versionid field in wrapper object. + DisplayID int + // VersionID is the unique id from DB associated with the incoming DisplayID + VersionID int + // DisplayVersionID is the DisplayID of the profile selected by OpenWrap incase DisplayID/versionid is 0 + DisplayVersionID int + + SSAuction int + SummaryDisable int + LogInfoFlag int + SSAI string + PartnerConfigMap map[int]map[string]string + SupportDeals bool + Platform string + LoggerImpressionID string + ClientConfigFlag int IP string TMax int64 @@ -57,8 +68,8 @@ type RequestCtx struct { // imp-bid ctx to avoid computing same thing for bidder params, logger and tracker ImpBidCtx map[string]ImpCtx Aliases map[string]string - NewReqExt json.RawMessage - ResponseExt json.RawMessage + NewReqExt *RequestExt + ResponseExt openrtb_ext.ExtBidResponse MarketPlaceBidders map[string]struct{} AdapterThrottleMap map[string]struct{} @@ -80,12 +91,18 @@ type RequestCtx struct { MetricsEngine metrics.MetricsEngine ReturnAllBidStatus bool // ReturnAllBidStatus stores the value of request.ext.prebid.returnallbidstatus Sshb string //Sshb query param to identify that the request executed heder-bidding or not, sshb=1(executed HB(8001)), sshb=2(reverse proxy set from HB(8001->8000)), sshb=""(direct request(8000)). + + DCName string + CachePutMiss int // to be used in case of CTV JSON endpoint/amp/inapp-ott-video endpoint + CurrencyConversion func(from string, to string, value float64) (float64, error) + MatchedImpression map[string]int } type OwBid struct { ID string NetEcpm float64 BidDealTierSatisfied bool + Nbr *openrtb3.NonBidStatusCode } func (r RequestCtx) GetVersionLevelKey(key string) string { @@ -100,7 +117,11 @@ type ImpCtx struct { ImpID string TagID string Div string + SlotName string + AdUnitName string Secure int + BidFloor float64 + BidFloorCur string IsRewardInventory *int8 Banner bool Video *openrtb2.Video diff --git a/modules/pubmatic/openwrap/models/openwrap_test.go b/modules/pubmatic/openwrap/models/openwrap_test.go index 99d6507e8e8..5ae43cbe1f0 100644 --- a/modules/pubmatic/openwrap/models/openwrap_test.go +++ b/modules/pubmatic/openwrap/models/openwrap_test.go @@ -1,70 +1,12 @@ package models import ( - "encoding/json" - "net/http" "testing" - - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/openrtb_ext" ) func TestRequestCtx_GetVersionLevelKey(t *testing.T) { type fields struct { - PubID int - ProfileID int - DisplayID int - VersionID int - SSAuction int - SummaryDisable int - LogInfoFlag int - SSAI string - PartnerConfigMap map[int]map[string]string - SupportDeals bool - Platform string - LoggerImpressionID string - ClientConfigFlag int - IP string - TMax int64 - IsTestRequest int8 - ABTestConfig int - ABTestConfigApplied int - IsCTVRequest bool - TrackerEndpoint string - VideoErrorTrackerEndpoint string - UA string - Cookies string - UidCookie *http.Cookie - KADUSERCookie *http.Cookie - OriginCookie string - Debug bool - Trace bool - PageURL string - StartTime int64 - DevicePlatform DevicePlatform - Trackers map[string]OWTracker - PrebidBidderCode map[string]string - ImpBidCtx map[string]ImpCtx - Aliases map[string]string - NewReqExt json.RawMessage - ResponseExt json.RawMessage - MarketPlaceBidders map[string]struct{} - AdapterThrottleMap map[string]struct{} - AdUnitConfig *adunitconfig.AdUnitConfig - Source string - Origin string - SendAllBids bool - WinningBids map[string]OwBid - DroppedBids map[string][]openrtb2.Bid - DefaultBids map[string]map[string][]openrtb2.Bid - SeatNonBids map[string][]openrtb_ext.NonBid - BidderResponseTimeMillis map[string]int - Endpoint string - PubIDStr string - ProfileIDStr string - MetricsEngine metrics.MetricsEngine + PartnerConfigMap map[int]map[string]string } type args struct { key string @@ -93,58 +35,7 @@ func TestRequestCtx_GetVersionLevelKey(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := RequestCtx{ - PubID: tt.fields.PubID, - ProfileID: tt.fields.ProfileID, - DisplayID: tt.fields.DisplayID, - VersionID: tt.fields.VersionID, - SSAuction: tt.fields.SSAuction, - SummaryDisable: tt.fields.SummaryDisable, - LogInfoFlag: tt.fields.LogInfoFlag, - SSAI: tt.fields.SSAI, - PartnerConfigMap: tt.fields.PartnerConfigMap, - SupportDeals: tt.fields.SupportDeals, - Platform: tt.fields.Platform, - LoggerImpressionID: tt.fields.LoggerImpressionID, - ClientConfigFlag: tt.fields.ClientConfigFlag, - IP: tt.fields.IP, - TMax: tt.fields.TMax, - IsTestRequest: tt.fields.IsTestRequest, - ABTestConfig: tt.fields.ABTestConfig, - ABTestConfigApplied: tt.fields.ABTestConfigApplied, - IsCTVRequest: tt.fields.IsCTVRequest, - TrackerEndpoint: tt.fields.TrackerEndpoint, - VideoErrorTrackerEndpoint: tt.fields.VideoErrorTrackerEndpoint, - UA: tt.fields.UA, - Cookies: tt.fields.Cookies, - UidCookie: tt.fields.UidCookie, - KADUSERCookie: tt.fields.KADUSERCookie, - OriginCookie: tt.fields.OriginCookie, - Debug: tt.fields.Debug, - Trace: tt.fields.Trace, - PageURL: tt.fields.PageURL, - StartTime: tt.fields.StartTime, - DevicePlatform: tt.fields.DevicePlatform, - Trackers: tt.fields.Trackers, - PrebidBidderCode: tt.fields.PrebidBidderCode, - ImpBidCtx: tt.fields.ImpBidCtx, - Aliases: tt.fields.Aliases, - NewReqExt: tt.fields.NewReqExt, - ResponseExt: tt.fields.ResponseExt, - MarketPlaceBidders: tt.fields.MarketPlaceBidders, - AdapterThrottleMap: tt.fields.AdapterThrottleMap, - AdUnitConfig: tt.fields.AdUnitConfig, - Source: tt.fields.Source, - Origin: tt.fields.Origin, - SendAllBids: tt.fields.SendAllBids, - WinningBids: tt.fields.WinningBids, - DroppedBids: tt.fields.DroppedBids, - DefaultBids: tt.fields.DefaultBids, - SeatNonBids: tt.fields.SeatNonBids, - BidderResponseTimeMillis: tt.fields.BidderResponseTimeMillis, - Endpoint: tt.fields.Endpoint, - PubIDStr: tt.fields.PubIDStr, - ProfileIDStr: tt.fields.ProfileIDStr, - MetricsEngine: tt.fields.MetricsEngine, + PartnerConfigMap: tt.fields.PartnerConfigMap, } if got := r.GetVersionLevelKey(tt.args.key); got != tt.want { t.Errorf("RequestCtx.GetVersionLevelKey() = %v, want %v", got, tt.want) diff --git a/modules/pubmatic/openwrap/models/reponse.go b/modules/pubmatic/openwrap/models/reponse.go index 68067594b8f..a64346ccc52 100644 --- a/modules/pubmatic/openwrap/models/reponse.go +++ b/modules/pubmatic/openwrap/models/reponse.go @@ -4,6 +4,7 @@ import ( "encoding/json" "github.com/prebid/openrtb/v19/adcom1" + "github.com/prebid/openrtb/v19/openrtb3" "github.com/prebid/prebid-server/openrtb_ext" ) @@ -23,10 +24,11 @@ type BidExt struct { Winner int `json:"winner,omitempty"` NetECPM float64 `json:"netecpm,omitempty"` - OriginalBidCPM float64 `json:"origbidcpm,omitempty"` - OriginalBidCur string `json:"origbidcur,omitempty"` - OriginalBidCPMUSD float64 `json:"origbidcpmusd,omitempty"` - Fsc int `json:"fsc,omitempty"` + OriginalBidCPM float64 `json:"origbidcpm,omitempty"` + OriginalBidCur string `json:"origbidcur,omitempty"` + OriginalBidCPMUSD float64 `json:"origbidcpmusd,omitempty"` + Nbr *openrtb3.NonBidStatusCode `json:"-"` // Reason for not bidding + Fsc int `json:"fsc,omitempty"` } // ExtBidVideo defines the contract for bidresponse.seatbid.bid[i].ext.video diff --git a/modules/pubmatic/openwrap/models/request.go b/modules/pubmatic/openwrap/models/request.go index e8f454e8e15..c2ed52b042a 100644 --- a/modules/pubmatic/openwrap/models/request.go +++ b/modules/pubmatic/openwrap/models/request.go @@ -42,8 +42,8 @@ type ImpExtension struct { SKAdnetwork json.RawMessage `json:"skadn,omitempty"` Data openrtb_ext.ExtImpData `json:"data,omitempty"` + GpId string `json:"gpid,omitempty"` Prebid openrtb_ext.ExtImpPrebid `json:"prebid,omitempty"` - Gpid string `json:"gpid,omitempty"` } // BidderExtension - Bidder specific items diff --git a/modules/pubmatic/openwrap/models/tracker.go b/modules/pubmatic/openwrap/models/tracker.go index cdd7dca58c9..fd4b2ac7150 100644 --- a/modules/pubmatic/openwrap/models/tracker.go +++ b/modules/pubmatic/openwrap/models/tracker.go @@ -26,10 +26,17 @@ type Tracker struct { RewardedInventory int SURL string // contains either req.site.domain or req.app.bundle value Platform int - Advertiser string // SSAI identifies the name of the SSAI vendor // Applicable only in case of incase of video/json endpoint. - SSAI string + SSAI string + AdPodSlot int + TestGroup int + Origin string + FloorSkippedFlag *int + FloorModelVersion string + FloorSource *int + FloorType int + LoggerData LoggerData // need this in logger to avoid duplicate computation ImpID string `json:"-"` Secure int `json:"-"` @@ -37,11 +44,36 @@ type Tracker struct { // Partner partner information to be logged in tracker object type Partner struct { - PartnerID string - BidderCode string - KGPV string - GrossECPM float64 - NetECPM float64 - BidID string - OrigBidID string + PartnerID string + BidderCode string + KGPV string + GrossECPM float64 + NetECPM float64 + BidID string + OrigBidID string + AdSize string + AdDuration int + Adformat string + ServerSide int + Advertiser string + FloorValue float64 + FloorRuleValue float64 + DealID string +} + +// LoggerData: this data to be needed in logger +type LoggerData struct { + KGPSV string + FloorProvider string + FloorFetchStatus *int +} + +// FloorsDetails contains floors info derived from responseExt.Prebid.Floors +type FloorsDetails struct { + FloorType int + FloorModelVersion string + FloorProvider string + Skipfloors *int + FloorFetchStatus *int + FloorSource *int } diff --git a/modules/pubmatic/openwrap/models/tracking.go b/modules/pubmatic/openwrap/models/tracking.go index 047a74fae36..a1f880cc34c 100644 --- a/modules/pubmatic/openwrap/models/tracking.go +++ b/modules/pubmatic/openwrap/models/tracking.go @@ -1,5 +1,7 @@ package models +import "github.com/prebid/prebid-server/openrtb_ext" + // impression tracker url parameters const ( // constants for query parameter names for tracker call @@ -24,6 +26,23 @@ const ( TRKQMARK = "?" TRKAmpersand = "&" TRKSSAI = "ssai" + TRKPlatform = "plt" + TRKAdSize = "psz" + TRKTestGroup = "tgid" + TRKAdvertiser = "adv" + TRKPubDomain = "orig" + TRKServerSide = "ss" + TRKAdformat = "af" + TRKAdDuration = "dur" + TRKAdPodExist = "aps" + TRKFloorType = "ft" + TRKFloorModelVersion = "fmv" + TRKFloorSkippedFlag = "fskp" + TRKFloorSource = "fsrc" + TRKFloorValue = "fv" + TRKFloorRuleValue = "frv" + TRKServerLogger = "sl" + TRKDealID = "di" ) // video error tracker url parameters @@ -64,3 +83,23 @@ const ( const ( DspId_DV360 = 80 ) + +var FloorSourceMap = map[string]int{ + openrtb_ext.NoDataLocation: 0, + openrtb_ext.RequestLocation: 1, + openrtb_ext.FetchLocation: 2, +} + +// FetchStatusMap maps floor fetch status with integer codes +var FetchStatusMap = map[string]int{ + openrtb_ext.FetchNone: 0, + openrtb_ext.FetchSuccess: 1, + openrtb_ext.FetchError: 2, + openrtb_ext.FetchInprogress: 3, + openrtb_ext.FetchTimeout: 4, +} + +const ( + NotSet = -1 + DealIDAbsent = "-1" +) diff --git a/modules/pubmatic/openwrap/models/utils.go b/modules/pubmatic/openwrap/models/utils.go index 46bbb08dacb..5e1864eb98a 100644 --- a/modules/pubmatic/openwrap/models/utils.go +++ b/modules/pubmatic/openwrap/models/utils.go @@ -13,10 +13,18 @@ import ( "github.com/buger/jsonparser" "github.com/pkg/errors" + "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/usersync" + "github.com/prebid/prebid-server/util/ptrutil" ) +var videoRegex *regexp.Regexp + +func init() { + videoRegex, _ = regexp.Compile(" 0 { + return string(bidExt.Prebid.Type) + } + if bid.AdM == "" { + return "" + } + if videoRegex.MatchString(bid.AdM) { + return Video + } + if impCtx.Native != nil { var admJSON map[string]interface{} - err := json.Unmarshal([]byte(strings.Replace(adm, "/\\/g", "", -1)), &admJSON) + err := json.Unmarshal([]byte(strings.Replace(bid.AdM, "/\\/g", "", -1)), &admJSON) if err == nil && admJSON != nil && admJSON["native"] != nil { - adFormat = Native + return Native } } - return adFormat + return Banner +} + +func IsDefaultBid(bid *openrtb2.Bid) bool { + return bid.Price == 0 && bid.DealID == "" && bid.W == 0 && bid.H == 0 +} + +// GetAdFormat returns adformat of the bid. +// for default bid it refers to impression object +// for non-default bids it uses creative(adm) of the bid +func GetAdFormat(bid *openrtb2.Bid, bidExt *BidExt, impCtx *ImpCtx) string { + if bid == nil || impCtx == nil { + return "" + } + if IsDefaultBid(bid) { + if impCtx.Banner { + return Banner + } + if impCtx.Video != nil { + return Video + } + if impCtx.Native != nil { + return Native + } + return "" + } + if bidExt == nil { + return "" + } + return GetCreativeType(bid, bidExt, impCtx) } func GetRevenueShare(partnerConfig map[string]string) float64 { @@ -218,3 +259,157 @@ func ErrorWrap(cErr, nErr error) error { return errors.Wrap(cErr, nErr.Error()) } + +func GetSizeForPlatform(width, height int64, platform string) string { + s := fmt.Sprintf("%dx%d", width, height) + if platform == PLATFORM_VIDEO { + s = s + VideoSizeSuffix + } + return s +} + +func GetKGPSV(bid openrtb2.Bid, bidderMeta PartnerData, adformat string, tagId string, div string, source string) (string, string) { + kgpv := bidderMeta.KGPV + kgpsv := bidderMeta.MatchedSlot + isRegex := bidderMeta.IsRegex + // 1. nobid + if IsDefaultBid(&bid) { + //NOTE: kgpsv = bidderMeta.MatchedSlot above. Use the same + if !isRegex && kgpv != "" { // unmapped pubmatic's slot + kgpsv = kgpv + } else if !isRegex { + kgpv = kgpsv + } + } else if !isRegex { + if kgpv != "" { // unmapped pubmatic's slot + kgpsv = kgpv + } else if adformat == Video { // Check when adformat is video, bid.W and bid.H has to be zero with Price !=0. Ex: UOE-9222(0x0 default kgpv and kgpsv for video bid) + // 2. valid video bid + // kgpv has regex, do not generate slotName again + // kgpsv could be unmapped or mapped slot, generate slotName with bid.W = bid.H = 0 + kgpsv = GenerateSlotName(0, 0, bidderMeta.KGP, tagId, div, source) + kgpv = kgpsv // original /43743431/DMDemo1234@300x250 but new could be /43743431/DMDemo1234@0x0 + } else if bid.H != 0 && bid.W != 0 { // Check when bid.H and bid.W will be zero with Price !=0. Ex: MobileInApp-MultiFormat-OnlyBannerMapping_Criteo_Partner_Validaton + // 3. valid bid + // kgpv has regex, do not generate slotName again + // kgpsv could be unmapped or mapped slot, generate slotName again based on bid.H and bid.W + kgpsv = GenerateSlotName(bid.H, bid.W, bidderMeta.KGP, tagId, div, source) + kgpv = kgpsv + } + } + if kgpv == "" { + kgpv = kgpsv + } + return kgpv, kgpsv +} + +// Harcode would be the optimal. We could make it configurable like _AU_@_W_x_H_:%s@%dx%d entries in pbs.yaml +// mysql> SELECT DISTINCT key_gen_pattern FROM wrapper_mapping_template; +// +----------------------+ +// | key_gen_pattern | +// +----------------------+ +// | _AU_@_W_x_H_ | +// | _DIV_@_W_x_H_ | +// | _W_x_H_@_W_x_H_ | +// | _DIV_ | +// | _AU_@_DIV_@_W_x_H_ | +// | _AU_@_SRC_@_VASTTAG_ | +// +----------------------+ +// 6 rows in set (0.21 sec) +func GenerateSlotName(h, w int64, kgp, tagid, div, src string) string { + // func (H, W, Div), no need to validate, will always be non-nil + switch kgp { + case "_AU_": // adunitconfig + return tagid + case "_DIV_": + return div + case "_AU_@_W_x_H_": + return fmt.Sprintf("%s@%dx%d", tagid, w, h) + case "_DIV_@_W_x_H_": + return fmt.Sprintf("%s@%dx%d", div, w, h) + case "_W_x_H_@_W_x_H_": + return fmt.Sprintf("%dx%d@%dx%d", w, h, w, h) + case "_AU_@_DIV_@_W_x_H_": + return fmt.Sprintf("%s@%s@%dx%d", tagid, div, w, h) + case "_AU_@_SRC_@_VASTTAG_": + return fmt.Sprintf("%s@%s@_VASTTAG_", tagid, src) //TODO check where/how _VASTTAG_ is updated + default: + // TODO: check if we need to fallback to old generic flow (below) + // Add this cases in a map and read it from yaml file + } + return "" +} + +func RoundToTwoDigit(value float64) float64 { + output := math.Pow(10, float64(2)) + return float64(math.Round(value*output)) / output +} + +// GetBidLevelFloorsDetails return floorvalue and floorrulevalue +func GetBidLevelFloorsDetails(bidExt BidExt, impCtx ImpCtx, + currencyConversion func(from, to string, value float64) (float64, error)) (fv, frv float64) { + var floorCurrency string + frv = NotSet + + if bidExt.Prebid != nil && bidExt.Prebid.Floors != nil { + floorCurrency = bidExt.Prebid.Floors.FloorCurrency + fv = RoundToTwoDigit(bidExt.Prebid.Floors.FloorValue) + frv = fv + if bidExt.Prebid.Floors.FloorRuleValue > 0 { + frv = RoundToTwoDigit(bidExt.Prebid.Floors.FloorRuleValue) + } + } + + // if floor values are not set from bid.ext then fall back to imp.bidfloor + if frv == NotSet && impCtx.BidFloor != 0 { + fv = RoundToTwoDigit(impCtx.BidFloor) + frv = fv + floorCurrency = impCtx.BidFloorCur + } + + // convert the floor values in USD currency + if floorCurrency != "" && floorCurrency != USD { + value, _ := currencyConversion(floorCurrency, USD, fv) + fv = RoundToTwoDigit(value) + value, _ = currencyConversion(floorCurrency, USD, frv) + frv = RoundToTwoDigit(value) + } + + if frv == NotSet { + frv = 0 // set it back to 0 + } + + return +} + +// GetFloorsDetails returns floors details from response.ext.prebid +func GetFloorsDetails(responseExt openrtb_ext.ExtBidResponse) (floorDetails FloorsDetails) { + if responseExt.Prebid != nil && responseExt.Prebid.Floors != nil { + floors := responseExt.Prebid.Floors + if floors.Skipped != nil { + floorDetails.Skipfloors = ptrutil.ToPtr(0) + if *floors.Skipped { + floorDetails.Skipfloors = ptrutil.ToPtr(1) + } + } + if floors.Data != nil && len(floors.Data.ModelGroups) > 0 { + floorDetails.FloorModelVersion = floors.Data.ModelGroups[0].ModelVersion + } + if len(floors.PriceFloorLocation) > 0 { + if source, ok := FloorSourceMap[floors.PriceFloorLocation]; ok { + floorDetails.FloorSource = &source + } + } + if status, ok := FetchStatusMap[floors.FetchStatus]; ok { + floorDetails.FloorFetchStatus = &status + } + floorDetails.FloorProvider = floors.FloorProvider + if floors.Data != nil && len(floors.Data.FloorProvider) > 0 { + floorDetails.FloorProvider = floors.Data.FloorProvider + } + if floors.Enforcement != nil && floors.Enforcement.EnforcePBS != nil && *floors.Enforcement.EnforcePBS { + floorDetails.FloorType = HardFloor + } + } + return floorDetails +} diff --git a/modules/pubmatic/openwrap/models/utils_test.go b/modules/pubmatic/openwrap/models/utils_test.go index e64a2338adc..fe3870580f4 100644 --- a/modules/pubmatic/openwrap/models/utils_test.go +++ b/modules/pubmatic/openwrap/models/utils_test.go @@ -3,6 +3,11 @@ package models import ( "fmt" "testing" + + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/util/ptrutil" + "github.com/stretchr/testify/assert" ) func TestErrorWrap(t *testing.T) { @@ -40,3 +45,1289 @@ func TestErrorWrap(t *testing.T) { }) } } + +func TestGetCreativeType(t *testing.T) { + type args struct { + bid *openrtb2.Bid + bidExt *BidExt + impCtx *ImpCtx + } + tests := []struct { + name string + args args + want string + }{ + { + name: "bid.ext.prebid.type absent", + args: args{ + bid: &openrtb2.Bid{}, + bidExt: &BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{}, + }, + }, + impCtx: &ImpCtx{}, + }, + want: "", + }, + { + name: "bid.ext.prebid.type empty", + args: args{ + bid: &openrtb2.Bid{}, + bidExt: &BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Type: "", + }, + }, + }, + impCtx: &ImpCtx{}, + }, + want: "", + }, + { + name: "bid.ext.prebid.type is banner", + args: args{ + bid: &openrtb2.Bid{}, + bidExt: &BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Type: Banner, + }, + }, + }, + impCtx: &ImpCtx{}, + }, + want: Banner, + }, + { + name: "bid.ext.prebid.type is video", + args: args{ + bid: &openrtb2.Bid{}, + bidExt: &BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Type: Video, + }, + }, + }, + impCtx: &ImpCtx{}, + }, + want: Video, + }, + { + name: "Banner Adm has json assets keyword", + args: args{ + bid: &openrtb2.Bid{ + AdM: `u003cscript src='mraid.js'>`, + }, + bidExt: &BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{}, + }, + }, + impCtx: &ImpCtx{}, + }, + want: Banner, + }, + { + name: "Empty Bid Adm", + args: args{ + bid: &openrtb2.Bid{ + AdM: "", + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: "", + }, + { + name: "VAST Ad", + args: args{ + bid: &openrtb2.Bid{ + AdM: "Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1http://172.16.4.213/AdServer/AdDisplayTrackerServlethttps://dsptracker.com/{PSPM}http://172.16.4.213/trackhttps://Errortrack.com00:00:04http://172.16.4.213/trackhttps://www.pubmatic.comhttps://stagingnyc.pubmatic.com:8443/video/Shashank/mediaFileHost/media/mp4-sample-1.mp4]https://stagingnyc.pubmatic.com:8443/video/Shashank/mediaFileHost/media/mp4-sample-2.mp4]", + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: Video, + }, + { + name: "VAST Ad xml", + args: args{ + bid: &openrtb2.Bid{ + AdM: "adnxs", + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: Video, + }, + { + name: "Banner Ad", + args: args{ + bid: &openrtb2.Bid{ + AdM: "
", + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: Banner, + }, + { + name: "Native Adm with `native` Object", + args: args{ + bid: &openrtb2.Bid{ + AdM: `{"native":{"ver":1.2,"link":{"url":"https://dummyimage.com/1x1/000000/fff.jpg&text=420x420+Creative.jpg","clicktrackers":["http://image3.pubmatic.com/AdServer/layer?a={PUBMATIC_SECOND_PRICE}&ucrid=9335447642416814892&t=FNOZW09VkdSTVM0eU5BPT09JmlkPTAmY2lkPTIyNzcyJnhwcj0xLjAwMDAwMCZmcD00JnBwPTIuMzcxMiZ0cD0yJnBlPTAuMDA9=","http://image3.pubmatic.com/AdServer/layer?a={PUBMATIC_SECOND_PRICE}&ucrid=9335447642416814892&t=FNOZW09VkdSTVM0eU5BPT09JmlkPTAmY2lkPTIyNzcyJnhwcj0xLjAwMDAwMCZmcD00JnBwPTIuMzcxMiZ0cD0yJnBlPTAuMDA9="]},"eventtrackers":[{"event":1,"method":1,"url":"http://image3.pubmatic.com/AdServer/layer?a={PUBMATIC_SECOND_PRICE}&ucrid=9335447642416814892&t=FNOZW09VkdSTVM0eU5BPT09JmlkPTAmY2lkPTIyNzcyJnhwcj0xLjAwMDAwMCZmcD00JnBwPTIuMzcxMiZ0cD0yJnBlPTAuMDA9="}]}}`, + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{ + Native: &openrtb2.Native{}, + }, + }, + want: Native, + }, + { + name: "Native Adm with `native` and `assets` Object", + args: args{ + bid: &openrtb2.Bid{ + AdM: "{\"native\":{\"assets\":[{\"id\":1,\"required\":0,\"title\":{\"text\":\"Lexus - Luxury vehicles company\"}},{\"id\":2,\"img\":{\"h\":150,\"url\":\"https://stagingnyc.pubmatic.com:8443//sdk/lexus_logo.png\",\"w\":150},\"required\":0},{\"id\":3,\"img\":{\"h\":428,\"url\":\"https://stagingnyc.pubmatic.com:8443//sdk/28f48244cafa0363b03899f267453fe7%20copy.png\",\"w\":214},\"required\":0},{\"data\":{\"value\":\"Goto PubMatic\"},\"id\":4,\"required\":0},{\"data\":{\"value\":\"Lexus - Luxury vehicles company\"},\"id\":5,\"required\":0},{\"data\":{\"value\":\"4\"},\"id\":6,\"required\":0}],\"imptrackers\":[\"http://phtrack.pubmatic.com/?ts=1496043362&r=84137f17-eefd-4f06-8380-09138dc616e6&i=c35b1240-a0b3-4708-afca-54be95283c61&a=130917&t=9756&au=10002949&p=&c=10014299&o=10002476&wl=10009731&ty=1\"],\"link\":{\"clicktrackers\":[\"http://ct.pubmatic.com/track?ts=1496043362&r=84137f17-eefd-4f06-8380-09138dc616e6&i=c35b1240-a0b3-4708-afca-54be95283c61&a=130917&t=9756&au=10002949&p=&c=10014299&o=10002476&wl=10009731&ty=3&url=\"],\"url\":\"http://www.lexus.com/\"},\"ver\":1}}", + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{ + Native: &openrtb2.Native{}, + }, + }, + want: Native, + }, + { + name: "Native Adm with `native` Object but Native is missing in impCtx", + args: args{ + bid: &openrtb2.Bid{ + AdM: `{"native":{"ver":1.2,"link":{"url":"https://dummyimage.com/1x1/000000/fff.jpg&text=420x420+Creative.jpg","clicktrackers":["http://image3.pubmatic.com/AdServer/layer?a={PUBMATIC_SECOND_PRICE}&ucrid=9335447642416814892&t=FNOZW09VkdSTVM0eU5BPT09JmlkPTAmY2lkPTIyNzcyJnhwcj0xLjAwMDAwMCZmcD00JnBwPTIuMzcxMiZ0cD0yJnBlPTAuMDA9=","http://image3.pubmatic.com/AdServer/layer?a={PUBMATIC_SECOND_PRICE}&ucrid=9335447642416814892&t=FNOZW09VkdSTVM0eU5BPT09JmlkPTAmY2lkPTIyNzcyJnhwcj0xLjAwMDAwMCZmcD00JnBwPTIuMzcxMiZ0cD0yJnBlPTAuMDA9="]},"eventtrackers":[{"event":1,"method":1,"url":"http://image3.pubmatic.com/AdServer/layer?a={PUBMATIC_SECOND_PRICE}&ucrid=9335447642416814892&t=FNOZW09VkdSTVM0eU5BPT09JmlkPTAmY2lkPTIyNzcyJnhwcj0xLjAwMDAwMCZmcD00JnBwPTIuMzcxMiZ0cD0yJnBlPTAuMDA9="}]}}`, + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: Banner, + }, + { + name: "Video Adm \t", + args: args{ + bid: &openrtb2.Bid{ + AdM: "", + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: Video, + }, + { + name: "Video Adm \r", + args: args{ + bid: &openrtb2.Bid{ + AdM: "", + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: Video, + }, + { + name: "Video AdM \n", + args: args{ + bid: &openrtb2.Bid{ + AdM: "", + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: Video, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + creativeType := GetCreativeType(tt.args.bid, tt.args.bidExt, tt.args.impCtx) + assert.Equal(t, tt.want, creativeType, tt.name) + }) + } +} + +func TestGetAdFormat(t *testing.T) { + type args struct { + bid *openrtb2.Bid + bidExt *BidExt + impCtx *ImpCtx + } + tests := []struct { + name string + args args + want string + }{ + { + name: "no bid object", + args: args{ + bid: nil, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: "", + }, + { + name: "no bidExt object", + args: args{ + bid: nil, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: "", + }, + { + name: "no impctx object", + args: args{ + bid: nil, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: "", + }, + { + name: "Default bid and banner present", + args: args{ + bid: &openrtb2.Bid{ + DealID: "", + Price: 0, + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{ + Banner: true, + }, + }, + want: Banner, + }, + { + name: "Default bid and video present", + args: args{ + bid: &openrtb2.Bid{ + DealID: "", + Price: 0, + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{ + Video: &openrtb2.Video{}, + }, + }, + want: Video, + }, + { + name: "Default bid and native present", + args: args{ + bid: &openrtb2.Bid{ + DealID: "", + Price: 0, + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{ + Native: &openrtb2.Native{}, + }, + }, + want: Native, + }, + { + name: "Default bid and banner and video and native present", + args: args{ + bid: &openrtb2.Bid{ + DealID: "", + Price: 0, + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{ + Banner: true, + Native: &openrtb2.Native{}, + Video: &openrtb2.Video{}, + }, + }, + want: Banner, + }, + { + name: "Default bid and none of banner/video/native present", + args: args{ + bid: &openrtb2.Bid{ + DealID: "", + Price: 0, + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: "", + }, + { + name: "Empty Bid Adm", + args: args{ + bid: &openrtb2.Bid{ + Price: 10, + AdM: "", + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: "", + }, + { + name: "VAST Ad", + args: args{ + bid: &openrtb2.Bid{ + DealID: "dl", + AdM: "Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1http://172.16.4.213/AdServer/AdDisplayTrackerServlethttps://dsptracker.com/{PSPM}http://172.16.4.213/trackhttps://Errortrack.com00:00:04http://172.16.4.213/trackhttps://www.pubmatic.comhttps://stagingnyc.pubmatic.com:8443/video/Shashank/mediaFileHost/media/mp4-sample-1.mp4]https://stagingnyc.pubmatic.com:8443/video/Shashank/mediaFileHost/media/mp4-sample-2.mp4]", + }, + bidExt: &BidExt{}, + impCtx: &ImpCtx{}, + }, + want: Video, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + creativeType := GetAdFormat(tt.args.bid, tt.args.bidExt, tt.args.impCtx) + assert.Equal(t, tt.want, creativeType, tt.name) + }) + } +} + +func TestGetSizeForPlatform(t *testing.T) { + type args struct { + width, height int64 + platform string + } + tests := []struct { + name string + args args + size string + }{ + { + name: "in-app platform", + args: args{ + width: 100, + height: 10, + platform: PLATFORM_APP, + }, + size: "100x10", + }, + { + name: "video platform", + args: args{ + width: 100, + height: 10, + platform: PLATFORM_VIDEO, + }, + size: "100x10v", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + size := GetSizeForPlatform(tt.args.width, tt.args.height, tt.args.platform) + assert.Equal(t, tt.size, size, tt.name) + }) + } +} + +func TestGenerateSlotName(t *testing.T) { + type args struct { + h int64 + w int64 + kgp string + tagid string + div string + src string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "_AU_", + args: args{ + h: 100, + w: 200, + kgp: "_AU_", + tagid: "/15671365/Test_Adunit", + div: "Div1", + src: "test.com", + }, + want: "/15671365/Test_Adunit", + }, + { + name: "_DIV_", + args: args{ + h: 100, + w: 200, + kgp: "_DIV_", + tagid: "/15671365/Test_Adunit", + div: "Div1", + src: "test.com", + }, + want: "Div1", + }, + { + name: "_AU_", + args: args{ + h: 100, + w: 200, + kgp: "_AU_", + tagid: "/15671365/Test_Adunit", + div: "Div1", + src: "test.com", + }, + want: "/15671365/Test_Adunit", + }, + { + name: "_AU_@_W_x_H_", + args: args{ + h: 100, + w: 200, + kgp: "_AU_@_W_x_H_", + tagid: "/15671365/Test_Adunit", + div: "Div1", + src: "test.com", + }, + want: "/15671365/Test_Adunit@200x100", + }, + { + name: "_DIV_@_W_x_H_", + args: args{ + h: 100, + w: 200, + kgp: "_DIV_@_W_x_H_", + tagid: "/15671365/Test_Adunit", + div: "Div1", + src: "test.com", + }, + want: "Div1@200x100", + }, + { + name: "_W_x_H_@_W_x_H_", + args: args{ + h: 100, + w: 200, + kgp: "_W_x_H_@_W_x_H_", + tagid: "/15671365/Test_Adunit", + div: "Div1", + src: "test.com", + }, + want: "200x100@200x100", + }, + { + name: "_AU_@_DIV_@_W_x_H_", + args: args{ + h: 100, + w: 200, + kgp: "_AU_@_DIV_@_W_x_H_", + tagid: "/15671365/Test_Adunit", + div: "Div1", + src: "test.com", + }, + want: "/15671365/Test_Adunit@Div1@200x100", + }, + { + name: "_AU_@_SRC_@_VASTTAG_", + args: args{ + h: 100, + w: 200, + kgp: "_AU_@_SRC_@_VASTTAG_", + tagid: "/15671365/Test_Adunit", + div: "Div1", + src: "test.com", + }, + want: "/15671365/Test_Adunit@test.com@_VASTTAG_", + }, + { + name: "empty_kgp", + args: args{ + h: 100, + w: 200, + kgp: "", + tagid: "/15671365/Test_Adunit", + div: "Div1", + src: "test.com", + }, + want: "", + }, + { + name: "random_kgp", + args: args{ + h: 100, + w: 200, + kgp: "fjkdfhk", + tagid: "/15671365/Test_Adunit", + div: "Div1", + src: "test.com", + }, + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := GenerateSlotName(tt.args.h, tt.args.w, tt.args.kgp, tt.args.tagid, tt.args.div, tt.args.src) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestGetRevenueShare(t *testing.T) { + tests := []struct { + name string + partnerConfig map[string]string + revshare float64 + }{ + { + name: "Empty partnerConfig", + partnerConfig: make(map[string]string), + revshare: 0, + }, + { + name: "partnerConfig without rev_share", + partnerConfig: map[string]string{ + "anykey": "anyval", + }, + revshare: 0, + }, + { + name: "partnerConfig with invalid rev_share", + partnerConfig: map[string]string{ + REVSHARE: "invalid", + }, + revshare: 0, + }, + { + name: "partnerConfig with valid rev_share", + partnerConfig: map[string]string{ + REVSHARE: "10", + }, + revshare: 10, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + revshare := GetRevenueShare(tt.partnerConfig) + assert.Equal(t, tt.revshare, revshare, tt.name) + }) + } +} + +func TestGetNetEcpm(t *testing.T) { + type args struct { + price, revShare float64 + } + tests := []struct { + name string + args args + netecpm float64 + }{ + { + name: "revshare is 0", + args: args{ + revShare: 0, + price: 10, + }, + netecpm: 10, + }, + { + name: "revshare is int", + args: args{ + revShare: 2, + price: 2, + }, + netecpm: 1.96, + }, + { + name: "revshare is float", + args: args{ + revShare: 3.338, + price: 100, + }, + netecpm: 96.66, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + netecpm := GetNetEcpm(tt.args.price, tt.args.revShare) + assert.Equal(t, tt.netecpm, netecpm, tt.name) + }) + } +} + +func TestGetGrossEcpm(t *testing.T) { + + tests := []struct { + name string + price float64 + grossecpm float64 + }{ + { + name: "grossecpm ceiling", + price: 18.998, + grossecpm: 19, + }, + { + name: "grossecpm floor", + price: 18.901, + grossecpm: 18.90, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + grossecpm := GetGrossEcpm(tt.price) + assert.Equal(t, tt.grossecpm, grossecpm, tt.name) + }) + } +} + +func TestExtractDomain(t *testing.T) { + type want struct { + domain string + err bool + } + tests := []struct { + name string + url string + want want + }{ + { + name: "url without http prefix", + url: "google.com", + want: want{ + domain: "google.com", + }, + }, + { + name: "url with http prefix", + url: "http://google.com", + want: want{ + domain: "google.com", + }, + }, + { + name: "url with https prefix", + url: "https://google.com", + want: want{ + domain: "google.com", + }, + }, + { + name: "invalid", + url: "https://google:com?a=1;b=2", + want: want{ + domain: "", + err: true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + domain, err := ExtractDomain(tt.url) + assert.Equal(t, tt.want.domain, domain, tt.name) + assert.Equal(t, tt.want.err, err != nil, tt.name) + }) + } +} + +func TestGetBidLevelFloorsDetails(t *testing.T) { + type args struct { + bidExt BidExt + impCtx ImpCtx + currencyConversion func(from, to string, value float64) (float64, error) + } + type want struct { + fv, frv float64 + } + tests := []struct { + name string + args args + want want + }{ + { + name: "set_floor_values_from_bidExt", + args: args{ + bidExt: BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Floors: &openrtb_ext.ExtBidPrebidFloors{ + FloorRuleValue: 10, + FloorValue: 5, + }, + }, + }, + }, + impCtx: ImpCtx{ + BidFloor: 2.2, + BidFloorCur: "EUR", + }, + }, + want: want{ + fv: 5, + frv: 10, + }, + }, + { + name: "frv_absent_in_bidExt", + args: args{ + bidExt: BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Floors: &openrtb_ext.ExtBidPrebidFloors{ + FloorValue: 5, + }, + }, + }, + }, + impCtx: ImpCtx{ + BidFloor: 2.2, + BidFloorCur: "EUR", + }, + }, + want: want{ + fv: 5, + frv: 5, + }, + }, + { + name: "fv_is_0_in_bidExt", + args: args{ + bidExt: BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Floors: &openrtb_ext.ExtBidPrebidFloors{ + FloorValue: 0, + }, + }, + }, + }, + impCtx: ImpCtx{ + BidFloor: 2.2, + BidFloorCur: "EUR", + }, + }, + want: want{ + fv: 0, + frv: 0, + }, + }, + { + name: "currency_conversion_for_floor_values_in_bidExt", + args: args{ + bidExt: BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Floors: &openrtb_ext.ExtBidPrebidFloors{ + FloorValue: 5, + FloorCurrency: "EUR", + }, + }, + }, + }, + impCtx: ImpCtx{ + BidFloor: 2.2, + BidFloorCur: "EUR", + }, + currencyConversion: func(from, to string, value float64) (float64, error) { + return 10, nil + }, + }, + want: want{ + fv: 10, + frv: 10, + }, + }, + { + name: "floor_values_missing_in_bidExt_fallback_to_impctx", + args: args{ + bidExt: BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{}, + }, + }, + impCtx: ImpCtx{ + BidFloor: 2.2, + BidFloorCur: "USD", + }, + }, + want: want{ + fv: 2.2, + frv: 2.2, + }, + }, + { + name: "bidExt.Prebid_is_nil_fallback_to_impctx", + args: args{ + bidExt: BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: nil, + }, + }, + impCtx: ImpCtx{ + BidFloor: 2.2, + BidFloorCur: "USD", + }, + }, + want: want{ + fv: 2.2, + frv: 2.2, + }, + }, + { + name: "bidExt.Prebid.Floors_is_nil_fallback_to_impctx", + args: args{ + bidExt: BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + Floors: nil, + }, + }, + }, + impCtx: ImpCtx{ + BidFloor: 2.2, + BidFloorCur: "USD", + }, + }, + want: want{ + fv: 2.2, + frv: 2.2, + }, + }, + { + name: "currency_conversion_for_floor_values_in_impctx", + args: args{ + bidExt: BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + }, + impCtx: ImpCtx{ + BidFloor: 5, + BidFloorCur: "EUR", + }, + currencyConversion: func(from, to string, value float64) (float64, error) { + return 10, nil + }, + }, + want: want{ + fv: 10, + frv: 10, + }, + }, + { + name: "floor_values_not_set_in_both_bidExt_and_impctx", + args: args{ + bidExt: BidExt{ + ExtBid: openrtb_ext.ExtBid{}, + }, + impCtx: ImpCtx{}, + currencyConversion: func(from, to string, value float64) (float64, error) { + return 10, nil + }, + }, + want: want{ + fv: 0, + frv: 0, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fv, frv := GetBidLevelFloorsDetails(tt.args.bidExt, tt.args.impCtx, tt.args.currencyConversion) + assert.Equal(t, tt.want.fv, fv, tt.name) + assert.Equal(t, tt.want.frv, frv, tt.name) + }) + } +} + +func Test_getFloorsDetails(t *testing.T) { + type args struct { + bidResponseExt openrtb_ext.ExtBidResponse + } + tests := []struct { + name string + args args + floorDetails FloorsDetails + }{ + { + name: "no_responseExt", + args: args{}, + floorDetails: FloorsDetails{}, + }, + { + name: "empty_responseExt", + args: args{ + bidResponseExt: openrtb_ext.ExtBidResponse{}, + }, + floorDetails: FloorsDetails{}, + }, + { + name: "empty_prebid_in_responseExt", + args: args{ + bidResponseExt: openrtb_ext.ExtBidResponse{ + Prebid: &openrtb_ext.ExtResponsePrebid{}, + }, + }, + floorDetails: FloorsDetails{}, + }, + { + name: "empty_prebidfloors_in_responseExt", + args: args{ + bidResponseExt: openrtb_ext.ExtBidResponse{ + Prebid: &openrtb_ext.ExtResponsePrebid{ + Floors: &openrtb_ext.PriceFloorRules{}, + }, + }, + }, + floorDetails: FloorsDetails{}, + }, + { + name: "no_enforced_floors_data_in_responseExt", + args: args{ + bidResponseExt: openrtb_ext.ExtBidResponse{ + Prebid: &openrtb_ext.ExtResponsePrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Data: &openrtb_ext.PriceFloorData{}, + PriceFloorLocation: openrtb_ext.FetchLocation, + }, + }, + }, + }, + floorDetails: FloorsDetails{ + Skipfloors: nil, + FloorType: SoftFloor, + FloorSource: ptrutil.ToPtr(2), + FloorModelVersion: "", + }, + }, + { + name: "no_modelsgroups_floors_data_in_responseExt", + args: args{ + bidResponseExt: openrtb_ext.ExtBidResponse{ + Prebid: &openrtb_ext.ExtResponsePrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Data: &openrtb_ext.PriceFloorData{}, + PriceFloorLocation: openrtb_ext.FetchLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(true), + }, + }, + }, + }, + }, + floorDetails: FloorsDetails{ + Skipfloors: nil, + FloorType: HardFloor, + FloorSource: ptrutil.ToPtr(2), + FloorModelVersion: "", + }, + }, + { + name: "no_skipped_floors_data_in_responseExt", + args: args{ + bidResponseExt: openrtb_ext.ExtBidResponse{ + Prebid: &openrtb_ext.ExtResponsePrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Data: &openrtb_ext.PriceFloorData{ + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "version 1", + }, + }, + }, + PriceFloorLocation: openrtb_ext.FetchLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(true), + }, + }, + }, + }, + }, + floorDetails: FloorsDetails{ + Skipfloors: nil, + FloorType: HardFloor, + FloorSource: ptrutil.ToPtr(2), + FloorModelVersion: "version 1", + }, + }, + { + name: "all_floors_data_in_responseExt", + args: args{ + bidResponseExt: openrtb_ext.ExtBidResponse{ + Prebid: &openrtb_ext.ExtResponsePrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Skipped: ptrutil.ToPtr(true), + Data: &openrtb_ext.PriceFloorData{ + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "version 1", + }, + }, + }, + PriceFloorLocation: openrtb_ext.FetchLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(true), + }, + }, + }, + }, + }, + floorDetails: FloorsDetails{ + Skipfloors: ptrutil.ToPtr(1), + FloorType: HardFloor, + FloorSource: ptrutil.ToPtr(2), + FloorModelVersion: "version 1", + }, + }, + { + name: "floor_provider_present", + args: args{ + bidResponseExt: openrtb_ext.ExtBidResponse{ + Prebid: &openrtb_ext.ExtResponsePrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Skipped: ptrutil.ToPtr(true), + Data: &openrtb_ext.PriceFloorData{ + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "version 1", + }, + }, + FloorProvider: "provider", + }, + PriceFloorLocation: openrtb_ext.FetchLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(true), + }, + }, + }, + }, + }, + floorDetails: FloorsDetails{ + Skipfloors: ptrutil.ToPtr(1), + FloorType: HardFloor, + FloorSource: ptrutil.ToPtr(2), + FloorModelVersion: "version 1", + FloorProvider: "provider", + }, + }, + { + name: "floor_fetch_status_absent_in_FloorSourceMap", + args: args{ + bidResponseExt: openrtb_ext.ExtBidResponse{ + Prebid: &openrtb_ext.ExtResponsePrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Skipped: ptrutil.ToPtr(true), + FetchStatus: "invalid", + Data: &openrtb_ext.PriceFloorData{ + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "version 1", + }, + }, + FloorProvider: "provider", + }, + PriceFloorLocation: openrtb_ext.FetchLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(true), + }, + }, + }, + }, + }, + floorDetails: FloorsDetails{ + Skipfloors: ptrutil.ToPtr(1), + FloorType: HardFloor, + FloorSource: ptrutil.ToPtr(2), + FloorModelVersion: "version 1", + FloorProvider: "provider", + }, + }, + { + name: "floor_fetch_status_present_in_FloorSourceMap", + args: args{ + bidResponseExt: openrtb_ext.ExtBidResponse{ + Prebid: &openrtb_ext.ExtResponsePrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Skipped: ptrutil.ToPtr(true), + FetchStatus: openrtb_ext.FetchError, + Data: &openrtb_ext.PriceFloorData{ + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "version 1", + }, + }, + FloorProvider: "provider", + }, + PriceFloorLocation: openrtb_ext.FetchLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(true), + }, + }, + }, + }, + }, + floorDetails: FloorsDetails{ + Skipfloors: ptrutil.ToPtr(1), + FloorType: HardFloor, + FloorSource: ptrutil.ToPtr(2), + FloorModelVersion: "version 1", + FloorProvider: "provider", + FloorFetchStatus: ptrutil.ToPtr(2), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + floorsDetails := GetFloorsDetails(tt.args.bidResponseExt) + assert.Equal(t, tt.floorDetails, floorsDetails, tt.name) + }) + } +} + +func TestGetKGPSV(t *testing.T) { + type args struct { + bid openrtb2.Bid + bidderMeta PartnerData + adformat string + tagId string + div string + source string + } + tests := []struct { + name string + args args + kgpv string + kgpsv string + }{ + { + name: "default bid not regex", + args: args{ + bidderMeta: PartnerData{ + KGPV: "kgpv", + }, + }, + kgpv: "kgpv", + kgpsv: "kgpv", + }, + { + name: "default bid regex", + args: args{ + bidderMeta: PartnerData{ + KGPV: "kgpv", + IsRegex: true, + }, + }, + kgpv: "kgpv", + kgpsv: "", + }, + { + name: "only kgpsv found in partnerData", + args: args{ + bidderMeta: PartnerData{ + MatchedSlot: "kgpsv", + IsRegex: true, + }, + }, + kgpv: "kgpsv", + kgpsv: "kgpsv", + }, + { + name: "valid bid found in partnerData and regex true", + args: args{ + bid: openrtb2.Bid{ + Price: 1, + DealID: "deal", + W: 250, + H: 300, + }, + bidderMeta: PartnerData{ + KGPV: "kgpv", + MatchedSlot: "kgpsv", + IsRegex: true, + }, + }, + kgpv: "kgpv", + kgpsv: "kgpsv", + }, + { + name: "valid bid and regex false", + args: args{ + bid: openrtb2.Bid{ + Price: 1, + DealID: "deal", + W: 250, + H: 300, + }, + bidderMeta: PartnerData{ + KGPV: "kgpv", + MatchedSlot: "kgpsv", + IsRegex: false, + }, + }, + kgpv: "kgpv", + kgpsv: "kgpv", + }, + { + name: "KGPV and KGP not present in partnerData,regex false and adformat is video", + args: args{ + bid: openrtb2.Bid{ + Price: 1, + DealID: "deal", + W: 250, + H: 300, + }, + adformat: Video, + }, + kgpv: "", + kgpsv: "", + }, + { + name: "KGPV not present in partnerData,regex false and adformat is video", + args: args{ + bid: openrtb2.Bid{ + Price: 1, + DealID: "deal", + W: 250, + H: 300, + }, + adformat: Video, + bidderMeta: PartnerData{ + KGP: "_AU_@_W_x_H_", + }, + tagId: "adunit", + }, + kgpv: "adunit@0x0", + kgpsv: "adunit@0x0", + }, + { + name: "KGPV not present in partnerData,regex false and adformat is banner", + args: args{ + bid: openrtb2.Bid{ + Price: 1, + DealID: "deal", + W: 250, + H: 300, + }, + adformat: Banner, + bidderMeta: PartnerData{ + KGP: "_AU_@_W_x_H_", + MatchedSlot: "matchedSlot", + }, + tagId: "adunit", + }, + kgpv: "adunit@250x300", + kgpsv: "adunit@250x300", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := GetKGPSV(tt.args.bid, tt.args.bidderMeta, tt.args.adformat, tt.args.tagId, tt.args.div, tt.args.source) + if got != tt.kgpv { + t.Errorf("GetKGPSV() got = %v, want %v", got, tt.kgpv) + } + if got1 != tt.kgpsv { + t.Errorf("GetKGPSV() got1 = %v, want %v", got1, tt.kgpsv) + } + }) + } +} diff --git a/modules/pubmatic/openwrap/nonbids.go b/modules/pubmatic/openwrap/nonbids.go index 0a1aa82071c..302a470588b 100644 --- a/modules/pubmatic/openwrap/nonbids.go +++ b/modules/pubmatic/openwrap/nonbids.go @@ -1,6 +1,7 @@ package openwrap import ( + "github.com/prebid/openrtb/v19/openrtb3" "github.com/prebid/prebid-server/exchange" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/openrtb_ext" @@ -63,3 +64,36 @@ func addSeatNonBidsInResponseExt(rctx models.RequestCtx, responseExt *openrtb_ex }) } } + +// addLostToDealBidNonBRCode function sets the NonBR code of all lost-bids not satisfying dealTier to LossBidLostToDealBid +func addLostToDealBidNonBRCode(rctx *models.RequestCtx) { + if !rctx.SupportDeals { + return + } + + for impID := range rctx.ImpBidCtx { + winBid, ok := rctx.WinningBids[impID] + if !ok { + continue + } + + for bidID, bidCtx := range rctx.ImpBidCtx[impID].BidCtx { + // do not update NonBR for winning bid + if winBid.ID == bidID { + continue + } + + bidDealTierSatisfied := false + if bidCtx.BidExt.Prebid != nil { + bidDealTierSatisfied = bidCtx.BidExt.Prebid.DealTierSatisfied + } + // do not update NonBr if lost-bid satisfies dealTier + // because it can have NonBr reason as LossBidLostToHigherBid + if bidDealTierSatisfied { + continue + } + bidCtx.BidExt.Nbr = GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid) + rctx.ImpBidCtx[impID].BidCtx[bidID] = bidCtx + } + } +} diff --git a/modules/pubmatic/openwrap/nonbids_test.go b/modules/pubmatic/openwrap/nonbids_test.go index f08233623f7..4e04f223755 100644 --- a/modules/pubmatic/openwrap/nonbids_test.go +++ b/modules/pubmatic/openwrap/nonbids_test.go @@ -3,6 +3,7 @@ package openwrap import ( "testing" + "github.com/prebid/openrtb/v19/openrtb3" "github.com/prebid/prebid-server/exchange" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/openrtb_ext" @@ -396,3 +397,433 @@ func TestAddSeatNonBidsInResponseExt(t *testing.T) { }) } } + +func TestAddLostToDealBidNonBRCode(t *testing.T) { + tests := []struct { + name string + rctx *models.RequestCtx + impBidCtx map[string]models.ImpCtx + }{ + { + name: "support deal flag is false", + rctx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + }, + }, + }, + }, + }, + }, + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "no winning bid for imp so dont update NonBR code", + rctx: &models.RequestCtx{ + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + }, + }, + }, + }, + }, + SupportDeals: true, + }, + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "do not update LossBidLostToHigherBid NonBR code if bid satisifies dealTier", + rctx: &models.RequestCtx{ + WinningBids: map[string]models.OwBid{ + "imp1": { + ID: "bid-id-3", + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + }, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 50, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + }, + }, + "bid-id-3": { + BidExt: models.BidExt{ + NetECPM: 100, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + }, + }, + }, + }, + }, + SupportDeals: true, + }, + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + }, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 50, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + }, + }, + "bid-id-3": { + BidExt: models.BidExt{ + NetECPM: 100, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "update LossBidLostToHigherBid NonBR code if bid not satisifies dealTier", + rctx: &models.RequestCtx{ + WinningBids: map[string]models.OwBid{ + "imp1": { + ID: "bid-id-3", + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + }, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 100, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + "bid-id-3": { + BidExt: models.BidExt{ + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + }, + }, + }, + }, + }, + SupportDeals: true, + }, + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 100, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + "bid-id-3": { + BidExt: models.BidExt{ + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "test for multiple impression", + rctx: &models.RequestCtx{ + WinningBids: map[string]models.OwBid{ + "imp1": { + ID: "bid-id-3", + }, + "imp2": { + ID: "bid-id-2", + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToHigherBid), + }, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 100, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + "bid-id-3": { + BidExt: models.BidExt{ + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + }, + }, + }, + }, + "imp2": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 100, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + }, + }, + "bid-id-3": { + BidExt: models.BidExt{ + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + }, + }, + }, + SupportDeals: true, + }, + impBidCtx: map[string]models.ImpCtx{ + "imp1": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 100, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + "bid-id-3": { + BidExt: models.BidExt{ + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + }, + }, + }, + }, + "imp2": { + BidCtx: map[string]models.BidCtx{ + "bid-id-1": { + BidExt: models.BidExt{ + NetECPM: 10, + ExtBid: openrtb_ext.ExtBid{ + + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + "bid-id-2": { + BidExt: models.BidExt{ + NetECPM: 100, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: true, + }, + }, + }, + }, + "bid-id-3": { + BidExt: models.BidExt{ + NetECPM: 5, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + DealTierSatisfied: false, + }, + }, + Nbr: GetNonBidStatusCodePtr(openrtb3.LossBidLostToDealBid), + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + addLostToDealBidNonBRCode(tt.rctx) + assert.Equal(t, tt.impBidCtx, tt.rctx.ImpBidCtx, tt.name) + }) + } +} diff --git a/modules/pubmatic/openwrap/openwrap.go b/modules/pubmatic/openwrap/openwrap.go index 8771338704f..62f473d412b 100644 --- a/modules/pubmatic/openwrap/openwrap.go +++ b/modules/pubmatic/openwrap/openwrap.go @@ -10,6 +10,7 @@ import ( "github.com/golang/glog" gocache "github.com/patrickmn/go-cache" + "github.com/prebid/prebid-server/currency" "github.com/prebid/prebid-server/modules/moduledeps" ow_adapters "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" @@ -28,9 +29,10 @@ const ( ) type OpenWrap struct { - cfg config.Config - cache cache.Cache - metricEngine metrics.MetricsEngine + cfg config.Config + cache cache.Cache + metricEngine metrics.MetricsEngine + currencyConversion currency.Conversions } func initOpenWrap(rawCfg json.RawMessage, moduleDeps moduledeps.ModuleDeps) (OpenWrap, error) { @@ -74,9 +76,10 @@ func initOpenWrap(rawCfg json.RawMessage, moduleDeps moduledeps.ModuleDeps) (Ope tbf.Init(cfg.Cache.CacheDefaultExpiry, owCache) return OpenWrap{ - cfg: cfg, - cache: owCache, - metricEngine: &metricEngine, + cfg: cfg, + cache: owCache, + metricEngine: &metricEngine, + currencyConversion: moduleDeps.CurrencyConversion, }, nil } @@ -101,6 +104,6 @@ func open(driverName string, cfg config.Database) (*sql.DB, error) { } func patchConfig(cfg *config.Config) { - cfg.Server.HostName = getHostName() + cfg.Server.HostName = GetHostName() models.TrackerCallWrapOMActive = strings.Replace(models.TrackerCallWrapOMActive, "${OMScript}", cfg.PixelView.OMScript, 1) } diff --git a/modules/pubmatic/openwrap/targeting.go b/modules/pubmatic/openwrap/targeting.go index 374b20ffe4a..73c10281e5d 100644 --- a/modules/pubmatic/openwrap/targeting.go +++ b/modules/pubmatic/openwrap/targeting.go @@ -78,7 +78,9 @@ func addPWTTargetingForBid(rctx models.RequestCtx, bidResponse *openrtb2.BidResp if !ok { continue } - + if bidCtx.Prebid == nil { + bidCtx.Prebid = new(openrtb_ext.ExtBidPrebid) + } newTargeting := make(map[string]string) for key, value := range bidCtx.Prebid.Targeting { if allowTargetingKey(key) { diff --git a/modules/pubmatic/openwrap/tracker/banner.go b/modules/pubmatic/openwrap/tracker/banner.go index 81de0750428..90691a084da 100644 --- a/modules/pubmatic/openwrap/tracker/banner.go +++ b/modules/pubmatic/openwrap/tracker/banner.go @@ -5,21 +5,40 @@ import ( "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/tbf" "github.com/prebid/prebid-server/openrtb_ext" ) -func injectBannerTracker(rctx models.RequestCtx, tracker models.OWTracker, bid openrtb2.Bid, seat string) string { +func injectBannerTracker(rctx models.RequestCtx, tracker models.OWTracker, bid openrtb2.Bid, seat string, pixels []adunitconfig.UniversalPixel) string { var replacedTrackerStr, trackerFormat string trackerFormat = models.TrackerCallWrap - if trackerWithOM(tracker, bid, rctx.Platform, seat) { + if trackerWithOM(tracker, rctx.Platform, seat) { trackerFormat = models.TrackerCallWrapOMActive } replacedTrackerStr = strings.Replace(trackerFormat, "${escapedUrl}", tracker.TrackerURL, 1) - return bid.AdM + replacedTrackerStr + adm := applyTBFFeature(rctx, bid, replacedTrackerStr) + return appendUPixelinBanner(adm, pixels) +} + +// append universal pixels in creative based on conditions +func appendUPixelinBanner(adm string, universalPixel []adunitconfig.UniversalPixel) string { + if universalPixel == nil { + return adm + } + + for _, pixelVal := range universalPixel { + if pixelVal.Pos == models.PixelPosAbove { + adm = pixelVal.Pixel + adm + continue + } + adm = adm + pixelVal.Pixel + } + return adm } // TrackerWithOM checks for OM active condition for DV360 -func trackerWithOM(tracker models.OWTracker, bid openrtb2.Bid, platform, bidderCode string) bool { +func trackerWithOM(tracker models.OWTracker, platform, bidderCode string) bool { if platform == models.PLATFORM_APP && bidderCode == string(openrtb_ext.BidderPubmatic) { if tracker.DspId == models.DspId_DV360 { return true @@ -27,3 +46,14 @@ func trackerWithOM(tracker models.OWTracker, bid openrtb2.Bid, platform, bidderC } return false } + +// applyTBFFeature adds the tracker before or after the actual bid.Adm +// If TBF feature is applicable based on database-configuration for +// given pub-prof combination then injects the tracker before adm +// else injects the tracker after adm. +func applyTBFFeature(rctx models.RequestCtx, bid openrtb2.Bid, tracker string) string { + if tbf.IsEnabledTBFFeature(rctx.PubID, rctx.ProfileID) { + return tracker + bid.AdM + } + return bid.AdM + tracker +} diff --git a/modules/pubmatic/openwrap/tracker/banner_test.go b/modules/pubmatic/openwrap/tracker/banner_test.go new file mode 100644 index 00000000000..1ee8e8659f2 --- /dev/null +++ b/modules/pubmatic/openwrap/tracker/banner_test.go @@ -0,0 +1,301 @@ +package tracker + +import ( + "testing" + + "github.com/prebid/openrtb/v19/openrtb2" + mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/tbf" + "github.com/stretchr/testify/assert" +) + +func Test_injectBannerTracker(t *testing.T) { + tbf.SetAndResetTBFConfig(&mock_cache.MockCache{}, map[int]map[int]int{ + 5890: {1234: 100}, + }) + type args struct { + rctx models.RequestCtx + tracker models.OWTracker + bid openrtb2.Bid + seat string + pixels []adunitconfig.UniversalPixel + } + tests := []struct { + name string + args args + want string + }{ + { + name: "app_platform", + args: args{ + rctx: models.RequestCtx{ + Platform: models.PLATFORM_APP, + }, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + bid: openrtb2.Bid{ + AdM: `sample_creative`, + }, + seat: "test", + }, + want: `sample_creative
`, + }, + { + name: "app_platform_OM_Inactive_pubmatic", + args: args{ + rctx: models.RequestCtx{ + Platform: models.PLATFORM_APP, + }, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + DspId: -1, + }, + bid: openrtb2.Bid{ + AdM: `sample_creative`, + }, + seat: models.BidderPubMatic, + }, + want: `sample_creative
`, + }, + { + name: "app_platform_OM_Active_pubmatic", + args: args{ + rctx: models.RequestCtx{ + Platform: models.PLATFORM_APP, + }, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + DspId: models.DspId_DV360, + }, + bid: openrtb2.Bid{ + AdM: `sample_creative`, + }, + seat: models.BidderPubMatic, + }, + want: `sample_creative`, + }, + { + name: "tbf_feature_enabled", + args: args{ + rctx: models.RequestCtx{ + PubID: 5890, + ProfileID: 1234, + }, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + bid: openrtb2.Bid{ + AdM: `sample_creative`, + }, + }, + want: `
sample_creative`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := injectBannerTracker(tt.args.rctx, tt.args.tracker, tt.args.bid, tt.args.seat, tt.args.pixels); got != tt.want { + t.Errorf("injectBannerTracker() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_trackerWithOM(t *testing.T) { + type args struct { + tracker models.OWTracker + platform string + bidderCode string + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "in-app_partner_otherthan_pubmatic", + args: args{ + tracker: models.OWTracker{ + DspId: models.DspId_DV360, + }, + platform: models.PLATFORM_APP, + bidderCode: "test", + }, + want: false, + }, + { + name: "in-app_partner_pubmatic_other_dv360", + args: args{ + tracker: models.OWTracker{ + DspId: -1, + }, + platform: models.PLATFORM_APP, + bidderCode: models.BidderPubMatic, + }, + want: false, + }, + { + name: "display_partner_pubmatic_dv360", + args: args{ + tracker: models.OWTracker{ + DspId: models.DspId_DV360, + }, + platform: models.PLATFORM_DISPLAY, + bidderCode: models.BidderPubMatic, + }, + want: false, + }, + { + name: "in-app_partner_pubmatic_dv360", + args: args{ + tracker: models.OWTracker{ + DspId: models.DspId_DV360, + }, + platform: models.PLATFORM_APP, + bidderCode: models.BidderPubMatic, + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := trackerWithOM(tt.args.tracker, tt.args.platform, tt.args.bidderCode); got != tt.want { + t.Errorf("trackerWithOM() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_applyTBFFeature(t *testing.T) { + tbf.SetAndResetTBFConfig(&mock_cache.MockCache{}, map[int]map[int]int{ + 5890: {1234: 100}, + }) + + type args struct { + rctx models.RequestCtx + bid openrtb2.Bid + tracker string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "tbf_feature_disabled", + args: args{ + rctx: models.RequestCtx{ + PubID: 5890, + ProfileID: 100, + }, + bid: openrtb2.Bid{ + AdM: "bid_AdM", + }, + tracker: "tracker_url", + }, + want: "bid_AdMtracker_url", + }, + { + name: "tbf_feature_enabled", + args: args{ + rctx: models.RequestCtx{ + PubID: 5890, + ProfileID: 1234, + }, + bid: openrtb2.Bid{ + AdM: "bid_AdM", + }, + tracker: "tracker_url", + }, + want: "tracker_urlbid_AdM", + }, + { + name: "invalid_pubid", + args: args{ + rctx: models.RequestCtx{ + PubID: -1, + ProfileID: 1234, + }, + bid: openrtb2.Bid{ + AdM: "bid_AdM", + }, + tracker: "tracker_url", + }, + want: "bid_AdMtracker_url", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := applyTBFFeature(tt.args.rctx, tt.args.bid, tt.args.tracker); got != tt.want { + t.Errorf("applyTBFFeature() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_appendUPixelinBanner(t *testing.T) { + type args struct { + adm string + universalPixel []adunitconfig.UniversalPixel + } + type want struct { + creative string + } + tests := []struct { + name string + args args + want want + }{ + { + name: "empty universal pixel", + args: args{ + adm: `sample_creative`, + }, + want: want{ + creative: `sample_creative`, + }, + }, + { + name: "valid insertion of upixel", + args: args{ + adm: `sample_creative`, + universalPixel: []adunitconfig.UniversalPixel{ + { + Id: 123, + Pixel: `
`, + PixelType: models.PixelTypeUrl, + Pos: models.PixelPosAbove, + MediaType: "banner", + Partners: []string{"pubmatic", "appnexus"}, + }, + { + Id: 123, + Pixel: "", + PixelType: models.PixelTypeJS, + Pos: models.PixelPosBelow, + MediaType: "banner", + Partners: []string{"pubmatic", "appnexus"}, + }, + { + Id: 123, + Pixel: `
`, + PixelType: "url", + MediaType: "banner", + Partners: []string{"pubmatic", "appnexus"}, + }, + }, + }, + want: want{ + creative: `
sample_creative
`, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := appendUPixelinBanner(tt.args.adm, tt.args.universalPixel) + assert.Equal(t, tt.want.creative, got) + }) + } +} diff --git a/modules/pubmatic/openwrap/tracker/create.go b/modules/pubmatic/openwrap/tracker/create.go index 61788b707c9..1b7cab5ca80 100644 --- a/modules/pubmatic/openwrap/tracker/create.go +++ b/modules/pubmatic/openwrap/tracker/create.go @@ -7,44 +7,81 @@ import ( "strconv" "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/bidderparams" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/utils" ) +// pubmatic's KGP details per impression +type pubmaticMarketplaceMeta struct { + PubmaticKGP, PubmaticKGPV, PubmaticKGPSV string +} + func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) map[string]models.OWTracker { trackers := make(map[string]models.OWTracker) - // pubmatic's KGP details per impression - type pubmaticMarketplaceMeta struct { - PubmaticKGP, PubmaticKGPV, PubmaticKGPSV string - } pmMkt := make(map[string]pubmaticMarketplaceMeta) + trackers = createTrackers(rctx, trackers, bidResponse, pmMkt) + + // overwrite marketplace bid details with that of parent bidder + for bidID, tracker := range trackers { + if _, ok := rctx.MarketPlaceBidders[tracker.Tracker.PartnerInfo.BidderCode]; ok { + if v, ok := pmMkt[tracker.Tracker.ImpID]; ok { + tracker.Tracker.PartnerInfo.PartnerID = "pubmatic" + tracker.Tracker.PartnerInfo.KGPV = v.PubmaticKGPV + tracker.Tracker.LoggerData.KGPSV = v.PubmaticKGPSV + } + } + + var finalTrackerURL string + trackerURL := constructTrackerURL(rctx, tracker.Tracker) + trackURL, err := url.Parse(trackerURL) + if err == nil { + trackURL.Scheme = models.HTTPSProtocol + finalTrackerURL = trackURL.String() + } + tracker.TrackerURL = finalTrackerURL + + trackers[bidID] = tracker + } + + return trackers +} + +func createTrackers(rctx models.RequestCtx, trackers map[string]models.OWTracker, bidResponse *openrtb2.BidResponse, pmMkt map[string]pubmaticMarketplaceMeta) map[string]models.OWTracker { + floorsDetails := models.GetFloorsDetails(rctx.ResponseExt) for _, seatBid := range bidResponse.SeatBid { for _, bid := range seatBid.Bid { tracker := models.Tracker{ - PubID: rctx.PubID, - ProfileID: fmt.Sprintf("%d", rctx.ProfileID), - VersionID: fmt.Sprintf("%d", rctx.DisplayID), - PageURL: rctx.PageURL, - Timestamp: rctx.StartTime, - IID: rctx.LoggerImpressionID, - Platform: int(rctx.DevicePlatform), - SSAI: rctx.SSAI, - ImpID: bid.ImpID, + PubID: rctx.PubID, + ProfileID: fmt.Sprintf("%d", rctx.ProfileID), + VersionID: fmt.Sprintf("%d", rctx.DisplayVersionID), + PageURL: rctx.PageURL, + Timestamp: rctx.StartTime, + IID: rctx.LoggerImpressionID, + Platform: int(rctx.DevicePlatform), + SSAI: rctx.SSAI, + ImpID: bid.ImpID, + Origin: rctx.Origin, + AdPodSlot: 0, //TODO: Need to changes based on AdPodSlot Obj for CTV Req + TestGroup: rctx.ABTestConfigApplied, + FloorModelVersion: floorsDetails.FloorModelVersion, + FloorType: floorsDetails.FloorType, + FloorSkippedFlag: floorsDetails.Skipfloors, + FloorSource: floorsDetails.FloorSource, + LoggerData: models.LoggerData{ + FloorFetchStatus: floorsDetails.FloorFetchStatus, + FloorProvider: floorsDetails.FloorProvider, + }, } - - tagid := "" - var eg, en float64 - matchedSlot := "" - isRewardInventory := 0 - partnerID := seatBid.Seat - bidType := "banner" - var dspId int - - var isRegex bool - var kgp, kgpv, kgpsv string + var ( + kgp, kgpv, kgpsv, matchedSlot, adformat, bidId = "", "", "", "", "banner", "" + floorValue, floorRuleValue = float64(0), float64(0) + partnerID = seatBid.Seat + isRewardInventory, adduration = 0, 0 + dspId int + eg, en float64 + ) if impCtx, ok := rctx.ImpBidCtx[bid.ImpID]; ok { if bidderMeta, ok := impCtx.Bidders[seatBid.Seat]; ok { @@ -52,24 +89,33 @@ func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) m partnerID = bidderMeta.PrebidBidderCode } - if bidCtx, ok := impCtx.BidCtx[bid.ID]; ok { - + bidCtx, ok := impCtx.BidCtx[bid.ID] + if ok { // TODO do most calculation in wt // marketplace/alternatebiddercodes feature bidExt := bidCtx.BidExt - if bidExt.Prebid != nil && bidExt.Prebid.Meta != nil && len(bidExt.Prebid.Meta.AdapterCode) != 0 && seatBid.Seat != bidExt.Prebid.Meta.AdapterCode { - partnerID = bidExt.Prebid.Meta.AdapterCode + if bidExt.Prebid != nil { + if bidExt.Prebid.Video != nil && bidExt.Prebid.Video.Duration > 0 { + adduration = bidExt.Prebid.Video.Duration + } + if len(bidExt.Prebid.BidId) > 0 { + bidId = bidExt.Prebid.BidId + } + if bidExt.Prebid.Meta != nil && len(bidExt.Prebid.Meta.AdapterCode) != 0 && seatBid.Seat != bidExt.Prebid.Meta.AdapterCode { + partnerID = bidExt.Prebid.Meta.AdapterCode - if aliasSeat, ok := rctx.PrebidBidderCode[partnerID]; ok { - if bidderMeta, ok := impCtx.Bidders[aliasSeat]; ok { - matchedSlot = bidderMeta.MatchedSlot + if aliasSeat, ok := rctx.PrebidBidderCode[partnerID]; ok { + if bidderMeta, ok := impCtx.Bidders[aliasSeat]; ok { + matchedSlot = bidderMeta.MatchedSlot + } } } } - bidType = bidCtx.CreativeType dspId = bidCtx.DspId eg = bidCtx.EG en = bidCtx.EN + adformat = models.GetAdFormat(&bid, &bidExt, &impCtx) + floorValue, floorRuleValue = models.GetBidLevelFloorsDetails(bidExt, impCtx, rctx.CurrencyConversion) } _ = matchedSlot @@ -81,39 +127,18 @@ func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) m if bidderMeta, ok := impCtx.Bidders[seatBid.Seat]; ok { partnerID = bidderMeta.PrebidBidderCode kgp = bidderMeta.KGP - kgpv = bidderMeta.KGPV - kgpsv = bidderMeta.MatchedSlot - isRegex = bidderMeta.IsRegex - } - - // 1. nobid - if bid.Price == 0 && bid.H == 0 && bid.W == 0 { - //NOTE: kgpsv = bidderMeta.MatchedSlot above. Use the same - if !isRegex && kgpv != "" { // unmapped pubmatic's slot - kgpsv = kgpv - } else if !isRegex { - kgpv = kgpsv - } - } else if !isRegex { - if kgpv != "" { // unmapped pubmatic's slot - kgpsv = kgpv - } else if bid.H != 0 && bid.W != 0 { // Check when bid.H and bid.W will be zero with Price !=0. Ex: MobileInApp-MultiFormat-OnlyBannerMapping_Criteo_Partner_Validaton - // 2. valid bid - // kgpv has regex, do not generate slotName again - // kgpsv could be unmapped or mapped slot, generate slotName again based on bid.H and bid.W - kgpsv := bidderparams.GenerateSlotName(bid.H, bid.W, kgp, impCtx.TagID, impCtx.Div, rctx.Source) - kgpv = kgpsv - } - } - - if kgpv == "" { - kgpv = kgpsv + kgpv, kgpsv = models.GetKGPSV(bid, bidderMeta, adformat, impCtx.TagID, impCtx.Div, rctx.Source) } // -------------------------------------------------------------------------------------------------- - tagid = impCtx.TagID + tracker.SlotID = impCtx.SlotName + tracker.LoggerData.KGPSV = kgpsv tracker.Secure = impCtx.Secure - isRewardInventory = getRewardedInventoryFlag(rctx.ImpBidCtx[bid.ImpID].IsRewardInventory) + tracker.Adunit = impCtx.AdUnitName + isRewardInventory = 0 + if impCtx.IsRewardInventory != nil { + isRewardInventory = int(*impCtx.IsRewardInventory) + } } if seatBid.Seat == "pubmatic" { @@ -124,27 +149,37 @@ func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) m } } - tracker.Adunit = tagid - tracker.SlotID = fmt.Sprintf("%s_%s", bid.ImpID, tagid) tracker.RewardedInventory = isRewardInventory tracker.PartnerInfo = models.Partner{ - PartnerID: partnerID, - BidderCode: seatBid.Seat, - BidID: utils.GetOriginalBidId(bid.ID), - OrigBidID: utils.GetOriginalBidId(bid.ID), - KGPV: kgpv, - GrossECPM: eg, - NetECPM: en, + PartnerID: partnerID, + BidderCode: seatBid.Seat, + BidID: utils.GetOriginalBidId(bid.ID), + OrigBidID: utils.GetOriginalBidId(bid.ID), + KGPV: kgpv, + NetECPM: en, + GrossECPM: eg, + AdSize: models.GetSizeForPlatform(bid.W, bid.H, rctx.Platform), + AdDuration: adduration, + Adformat: adformat, + ServerSide: 1, + FloorValue: floorValue, + FloorRuleValue: floorRuleValue, + DealID: "-1", + } + if len(bidId) > 0 { + tracker.PartnerInfo.BidID = bidId } - if len(bid.ADomain) != 0 { if domain, err := models.ExtractDomain(bid.ADomain[0]); err == nil { - tracker.Advertiser = domain + tracker.PartnerInfo.Advertiser = domain } } + if len(bid.DealID) > 0 { + tracker.PartnerInfo.DealID = bid.DealID + } var finalTrackerURL string - trackerURL := ConstructTrackerURL(rctx, tracker) + trackerURL := constructTrackerURL(rctx, tracker) trackURL, err := url.Parse(trackerURL) if err == nil { trackURL.Scheme = models.HTTPSProtocol @@ -157,46 +192,17 @@ func CreateTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) m Price: bid.Price, PriceModel: models.VideoPricingModelCPM, PriceCurrency: bidResponse.Cur, - ErrorURL: ConstructVideoErrorURL(rctx, rctx.VideoErrorTrackerEndpoint, bid, tracker), - BidType: bidType, + ErrorURL: constructVideoErrorURL(rctx, rctx.VideoErrorTrackerEndpoint, bid, tracker), + BidType: adformat, DspId: dspId, } } } - - // overwrite marketplace bid details with that of parent bidder - for bidID, tracker := range trackers { - if _, ok := rctx.MarketPlaceBidders[tracker.Tracker.PartnerInfo.BidderCode]; ok { - if v, ok := pmMkt[tracker.Tracker.ImpID]; ok { - tracker.Tracker.PartnerInfo.PartnerID = "pubmatic" - tracker.Tracker.PartnerInfo.KGPV = v.PubmaticKGPV - } - } - - var finalTrackerURL string - trackerURL := ConstructTrackerURL(rctx, tracker.Tracker) - trackURL, err := url.Parse(trackerURL) - if err == nil { - trackURL.Scheme = models.HTTPSProtocol - finalTrackerURL = trackURL.String() - } - tracker.TrackerURL = finalTrackerURL - - trackers[bidID] = tracker - } - return trackers } -func getRewardedInventoryFlag(reward *int8) int { - if reward != nil { - return int(*reward) - } - return 0 -} - // ConstructTrackerURL constructing tracker url for impression -func ConstructTrackerURL(rctx models.RequestCtx, tracker models.Tracker) string { +func constructTrackerURL(rctx models.RequestCtx, tracker models.Tracker) string { trackerURL, err := url.Parse(rctx.TrackerEndpoint) if err != nil { return "" @@ -214,6 +220,10 @@ func ConstructTrackerURL(rctx models.RequestCtx, tracker models.Tracker) string if tracker.RewardedInventory == 1 { v.Set(models.TRKRewardedInventory, strconv.Itoa(tracker.RewardedInventory)) } + v.Set(models.TRKPlatform, strconv.Itoa(tracker.Platform)) + v.Set(models.TRKTestGroup, strconv.Itoa(tracker.TestGroup)) + v.Set(models.TRKPubDomain, tracker.Origin) + v.Set(models.TRKAdPodExist, strconv.Itoa(tracker.AdPodSlot)) partner := tracker.PartnerInfo v.Set(models.TRKPartnerID, partner.PartnerID) v.Set(models.TRKBidderCode, partner.BidderCode) @@ -225,6 +235,32 @@ func ConstructTrackerURL(rctx models.RequestCtx, tracker models.Tracker) string v.Set(models.TRKSSAI, tracker.SSAI) } v.Set(models.TRKOrigBidID, partner.OrigBidID) + v.Set(models.TRKAdSize, partner.AdSize) + if partner.AdDuration > 0 { + v.Set(models.TRKAdDuration, strconv.Itoa(partner.AdDuration)) + } + v.Set(models.TRKAdformat, partner.Adformat) + v.Set(models.TRKServerSide, strconv.Itoa(partner.ServerSide)) + v.Set(models.TRKAdvertiser, partner.Advertiser) + + v.Set(models.TRKFloorType, strconv.Itoa(tracker.FloorType)) + if tracker.FloorSkippedFlag != nil { + v.Set(models.TRKFloorSkippedFlag, strconv.Itoa(*tracker.FloorSkippedFlag)) + } + if len(tracker.FloorModelVersion) > 0 { + v.Set(models.TRKFloorModelVersion, tracker.FloorModelVersion) + } + if tracker.FloorSource != nil { + v.Set(models.TRKFloorSource, strconv.Itoa(*tracker.FloorSource)) + } + if partner.FloorValue > 0 { + v.Set(models.TRKFloorValue, fmt.Sprint(partner.FloorValue)) + } + if partner.FloorRuleValue > 0 { + v.Set(models.TRKFloorRuleValue, fmt.Sprint(partner.FloorRuleValue)) + } + v.Set(models.TRKServerLogger, "1") + v.Set(models.TRKDealID, partner.DealID) queryString := v.Encode() //Code for making tracker call http/https based on secure flag for in-app platform @@ -242,7 +278,7 @@ func ConstructTrackerURL(rctx models.RequestCtx, tracker models.Tracker) string } // ConstructVideoErrorURL constructing video error url for video impressions -func ConstructVideoErrorURL(rctx models.RequestCtx, errorURLString string, bid openrtb2.Bid, tracker models.Tracker) string { +func constructVideoErrorURL(rctx models.RequestCtx, errorURLString string, bid openrtb2.Bid, tracker models.Tracker) string { if len(errorURLString) == 0 { return "" } @@ -253,7 +289,7 @@ func ConstructVideoErrorURL(rctx models.RequestCtx, errorURLString string, bid o } errorURL.Scheme = models.HTTPSProtocol - tracker.SURL = rctx.OriginCookie + tracker.SURL = url.QueryEscape(rctx.Origin) //operId Note: It should be first parameter in url otherwise it will get failed at analytics side. if len(errorURL.RawQuery) > 0 { @@ -272,7 +308,7 @@ func ConstructVideoErrorURL(rctx models.RequestCtx, errorURLString string, bid o v.Set(models.ERRAdunit, tracker.Adunit) //au v.Set(models.ERRSUrl, tracker.SURL) // sURL v.Set(models.ERRPlatform, strconv.Itoa(tracker.Platform)) // pfi - v.Set(models.ERRAdvertiser, tracker.Advertiser) // adv + v.Set(models.ERRAdvertiser, tracker.PartnerInfo.Advertiser) // adv if tracker.SSAI != "" { v.Set(models.ERRSSAI, tracker.SSAI) // ssai for video/json endpoint diff --git a/modules/pubmatic/openwrap/tracker/create_test.go b/modules/pubmatic/openwrap/tracker/create_test.go new file mode 100644 index 00000000000..cba9528123b --- /dev/null +++ b/modules/pubmatic/openwrap/tracker/create_test.go @@ -0,0 +1,846 @@ +package tracker + +import ( + "net/url" + "strconv" + "testing" + "time" + + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/util/ptrutil" + "github.com/stretchr/testify/assert" +) + +var rctx = models.RequestCtx{ + PubID: 5890, + ProfileID: 1234, + DisplayID: 1, + DisplayVersionID: 1, + PageURL: "abc.com", + LoggerImpressionID: "loggerIID", + DevicePlatform: 5, + SSAI: "mediatailor", + Origin: "publisher.com", + ABTestConfigApplied: 1, + PrebidBidderCode: map[string]string{ + "pubmatic": "pubmatic", + }, + MarketPlaceBidders: map[string]struct{}{ + "pubmatic": {}, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "impID-1": { + TagID: "adunit-1", + AdUnitName: "adunit-1", + SlotName: "impID-1_adunit-1", + Bidders: map[string]models.PartnerData{ + "pubmatic": { + MatchedSlot: "matchedSlot", + PrebidBidderCode: "prebidBidderCode", + KGP: "_AU_@_W_x_H_", + }, + "pubmatic2": { + MatchedSlot: "matchedSlot2", + PrebidBidderCode: "prebidBidderCode2", + KGP: "_AU_@_W_x_H_", + }, + }, + BidFloor: 5.5, + BidFloorCur: "EUR", + BidCtx: map[string]models.BidCtx{ + "bidID-1": { + EG: 8.7, + EN: 8.7, + BidExt: models.BidExt{ + OriginalBidCPMUSD: 0, + ExtBid: openrtb_ext.ExtBid{ + Prebid: &openrtb_ext.ExtBidPrebid{ + BidId: "bidID-1", + Video: &openrtb_ext.ExtBidPrebidVideo{ + Duration: 20, + }, + Meta: &openrtb_ext.ExtBidPrebidMeta{ + AdapterCode: "pubmatic", + }, + Floors: &openrtb_ext.ExtBidPrebidFloors{ + FloorRule: "rule1", + FloorValue: 6.4, + FloorRuleValue: 4.4, + }, + Type: models.Banner, + }, + }, + }, + }, + }, + }, + }, +} + +func Test_createTrackers(t *testing.T) { + startTime := time.Now().Unix() + type args struct { + trackers map[string]models.OWTracker + rctx models.RequestCtx + bidResponse *openrtb2.BidResponse + pmMkt map[string]pubmaticMarketplaceMeta + } + tests := []struct { + name string + args args + want map[string]models.OWTracker + }{ + { + name: "empty_bidResponse", + args: args{ + trackers: map[string]models.OWTracker{}, + bidResponse: &openrtb2.BidResponse{}, + }, + want: map[string]models.OWTracker{}, + }, + { + name: "response with all details", + args: args{ + trackers: map[string]models.OWTracker{}, + rctx: func() models.RequestCtx { + testRctx := rctx + testRctx.StartTime = startTime + return testRctx + }(), + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bidID-1", + ImpID: "impID-1", + Price: 8.7, + W: 250, + H: 300, + ADomain: []string{"domain.com"}, + DealID: "deal-id-1", + }, + }, + Seat: "pubmatic", + }, + }, + Cur: models.USD, + }, + pmMkt: map[string]pubmaticMarketplaceMeta{}, + }, + want: map[string]models.OWTracker{ + "bidID-1": { + Tracker: models.Tracker{ + PubID: 5890, + PageURL: "abc.com", + Timestamp: startTime, + IID: "loggerIID", + ProfileID: "1234", + VersionID: "1", + Adunit: "adunit-1", + SlotID: "impID-1_adunit-1", + PartnerInfo: models.Partner{ + PartnerID: "prebidBidderCode", + BidderCode: "pubmatic", + KGPV: "adunit-1@250x300", + GrossECPM: 8.7, + NetECPM: 8.7, + BidID: "bidID-1", + OrigBidID: "bidID-1", + AdSize: "250x300", + AdDuration: 20, + Adformat: "banner", + ServerSide: 1, + Advertiser: "domain.com", + FloorValue: 6.4, + FloorRuleValue: 4.4, + DealID: "deal-id-1", + }, + Platform: 5, + SSAI: "mediatailor", + AdPodSlot: 0, + TestGroup: 1, + Origin: "publisher.com", + ImpID: "impID-1", + LoggerData: models.LoggerData{ + KGPSV: "adunit-1@250x300", + }, + }, + TrackerURL: "https:?adv=domain.com&af=banner&aps=0&au=adunit-1&bc=pubmatic&bidid=bidID-1&di=deal-id-1&dur=20&eg=8.7&en=8.7&frv=4.4&ft=0&fv=6.4&iid=loggerIID&kgpv=adunit-1%40250x300&orig=publisher.com&origbidid=bidID-1&pdvid=1&pid=1234&plt=5&pn=prebidBidderCode&psz=250x300&pubid=5890&purl=abc.com&sl=1&slot=impID-1_adunit-1&ss=1&ssai=mediatailor&tgid=1&tst=" + strconv.FormatInt(startTime, 10), + Price: 8.7, + PriceModel: "CPM", + PriceCurrency: "USD", + BidType: "banner", + }, + }, + }, + { + name: "response with all details with alias partner", + args: args{ + trackers: map[string]models.OWTracker{}, + rctx: func() models.RequestCtx { + testRctx := rctx + testRctx.StartTime = startTime + testRctx.PrebidBidderCode["pubmatic"] = "pubmatic2" + return testRctx + }(), + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bidID-1", + ImpID: "impID-1", + Price: 8.7, + W: 250, + H: 300, + ADomain: []string{"domain.com"}, + DealID: "deal-id-1", + }, + }, + Seat: "pubmatic2", + }, + }, + Cur: models.USD, + }, + pmMkt: map[string]pubmaticMarketplaceMeta{}, + }, + want: map[string]models.OWTracker{ + "bidID-1": { + Tracker: models.Tracker{ + PubID: 5890, + PageURL: "abc.com", + Timestamp: startTime, + IID: "loggerIID", + ProfileID: "1234", + VersionID: "1", + Adunit: "adunit-1", + SlotID: "impID-1_adunit-1", + PartnerInfo: models.Partner{ + PartnerID: "prebidBidderCode2", + BidderCode: "pubmatic2", + KGPV: "adunit-1@250x300", + GrossECPM: 8.7, + NetECPM: 8.7, + BidID: "bidID-1", + OrigBidID: "bidID-1", + AdSize: "250x300", + AdDuration: 20, + Adformat: "banner", + ServerSide: 1, + Advertiser: "domain.com", + FloorValue: 6.4, + FloorRuleValue: 4.4, + DealID: "deal-id-1", + }, + Platform: 5, + SSAI: "mediatailor", + AdPodSlot: 0, + TestGroup: 1, + Origin: "publisher.com", + ImpID: "impID-1", + LoggerData: models.LoggerData{ + KGPSV: "adunit-1@250x300", + }, + }, + TrackerURL: "https:?adv=domain.com&af=banner&aps=0&au=adunit-1&bc=pubmatic2&bidid=bidID-1&di=deal-id-1&dur=20&eg=8.7&en=8.7&frv=4.4&ft=0&fv=6.4&iid=loggerIID&kgpv=adunit-1%40250x300&orig=publisher.com&origbidid=bidID-1&pdvid=1&pid=1234&plt=5&pn=prebidBidderCode2&psz=250x300&pubid=5890&purl=abc.com&sl=1&slot=impID-1_adunit-1&ss=1&ssai=mediatailor&tgid=1&tst=" + strconv.FormatInt(startTime, 10), + Price: 8.7, + PriceModel: "CPM", + PriceCurrency: "USD", + BidType: "banner", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := createTrackers(tt.args.rctx, tt.args.trackers, tt.args.bidResponse, tt.args.pmMkt) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestConstructTrackerURL(t *testing.T) { + type args struct { + rctx models.RequestCtx + tracker models.Tracker + } + tests := []struct { + name string + args args + want string + }{ + { + name: "trackerEndpoint_parsingError", + args: args{ + rctx: models.RequestCtx{ + TrackerEndpoint: ":www.example.com", + }, + }, + want: "", + }, + { + name: "empty_tracker_details", + args: args{ + rctx: models.RequestCtx{ + TrackerEndpoint: "//t.pubmatic.com/wt", + Platform: models.PLATFORM_DISPLAY, + }, + tracker: models.Tracker{}, + }, + want: "http://t.pubmatic.com/wt?adv=&af=&aps=0&au=&bc=&bidid=&di=&eg=0&en=0&ft=0&iid=&kgpv=&orig=&origbidid=&pdvid=&pid=&plt=0&pn=&psz=&pubid=0&purl=&sl=1&slot=&ss=0&tgid=0&tst=0", + }, + { + name: "platform_amp_with_tracker_details", + args: args{ + rctx: models.RequestCtx{ + TrackerEndpoint: "//t.pubmatic.com/wt", + Platform: models.PLATFORM_AMP, + }, + tracker: models.Tracker{ + PubID: 12345, + PageURL: "www.abc.com", + IID: "98765", + ProfileID: "123", + VersionID: "1", + SlotID: "1234_1234", + Adunit: "adunit", + Platform: 1, + Origin: "www.publisher.com", + TestGroup: 1, + AdPodSlot: 0, + PartnerInfo: models.Partner{ + PartnerID: "AppNexus", + BidderCode: "AppNexus1", + BidID: "6521", + OrigBidID: "6521", + GrossECPM: 4.3, + NetECPM: 2.5, + KGPV: "adunit@300x250", + AdDuration: 10, + Adformat: models.Banner, + AdSize: "300x250", + ServerSide: 1, + Advertiser: "fb.com", + DealID: "420", + }, + }, + }, + want: "//t.pubmatic.com/wt?adv=fb.com&af=banner&aps=0&au=adunit&bc=AppNexus1&bidid=6521&di=420&dur=10&eg=4.3&en=2.5&ft=0&iid=98765&kgpv=adunit@300x250&orig=www.publisher.com&origbidid=6521&pdvid=1&pid=123&plt=1&pn=AppNexus&psz=300x250&pubid=12345&purl=www.abc.com&sl=1&slot=1234_1234&ss=1&tgid=1&tst=0", + }, + { + name: "all_details_with_ssai_in_tracker", + args: args{ + rctx: models.RequestCtx{ + TrackerEndpoint: "//t.pubmatic.com/wt", + Platform: models.PLATFORM_DISPLAY, + }, + tracker: models.Tracker{ + PubID: 12345, + PageURL: "www.abc.com", + IID: "98765", + ProfileID: "123", + VersionID: "1", + SlotID: "1234_1234", + Adunit: "adunit", + Platform: 1, + Origin: "www.publisher.com", + TestGroup: 1, + AdPodSlot: 0, + FloorSkippedFlag: ptrutil.ToPtr(0), + FloorModelVersion: "test version", + FloorSource: ptrutil.ToPtr(1), + FloorType: 1, + RewardedInventory: 1, + Secure: 1, + SSAI: "mediatailor", + PartnerInfo: models.Partner{ + PartnerID: "AppNexus", + BidderCode: "AppNexus1", + BidID: "6521", + OrigBidID: "6521", + GrossECPM: 4.3, + NetECPM: 2.5, + KGPV: "adunit@300x250", + AdDuration: 10, + Adformat: models.Banner, + AdSize: "300x250", + ServerSide: 1, + Advertiser: "fb.com", + DealID: "420", + FloorValue: 4.4, + FloorRuleValue: 2, + }, + }, + }, + want: "https://t.pubmatic.com/wt?adv=fb.com&af=banner&aps=0&au=adunit&bc=AppNexus1&bidid=6521&di=420&dur=10&eg=4.3&en=2.5&fmv=test version&frv=2&fskp=0&fsrc=1&ft=1&fv=4.4&iid=98765&kgpv=adunit@300x250&orig=www.publisher.com&origbidid=6521&pdvid=1&pid=123&plt=1&pn=AppNexus&psz=300x250&pubid=12345&purl=www.abc.com&rwrd=1&sl=1&slot=1234_1234&ss=1&ssai=mediatailor&tgid=1&tst=0", + }, + { + name: "all_details_with_secure_enable_in_tracker", + args: args{ + rctx: models.RequestCtx{ + TrackerEndpoint: "//t.pubmatic.com/wt", + Platform: models.PLATFORM_DISPLAY, + }, + tracker: models.Tracker{ + PubID: 12345, + PageURL: "www.abc.com", + IID: "98765", + ProfileID: "123", + VersionID: "1", + SlotID: "1234_1234", + Adunit: "adunit", + Platform: 1, + Origin: "www.publisher.com", + TestGroup: 1, + AdPodSlot: 0, + FloorSkippedFlag: ptrutil.ToPtr(0), + FloorModelVersion: "test version", + FloorSource: ptrutil.ToPtr(1), + FloorType: 1, + RewardedInventory: 1, + Secure: 1, + PartnerInfo: models.Partner{ + PartnerID: "AppNexus", + BidderCode: "AppNexus1", + BidID: "6521", + OrigBidID: "6521", + GrossECPM: 4.3, + NetECPM: 2.5, + KGPV: "adunit@300x250", + AdDuration: 10, + Adformat: models.Banner, + AdSize: "300x250", + ServerSide: 1, + Advertiser: "fb.com", + DealID: "420", + FloorValue: 4.4, + FloorRuleValue: 2, + }, + }, + }, + want: "https://t.pubmatic.com/wt?adv=fb.com&af=banner&aps=0&au=adunit&bc=AppNexus1&bidid=6521&di=420&dur=10&eg=4.3&en=2.5&fmv=test version&frv=2&fskp=0&fsrc=1&ft=1&fv=4.4&iid=98765&kgpv=adunit@300x250&orig=www.publisher.com&origbidid=6521&pdvid=1&pid=123&plt=1&pn=AppNexus&psz=300x250&pubid=12345&purl=www.abc.com&rwrd=1&sl=1&slot=1234_1234&ss=1&tgid=1&tst=0", + }, + { + name: "all_details_with_RewardInventory_in_tracker", + args: args{ + rctx: models.RequestCtx{ + TrackerEndpoint: "//t.pubmatic.com/wt", + Platform: models.PLATFORM_APP, + }, + tracker: models.Tracker{ + PubID: 12345, + PageURL: "www.abc.com", + IID: "98765", + ProfileID: "123", + VersionID: "1", + SlotID: "1234_1234", + Adunit: "adunit", + Platform: 1, + Origin: "www.publisher.com", + TestGroup: 1, + AdPodSlot: 0, + FloorSkippedFlag: ptrutil.ToPtr(0), + FloorModelVersion: "test version", + FloorSource: ptrutil.ToPtr(1), + FloorType: 1, + RewardedInventory: 1, + PartnerInfo: models.Partner{ + PartnerID: "AppNexus", + BidderCode: "AppNexus1", + BidID: "6521", + OrigBidID: "6521", + GrossECPM: 4.3, + NetECPM: 2.5, + KGPV: "adunit@300x250", + AdDuration: 10, + Adformat: models.Banner, + AdSize: "300x250", + ServerSide: 1, + Advertiser: "fb.com", + DealID: "420", + FloorValue: 4.4, + FloorRuleValue: 2, + }, + }, + }, + want: "//t.pubmatic.com/wt?adv=fb.com&af=banner&aps=0&au=adunit&bc=AppNexus1&bidid=6521&di=420&dur=10&eg=4.3&en=2.5&fmv=test version&frv=2&fskp=0&fsrc=1&ft=1&fv=4.4&iid=98765&kgpv=adunit@300x250&orig=www.publisher.com&origbidid=6521&pdvid=1&pid=123&plt=1&pn=AppNexus&psz=300x250&pubid=12345&purl=www.abc.com&rwrd=1&sl=1&slot=1234_1234&ss=1&tgid=1&tst=0", + }, + { + name: "all_floors_details_in_tracker", + args: args{ + rctx: models.RequestCtx{ + TrackerEndpoint: "//t.pubmatic.com/wt", + Platform: models.PLATFORM_APP, + }, + tracker: models.Tracker{ + PubID: 12345, + PageURL: "www.abc.com", + IID: "98765", + ProfileID: "123", + VersionID: "1", + SlotID: "1234_1234", + Adunit: "adunit", + Platform: 1, + Origin: "www.publisher.com", + TestGroup: 1, + AdPodSlot: 0, + FloorSkippedFlag: ptrutil.ToPtr(0), + FloorModelVersion: "test version", + FloorSource: ptrutil.ToPtr(1), + FloorType: 1, + PartnerInfo: models.Partner{ + PartnerID: "AppNexus", + BidderCode: "AppNexus1", + BidID: "6521", + OrigBidID: "6521", + GrossECPM: 4.3, + NetECPM: 2.5, + KGPV: "adunit@300x250", + AdDuration: 10, + Adformat: models.Banner, + AdSize: "300x250", + ServerSide: 1, + Advertiser: "fb.com", + DealID: "420", + FloorValue: 4.4, + FloorRuleValue: 2, + }, + }, + }, + want: "//t.pubmatic.com/wt?adv=fb.com&af=banner&aps=0&au=adunit&bc=AppNexus1&bidid=6521&di=420&dur=10&eg=4.3&en=2.5&fmv=test version&frv=2&fskp=0&fsrc=1&ft=1&fv=4.4&iid=98765&kgpv=adunit@300x250&orig=www.publisher.com&origbidid=6521&pdvid=1&pid=123&plt=1&pn=AppNexus&psz=300x250&pubid=12345&purl=www.abc.com&sl=1&slot=1234_1234&ss=1&tgid=1&tst=0", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + trackerUrl := constructTrackerURL(tt.args.rctx, tt.args.tracker) + decodedTrackerUrl, _ := url.QueryUnescape(trackerUrl) + assert.Equal(t, tt.want, decodedTrackerUrl, tt.name) + }) + } +} + +func TestConstructVideoErrorURL(t *testing.T) { + type args struct { + rctx models.RequestCtx + errorURLString string + bid openrtb2.Bid + tracker models.Tracker + } + tests := []struct { + name string + args args + want string + prefix string + }{ + { + name: "empty_urlString", + args: args{ + rctx: models.RequestCtx{}, + errorURLString: "", + bid: openrtb2.Bid{}, + tracker: models.Tracker{}, + }, + want: "", + prefix: "", + }, + { + name: "invalid_urlString_with_parsing_error", + args: args{ + rctx: models.RequestCtx{}, + errorURLString: `:invalid_url`, + bid: openrtb2.Bid{}, + tracker: models.Tracker{}, + }, + want: "", + prefix: "", + }, + { + name: "invalid_urlString_with_parsing", + args: args{ + rctx: models.RequestCtx{}, + errorURLString: `invalid_url`, + bid: openrtb2.Bid{}, + tracker: models.Tracker{}, + }, + want: "", + prefix: "", + }, + { + name: "valid_video_errorUrl", + args: args{ + rctx: models.RequestCtx{ + Origin: "domain.com:8080", + }, + errorURLString: `//t.pubmatic.com/wt`, + bid: openrtb2.Bid{}, + tracker: models.Tracker{ + PubID: 12345, + PageURL: "www.abc.com", + IID: "98765", + ProfileID: "123", + VersionID: "1", + SlotID: "1234_1234", + Adunit: "adunit", + Platform: 1, + Origin: "www.publisher.com", + TestGroup: 1, + AdPodSlot: 0, + SSAI: "mediatailor", + PartnerInfo: models.Partner{ + PartnerID: "AppNexus", + BidderCode: "AppNexus1", + BidID: "6521", + OrigBidID: "6521", + GrossECPM: 4.3, + NetECPM: 2.5, + KGPV: "adunit@300x250", + AdDuration: 10, + Adformat: models.Banner, + AdSize: "300x250", + ServerSide: 1, + Advertiser: "fb.com", + DealID: "420", + }, + }, + }, + want: `https://t.pubmatic.com/wt?operId=8&crId=-1&p=12345&pid=123&pn=AppNexus&ts=0&v=1&ier=[ERRORCODE]&bc=AppNexus1&au=adunit&sURL=domain.com%253A8080&ssai=mediatailor&pfi=1&adv=fb.com`, + prefix: `https://t.pubmatic.com/wt?operId=8`, + }, + { + name: "URL_with_Constant_Parameter", + args: args{ + rctx: models.RequestCtx{ + Origin: "domain.com:8080", + }, + errorURLString: `//t.pubmatic.com/wt?p1=v1&p2=v2`, + bid: openrtb2.Bid{}, + tracker: models.Tracker{ + PubID: 12345, + PageURL: "www.abc.com", + IID: "98765", + ProfileID: "123", + VersionID: "1", + SlotID: "1234_1234", + Adunit: "adunit", + Platform: 1, + Origin: "www.publisher.com", + TestGroup: 1, + AdPodSlot: 0, + SSAI: "mediatailor", + PartnerInfo: models.Partner{ + PartnerID: "AppNexus", + BidderCode: "AppNexus1", + BidID: "6521", + OrigBidID: "6521", + GrossECPM: 4.3, + NetECPM: 2.5, + KGPV: "adunit@300x250", + AdDuration: 10, + Adformat: models.Banner, + AdSize: "300x250", + ServerSide: 1, + Advertiser: "fb.com", + DealID: "420", + }, + }, + }, + want: `https://t.pubmatic.com/wt?operId=8&p1=v1&p2=v2&crId=-1&p=12345&pid=123&pn=AppNexus&ts=0&v=1&ier=[ERRORCODE]&bc=AppNexus1&au=adunit&sURL=domain.com%253A8080&ssai=mediatailor&pfi=1&adv=fb.com`, + prefix: `https://t.pubmatic.com/wt?operId=8`, + }, + { + name: "Creative_ID_in_bid", + args: args{ + rctx: models.RequestCtx{ + Origin: "domain.com:8080", + }, + errorURLString: `//t.pubmatic.com/wt`, + bid: openrtb2.Bid{ + CrID: "cr123", + }, + tracker: models.Tracker{ + PubID: 12345, + PageURL: "www.abc.com", + IID: "98765", + ProfileID: "123", + VersionID: "1", + SlotID: "1234_1234", + Adunit: "adunit", + Platform: 1, + Origin: "www.publisher.com", + TestGroup: 1, + AdPodSlot: 0, + SSAI: "mediatailor", + PartnerInfo: models.Partner{ + PartnerID: "AppNexus", + BidderCode: "AppNexus1", + BidID: "6521", + OrigBidID: "6521", + GrossECPM: 4.3, + NetECPM: 2.5, + KGPV: "adunit@300x250", + AdDuration: 10, + Adformat: models.Banner, + AdSize: "300x250", + ServerSide: 1, + Advertiser: "fb.com", + DealID: "420", + }, + }, + }, + want: `https://t.pubmatic.com/wt?operId=8&crId=cr123&p=12345&pid=123&pn=AppNexus&ts=0&v=1&ier=[ERRORCODE]&bc=AppNexus1&au=adunit&sURL=domain.com%253A8080&ssai=mediatailor&pfi=1&adv=fb.com`, + prefix: `https://t.pubmatic.com/wt?operId=8`, + }, + { + name: "URL_with_Schema", + args: args{ + rctx: models.RequestCtx{ + Origin: "com.myapp.test", + }, + errorURLString: `http://t.pubmatic.com/wt?p1=v1&p2=v2`, + bid: openrtb2.Bid{}, + tracker: models.Tracker{ + PubID: 12345, + PageURL: "www.abc.com", + IID: "98765", + ProfileID: "123", + VersionID: "1", + SlotID: "1234_1234", + Adunit: "adunit", + Platform: 1, + Origin: "www.publisher.com", + TestGroup: 1, + AdPodSlot: 0, + SSAI: "mediatailor", + PartnerInfo: models.Partner{ + PartnerID: "AppNexus", + BidderCode: "AppNexus1", + BidID: "6521", + OrigBidID: "6521", + GrossECPM: 4.3, + NetECPM: 2.5, + KGPV: "adunit@300x250", + AdDuration: 10, + Adformat: models.Banner, + AdSize: "300x250", + ServerSide: 1, + Advertiser: "fb.com", + DealID: "420", + }, + }, + }, + want: `https://t.pubmatic.com/wt?operId=8&p1=v1&p2=v2&crId=-1&p=12345&pid=123&pn=AppNexus&ts=0&v=1&ier=[ERRORCODE]&bc=AppNexus1&au=adunit&sURL=com.myapp.test&ssai=mediatailor&pfi=1&adv=fb.com`, + prefix: `https://t.pubmatic.com/wt?operId=8`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + constructedURL := constructVideoErrorURL(tt.args.rctx, tt.args.errorURLString, tt.args.bid, tt.args.tracker) + if len(constructedURL) > 0 && len(tt.want) > 0 { + wantURL, _ := url.Parse(constructedURL) + expectedURL, _ := url.Parse(tt.want) + if wantURL != nil && expectedURL != nil { + assert.Equal(t, wantURL.Host, expectedURL.Host) + assert.Equal(t, wantURL.Query(), expectedURL.Query()) + assert.Contains(t, constructedURL, tt.prefix) + } + } + }) + } +} + +func TestCreateTrackers(t *testing.T) { + startTime := time.Now().Unix() + type args struct { + rctx models.RequestCtx + bidResponse *openrtb2.BidResponse + } + tests := []struct { + name string + args args + want map[string]models.OWTracker + }{ + { + name: "overwrite marketplace bid details", + args: args{ + rctx: func() models.RequestCtx { + testRctx := rctx + testRctx.StartTime = startTime + return testRctx + }(), + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "bidID-1", + ImpID: "impID-1", + Price: 8.7, + W: 250, + H: 300, + ADomain: []string{"domain.com"}, + DealID: "deal-id-1", + }, + }, + Seat: "pubmatic", + }, + }, + Cur: models.USD, + }, + }, + want: map[string]models.OWTracker{ + "bidID-1": { + Tracker: models.Tracker{ + PubID: 5890, + PageURL: "abc.com", + Timestamp: startTime, + IID: "loggerIID", + ProfileID: "1234", + VersionID: "1", + Adunit: "adunit-1", + SlotID: "impID-1_adunit-1", + PartnerInfo: models.Partner{ + PartnerID: "pubmatic", + BidderCode: "pubmatic", + KGPV: "adunit-1@250x300", + GrossECPM: 8.7, + NetECPM: 8.7, + BidID: "bidID-1", + OrigBidID: "bidID-1", + AdSize: "250x300", + AdDuration: 20, + Adformat: "banner", + ServerSide: 1, + Advertiser: "domain.com", + FloorValue: 6.4, + FloorRuleValue: 4.4, + DealID: "deal-id-1", + }, + Platform: 5, + SSAI: "mediatailor", + AdPodSlot: 0, + TestGroup: 1, + Origin: "publisher.com", + ImpID: "impID-1", + LoggerData: models.LoggerData{ + KGPSV: "adunit-1@250x300", + }, + }, + TrackerURL: "https:?adv=domain.com&af=banner&aps=0&au=adunit-1&bc=pubmatic&bidid=bidID-1&di=deal-id-1&dur=20&eg=8.7&en=8.7&frv=4.4&ft=0&fv=6.4&iid=loggerIID&kgpv=adunit-1%40250x300&orig=publisher.com&origbidid=bidID-1&pdvid=1&pid=1234&plt=5&pn=pubmatic&psz=250x300&pubid=5890&purl=abc.com&sl=1&slot=impID-1_adunit-1&ss=1&ssai=mediatailor&tgid=1&tst=" + strconv.FormatInt(startTime, 10), + Price: 8.7, + PriceModel: "CPM", + PriceCurrency: "USD", + BidType: "banner", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := CreateTrackers(tt.args.rctx, tt.args.bidResponse) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/modules/pubmatic/openwrap/tracker/inject.go b/modules/pubmatic/openwrap/tracker/inject.go index f57bfd0b1f0..c84bf160c95 100644 --- a/modules/pubmatic/openwrap/tracker/inject.go +++ b/modules/pubmatic/openwrap/tracker/inject.go @@ -1,11 +1,15 @@ package tracker import ( + "errors" "fmt" + "strings" + + "golang.org/x/exp/slices" - "github.com/pkg/errors" "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" ) func InjectTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) (*openrtb2.BidResponse, error) { @@ -13,38 +17,63 @@ func InjectTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) ( for i, seatBid := range bidResponse.SeatBid { for j, bid := range seatBid.Bid { var errMsg string + var err error tracker := rctx.Trackers[bid.ID] adformat := tracker.BidType if rctx.Platform == models.PLATFORM_VIDEO { adformat = "video" } + pixels := getUniversalPixels(rctx, adformat, seatBid.Seat) switch adformat { case models.Banner: - bidResponse.SeatBid[i].Bid[j].AdM = injectBannerTracker(rctx, tracker, bidResponse.SeatBid[i].Bid[j], seatBid.Seat) + bidResponse.SeatBid[i].Bid[j].AdM = injectBannerTracker(rctx, tracker, bid, seatBid.Seat, pixels) case models.Video: - // trackers := make([]models.OWTracker, 0, len(rctx.Trackers)) - // for _, tracker := range rctx.Trackers { - // trackers = append(trackers, tracker) - // } trackers := []models.OWTracker{tracker} - - var err error bidResponse.SeatBid[i].Bid[j].AdM, err = injectVideoCreativeTrackers(bid, trackers) - if err != nil { - errMsg = fmt.Sprintf("failed to inject tracker for bidid %s with error %s", bid.ID, err.Error()) - } case models.Native: + if impBidCtx, ok := rctx.ImpBidCtx[bid.ImpID]; ok { + bidResponse.SeatBid[i].Bid[j].AdM, err = injectNativeCreativeTrackers(impBidCtx.Native, bid.AdM, tracker) + } else { + errMsg = fmt.Sprintf("native obj not found for impid %s", bid.ImpID) + } default: errMsg = fmt.Sprintf("Invalid adformat %s for bidid %s", adformat, bid.ID) } + if err != nil { + errMsg = fmt.Sprintf("failed to inject tracker for bidid %s with error %s", bid.ID, err.Error()) + } if errMsg != "" { rctx.MetricsEngine.RecordInjectTrackerErrorCount(adformat, rctx.PubIDStr, seatBid.Seat) - errs = errors.Wrap(errs, errMsg) + errs = models.ErrorWrap(errs, errors.New(errMsg)) } } } return bidResponse, errs } + +func getUniversalPixels(rctx models.RequestCtx, adformat string, bidderCode string) []adunitconfig.UniversalPixel { + var pixels, upixels []adunitconfig.UniversalPixel + if rctx.AdUnitConfig != nil && rctx.AdUnitConfig.Config != nil { + if defaultAdUnitConfig, ok := rctx.AdUnitConfig.Config[models.AdunitConfigDefaultKey]; ok && defaultAdUnitConfig != nil && len(defaultAdUnitConfig.UniversalPixel) != 0 { + upixels = defaultAdUnitConfig.UniversalPixel + } + } + for _, pixelVal := range upixels { + if pixelVal.MediaType != adformat { + continue + } + if len(pixelVal.Partners) > 0 && !slices.Contains(pixelVal.Partners, bidderCode) { + continue + } + pixel := pixelVal.Pixel // for pixelType `js` + if pixelVal.PixelType == models.PixelTypeUrl { + pixel = strings.Replace(models.UniversalPixelMacroForUrl, "${pixelUrl}", pixelVal.Pixel, 1) + } + pixelVal.Pixel = pixel + pixels = append(pixels, pixelVal) + } + return pixels +} diff --git a/modules/pubmatic/openwrap/tracker/inject_test.go b/modules/pubmatic/openwrap/tracker/inject_test.go new file mode 100644 index 00000000000..d1af134739d --- /dev/null +++ b/modules/pubmatic/openwrap/tracker/inject_test.go @@ -0,0 +1,688 @@ +package tracker + +import ( + "reflect" + "testing" + + "github.com/golang/mock/gomock" + "github.com/magiconair/properties/assert" + "github.com/prebid/openrtb/v19/openrtb2" + 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/models/adunitconfig" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/tbf" +) + +func TestInjectTrackers(t *testing.T) { + ctrl := gomock.NewController(t) + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockCache := mock_cache.NewMockCache(ctrl) + defer ctrl.Finish() + tbf.SetAndResetTBFConfig(mockCache, map[int]map[int]int{ + 5890: {1234: 100}, + }) + models.TrackerCallWrapOMActive = `` + + type args struct { + rctx models.RequestCtx + bidResponse *openrtb2.BidResponse + } + tests := []struct { + name string + args args + want *openrtb2.BidResponse + wantErr bool + }{ + { + name: "no_bidresponse", + args: args{ + bidResponse: &openrtb2.BidResponse{}, + }, + want: &openrtb2.BidResponse{}, + wantErr: false, + }, + { + name: "tracker_params_missing", + args: args{ + rctx: models.RequestCtx{ + Platform: "video", + Trackers: map[string]models.OWTracker{}, + MetricsEngine: mockEngine, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `creative`, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `creative`, + }, + }, + }, + }, + }, + wantErr: true, + }, + { + name: "invalid_adformat", + args: args{ + rctx: models.RequestCtx{ + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "invalid", + TrackerURL: `Tracking URL`, + }, + }, + MetricsEngine: mockEngine, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `creative`, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `creative`, + }, + }, + }, + }, + }, + wantErr: true, + }, + { + name: "empty_tracker_params", + args: args{ + rctx: models.RequestCtx{ + MetricsEngine: mockEngine, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `creative`, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `creative`, + }, + }, + }, + }, + }, + wantErr: true, + }, + { + name: "adformat_is_banner", + args: args{ + rctx: models.RequestCtx{ + Platform: "", + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "banner", + TrackerURL: `Tracking URL`, + }, + }, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `sample_creative`, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `sample_creative
`, + }, + }, + }, + }, + }, + wantErr: false, + }, + { + name: "adformat_is_video", + args: args{ + rctx: models.RequestCtx{ + Platform: "", + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "video", + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: ``, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: ``, + }, + }, + }, + }, + }, + wantErr: false, + }, + { + name: "platform_is_video", + args: args{ + rctx: models.RequestCtx{ + Platform: "video", + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "video", + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: ``, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: ``, + }, + }, + }, + }, + }, + wantErr: false, + }, + { + name: "platform_is_app", + args: args{ + rctx: models.RequestCtx{ + Platform: models.PLATFORM_APP, + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "banner", + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `sample_creative`, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `sample_creative
`, + }, + }, + }, + }, + }, + wantErr: false, + }, + { + name: "platform_is_app_with_OM_Inactive_pubmatic", + args: args{ + rctx: models.RequestCtx{ + Platform: models.PLATFORM_APP, + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "banner", + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + DspId: -1, + }, + }, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `sample_creative`, + }, + }, + Seat: models.BidderPubMatic, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `sample_creative
`, + }, + }, + Seat: models.BidderPubMatic, + }, + }, + }, + wantErr: false, + }, + { + name: "platform_is_app_with_OM_active_pubmatic", + args: args{ + rctx: models.RequestCtx{ + Platform: models.PLATFORM_APP, + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "banner", + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + DspId: models.DspId_DV360, + }, + }, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `sample_creative`, + }, + }, + Seat: models.BidderPubMatic, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + AdM: `sample_creative`, + }, + }, + Seat: models.BidderPubMatic, + }, + }, + }, + wantErr: false, + }, + { + name: "native_obj_not_found", + args: args{ + rctx: models.RequestCtx{ + Platform: models.PLATFORM_APP, + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "native", + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + MetricsEngine: mockEngine, + ImpBidCtx: map[string]models.ImpCtx{}, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + ImpID: "imp123", + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + ImpID: "imp123", + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + }, + }, + }, + }, + }, + wantErr: true, + }, + { + name: "adformat_is_native", + args: args{ + rctx: models.RequestCtx{ + Platform: models.PLATFORM_APP, + Trackers: map[string]models.OWTracker{ + "12345": { + BidType: "native", + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "imp123": { + Native: &openrtb2.Native{ + Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", + }, + }, + }, + }, + bidResponse: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + ImpID: "imp123", + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + }, + }, + }, + }, + }, + }, + want: &openrtb2.BidResponse{ + SeatBid: []openrtb2.SeatBid{ + { + Bid: []openrtb2.Bid{ + { + ID: "12345", + ImpID: "imp123", + AdM: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"Tracking URL"}]}`, + }, + }, + }, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.args.rctx.MetricsEngine != nil { + mockEngine.EXPECT().RecordInjectTrackerErrorCount(gomock.Any(), gomock.Any(), gomock.Any()) + } + got, err := InjectTrackers(tt.args.rctx, tt.args.bidResponse) + if (err != nil) != tt.wantErr { + t.Errorf("InjectTrackers() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("InjectTrackers() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getUniversalPixels(t *testing.T) { + type args struct { + rctx models.RequestCtx + adFormat string + bidderCode string + } + tests := []struct { + name string + args args + want []adunitconfig.UniversalPixel + }{ + { + name: "No default in adunitconfig", + args: args{ + adFormat: models.Banner, + bidderCode: models.BidderPubMatic, + rctx: models.RequestCtx{ + AdUnitConfig: &adunitconfig.AdUnitConfig{ + Config: map[string]*adunitconfig.AdConfig{}, + }, + }, + }, + want: nil, + }, + { + name: "No partners", + args: args{ + adFormat: models.Banner, + bidderCode: models.BidderPubMatic, + rctx: models.RequestCtx{ + AdUnitConfig: &adunitconfig.AdUnitConfig{ + Config: map[string]*adunitconfig.AdConfig{ + "default": { + UniversalPixel: []adunitconfig.UniversalPixel{ + { + Id: 123, + Pixel: "sample.com", + PixelType: models.PixelTypeUrl, + Pos: models.PixelPosAbove, + MediaType: "banner", + }, + }, + }, + }, + }, + }, + }, + want: []adunitconfig.UniversalPixel{ + { + Id: 123, + Pixel: `
`, + PixelType: models.PixelTypeUrl, + Pos: models.PixelPosAbove, + MediaType: "banner", + }, + }, + }, + { + name: "partner not present", + args: args{ + adFormat: models.Banner, + bidderCode: models.BidderPubMatic, + rctx: models.RequestCtx{ + AdUnitConfig: &adunitconfig.AdUnitConfig{ + Config: map[string]*adunitconfig.AdConfig{ + "default": { + UniversalPixel: []adunitconfig.UniversalPixel{ + { + Id: 123, + Pixel: "sample.com", + PixelType: models.PixelTypeUrl, + Pos: models.PixelPosAbove, + MediaType: models.Banner, + Partners: []string{"appnexus"}, + }, + }, + }, + }, + }, + }, + }, + want: nil, + }, + { + name: "mismatch in adformat and mediatype", + args: args{ + adFormat: models.Banner, + rctx: models.RequestCtx{ + AdUnitConfig: &adunitconfig.AdUnitConfig{ + Config: map[string]*adunitconfig.AdConfig{ + "default": { + UniversalPixel: []adunitconfig.UniversalPixel{ + { + Id: 123, + Pixel: "sample.com", + PixelType: models.PixelTypeJS, + Pos: models.PixelPosAbove, + MediaType: "video", + Partners: []string{"pubmatic", "appnexus"}, + }, + }, + }, + }, + }, + }, + }, + want: nil, + }, + { + name: "send valid upixel", + args: args{ + bidderCode: models.BidderPubMatic, + adFormat: models.Banner, + rctx: models.RequestCtx{ + AdUnitConfig: &adunitconfig.AdUnitConfig{ + Config: map[string]*adunitconfig.AdConfig{ + "default": { + UniversalPixel: []adunitconfig.UniversalPixel{ + { + Id: 123, + Pixel: "sample.com", + PixelType: "url", + Pos: models.PixelPosAbove, + MediaType: "banner", + Partners: []string{"pubmatic", "appnexus"}, + }, + { + Id: 123, + Pixel: "", + PixelType: models.PixelTypeJS, + Pos: models.PixelPosBelow, + MediaType: "banner", + Partners: []string{"pubmatic", "appnexus"}, + }, + { + Id: 123, + Pixel: "sample.com", + PixelType: "url", + MediaType: "banner", + Partners: []string{"pubmatic", "appnexus"}, + }, + }, + }, + }, + }, + }, + }, + want: []adunitconfig.UniversalPixel{ + { + Id: 123, + Pixel: `
`, + PixelType: "url", + Pos: models.PixelPosAbove, + MediaType: "banner", + Partners: []string{"pubmatic", "appnexus"}, + }, + { + Id: 123, + Pixel: "", + PixelType: models.PixelTypeJS, + Pos: models.PixelPosBelow, + MediaType: "banner", + Partners: []string{"pubmatic", "appnexus"}, + }, + { + Id: 123, + Pixel: `
`, + PixelType: "url", + MediaType: "banner", + Partners: []string{"pubmatic", "appnexus"}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getUniversalPixels(tt.args.rctx, tt.args.adFormat, tt.args.bidderCode) + assert.Equal(t, got, tt.want) + }) + } +} diff --git a/modules/pubmatic/openwrap/tracker/models.go b/modules/pubmatic/openwrap/tracker/models.go new file mode 100644 index 00000000000..fd94fcab9fe --- /dev/null +++ b/modules/pubmatic/openwrap/tracker/models.go @@ -0,0 +1,8 @@ +package tracker + +func getRewardedInventoryFlag(reward *int8) int { + if reward != nil { + return int(*reward) + } + return 0 +} diff --git a/modules/pubmatic/openwrap/tracker/models_test.go b/modules/pubmatic/openwrap/tracker/models_test.go new file mode 100644 index 00000000000..bf0317392f4 --- /dev/null +++ b/modules/pubmatic/openwrap/tracker/models_test.go @@ -0,0 +1 @@ +package tracker diff --git a/modules/pubmatic/openwrap/tracker/native.go b/modules/pubmatic/openwrap/tracker/native.go new file mode 100644 index 00000000000..693917af68d --- /dev/null +++ b/modules/pubmatic/openwrap/tracker/native.go @@ -0,0 +1,77 @@ +package tracker + +import ( + "errors" + "fmt" + "strings" + + "github.com/buger/jsonparser" + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" +) + +// Inject TrackerCall in Native Adm +func injectNativeCreativeTrackers(native *openrtb2.Native, adm string, tracker models.OWTracker) (string, error) { + if native == nil { + return adm, errors.New("native object is missing") + } + if len(native.Request) == 0 { + return adm, errors.New("native request is empty") + } + setTrackerURL := false + callback := func(value []byte, dataType jsonparser.ValueType, offset int, err error) { + if err != nil { + return + } + if setTrackerURL { + return + } + adm, setTrackerURL = injectNativeEventTracker(&adm, value, tracker) + } + jsonparser.ArrayEach([]byte(native.Request), callback, models.EventTrackers) + + if setTrackerURL { + return adm, nil + } + return injectNativeImpressionTracker(&adm, tracker) +} + +// inject tracker in EventTracker Object +func injectNativeEventTracker(adm *string, value []byte, trackerParam models.OWTracker) (string, bool) { + //Check for event=1 + event, _, _, err := jsonparser.Get(value, models.Event) + if err != nil || string(event) != models.EventValue { + return *adm, false + } + //Check for method=1 + methodsArray, _, _, err := jsonparser.Get(value, models.Methods) // "[1]","[2]","[1,2]", "[2,1]" + if err != nil || !strings.Contains(string(methodsArray), models.MethodValue) { + return *adm, false + } + + nativeEventTracker := strings.Replace(models.NativeTrackerMacro, "${trackerUrl}", trackerParam.TrackerURL, 1) + newAdm, err := jsonparser.Set([]byte(*adm), []byte(nativeEventTracker), models.EventTrackers, "[]") + if err != nil { + return *adm, false + } + *adm = string(newAdm) + return *adm, true +} + +// inject tracker in ImpTracker Object +func injectNativeImpressionTracker(adm *string, tracker models.OWTracker) (string, error) { + impTrackers := []string{} + callback := func(value []byte, dataType jsonparser.ValueType, offset int, err error) { + impTrackers = append(impTrackers, string(value)) + } + jsonparser.ArrayEach([]byte(*adm), callback, models.ImpTrackers) + //append trackerUrl + impTrackers = append(impTrackers, tracker.TrackerURL) + allImpTrackers := fmt.Sprintf(`["%s"]`, strings.Join(impTrackers, `","`)) + newAdm, err := jsonparser.Set([]byte(*adm), []byte(allImpTrackers), models.ImpTrackers) + if err != nil { + return *adm, errors.New("error setting imptrackers in native adm") + } + *adm = string(newAdm) + return *adm, nil +} diff --git a/modules/pubmatic/openwrap/tracker/native_test.go b/modules/pubmatic/openwrap/tracker/native_test.go new file mode 100644 index 00000000000..9a1e987e78f --- /dev/null +++ b/modules/pubmatic/openwrap/tracker/native_test.go @@ -0,0 +1,331 @@ +package tracker + +import ( + "testing" + + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/util/ptrutil" +) + +func Test_injectNativeCreativeTrackers(t *testing.T) { + type args struct { + native *openrtb2.Native + adm string + tracker models.OWTracker + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "native_object_nil", + args: args{ + adm: `creative`, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `creative`, + wantErr: true, + }, + { + name: "empty_native_object", + args: args{ + native: &openrtb2.Native{}, + adm: `creative`, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `creative`, + wantErr: true, + }, + { + name: "error_injecting_impression_tracker", + args: args{ + native: &openrtb2.Native{ + Request: `request`, + }, + adm: `creative`, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `creative`, + wantErr: true, + }, + { + name: "no_eventTracker_in_req_but_impTracker_exists_in_adm", + args: args{ + native: &openrtb2.Native{ + Request: "{\"context\":1,\"plcmttype\":1,\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", + }, + adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35","Tracking URL"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + wantErr: false, + }, + { + name: "none_of_eventTracker_and_impTracker_in_req", + args: args{ + native: &openrtb2.Native{ + Request: "{\"context\":1,\"plcmttype\":1,\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", + }, + adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}],"imptrackers":["Tracking URL"]}`, + wantErr: false, + }, + { + name: "event_is_1_&_method_is_2", + args: args{ + native: &openrtb2.Native{ + Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[2]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", + }, + adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":2,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.js"}]}`, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":2,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.js"}],"imptrackers":["Tracking URL"]}`, + wantErr: false, + }, + { + name: "event_is_2_&_method_is_1", + args: args{ + native: &openrtb2.Native{ + Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":2,\"methods\":[1]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", + }, + adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["abc.com"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":2,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.php"}]}`, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["abc.com","Tracking URL"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":2,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.php"}]}`, + wantErr: false, + }, + { + name: "event_1_&_method_1_but_eventtracker_exist_in_adm", + args: args{ + native: &openrtb2.Native{ + Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", + }, + adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"Tracking URL"}]}`, + wantErr: false, + }, + { + name: "event_1_&_method_1&2_but_eventtracker_exist_in_adm", + args: args{ + native: &openrtb2.Native{ + Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", + }, + adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"Tracking URL"}]}`, + wantErr: false, + }, + { + name: "event_1_&_method_1_but_eventtracker_NOT_exist_in_adm", + args: args{ + native: &openrtb2.Native{ + Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", + }, + adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e"}`, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"Tracking URL"}]}`, + wantErr: false, + }, + { + name: "event_1_method_1&2_and_multiple_eventrackers", + args: args{ + native: &openrtb2.Native{ + Request: "{\"context\":1,\"plcmttype\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]}],\"ver\":\"1.2\",\"assets\":[{\"id\":0,\"required\":0,\"img\":{\"type\":3,\"w\":300,\"h\":250}},{\"id\":1,\"required\":0,\"data\":{\"type\":1,\"len\":2}},{\"id\":2,\"required\":0,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"required\":0,\"title\":{\"len\":80}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":2}}]}", + }, + adm: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"abc.com"}]}`, + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"abc.com"},{"event":1,"method":1,"url":"Tracking URL"}]}`, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := injectNativeCreativeTrackers(tt.args.native, tt.args.adm, tt.args.tracker) + if (err != nil) != tt.wantErr { + t.Errorf("injectNativeCreativeTrackers() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("injectNativeCreativeTrackers() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_injectNativeEventTracker(t *testing.T) { + type args struct { + adm *string + value []byte + trackerParam models.OWTracker + } + tests := []struct { + name string + args args + want string + want1 bool + }{ + { + name: "error_injecting_eventTracker_empty_adm", + args: args{ + value: []byte("{\"event\":1,\"methods\":[1]}"), + adm: ptrutil.ToPtr(""), + trackerParam: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: "", + want1: false, + }, + { + name: "event is 1 & method 2", + args: args{ + value: []byte("{\"event\":1,\"methods\":[2]}"), + adm: ptrutil.ToPtr(`{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":2,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.js"}]}`), + trackerParam: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":2,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.js"}]}`, + want1: false, + }, + { + name: "event is 2 & method 1", + args: args{ + value: []byte("{\"event\":2,\"methods\":[1]}"), + adm: ptrutil.ToPtr(`{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":2,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.php"}]}`), + trackerParam: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":2,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet.php"}]}`, + want1: false, + }, + { + name: "event 1 & method 1, eventtracker exist in adm", + args: args{ + value: []byte("{\"event\":1,\"methods\":[1]}"), + adm: ptrutil.ToPtr(`{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`), + trackerParam: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"Tracking URL"}]}`, + want1: true, + }, + { + name: "event 1 & method 1,2, eventtracker exist in adm", + args: args{ + value: []byte("{\"event\":1,\"methods\":[1,2]}"), + adm: ptrutil.ToPtr(`{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`), + trackerParam: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"},{"event":1,"method":1,"url":"Tracking URL"}]}`, + want1: true, + }, + { + name: "event 1 & method 1, eventtracker NOT exist in adm", + args: args{ + value: []byte("{\"event\":1,\"methods\":[1]}"), + adm: ptrutil.ToPtr(`{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e"}`), + trackerParam: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"Tracking URL"}]}`, + want1: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := injectNativeEventTracker(tt.args.adm, tt.args.value, tt.args.trackerParam) + if got != tt.want { + t.Errorf("injectNativeEventTracker() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("injectNativeEventTracker() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func Test_injectNativeImpressionTracker(t *testing.T) { + type args struct { + adm *string + tracker models.OWTracker + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "error_injecting_impression_tracker_empty_adm", + args: args{ + adm: ptrutil.ToPtr(""), + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: "", + wantErr: true, + }, + { + name: "no_eventTracker_in_req_but_impTracker_exists_in_adm", + args: args{ + adm: ptrutil.ToPtr(`{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`), + tracker: models.OWTracker{ + TrackerURL: `Tracking URL`, + }, + }, + want: `{"assets":[{"id":0,"img":{"type":3,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":1,"data":{"type":1,"value":"Sponsored By PubMatic"}},{"id":2,"img":{"type":1,"url":"//sample.com/AdTag/native/728x90.png","w":728,"h":90}},{"id":3,"title":{"text":"Native Test Title"}},{"id":4,"data":{"type":2,"value":"Sponsored By PubMatic"}}],"link":{"url":"//www.sample.com","clicktrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35"],"fallback":"http://www.sample.com"},"imptrackers":["http://sampletracker.com/AdTag/9bde02d0-6017-11e4-9df7-005056967c35","Tracking URL"],"jstracker":"\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e\u003cscript src='\\/\\/sample.com\\/AdTag\\/native\\/tempReseponse.js'\u003e","eventtrackers":[{"event":1,"method":1,"url":"http://sample.com/AdServer/AdDisplayTrackerServlet"}]}`, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := injectNativeImpressionTracker(tt.args.adm, tt.args.tracker) + if (err != nil) != tt.wantErr { + t.Errorf("injectNativeImpressionTracker() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("injectNativeImpressionTracker() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/modules/pubmatic/openwrap/tracker/tracker.go b/modules/pubmatic/openwrap/tracker/tracker.go index e8e1c820c7b..bf93fac8d55 100644 --- a/modules/pubmatic/openwrap/tracker/tracker.go +++ b/modules/pubmatic/openwrap/tracker/tracker.go @@ -5,20 +5,28 @@ import ( "net/url" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/openrtb_ext" ) -func GetTrackerInfo(rCtx models.RequestCtx) string { +func GetTrackerInfo(rCtx models.RequestCtx, responseExt openrtb_ext.ExtBidResponse) string { + floorsDetails := models.GetFloorsDetails(responseExt) tracker := models.Tracker{ - PubID: rCtx.PubID, - ProfileID: fmt.Sprintf("%d", rCtx.ProfileID), - VersionID: fmt.Sprintf("%d", rCtx.DisplayID), - PageURL: rCtx.PageURL, - Timestamp: rCtx.StartTime, - IID: rCtx.LoggerImpressionID, - Platform: int(rCtx.DevicePlatform), + PubID: rCtx.PubID, + ProfileID: fmt.Sprintf("%d", rCtx.ProfileID), + VersionID: fmt.Sprintf("%d", rCtx.DisplayID), + PageURL: rCtx.PageURL, + Timestamp: rCtx.StartTime, + IID: rCtx.LoggerImpressionID, + Platform: int(rCtx.DevicePlatform), + Origin: rCtx.Origin, + TestGroup: rCtx.ABTestConfigApplied, + FloorModelVersion: floorsDetails.FloorModelVersion, + FloorType: floorsDetails.FloorType, + FloorSkippedFlag: floorsDetails.Skipfloors, + FloorSource: floorsDetails.FloorSource, } - constructedURLString := ConstructTrackerURL(rCtx, tracker) + constructedURLString := constructTrackerURL(rCtx, tracker) trackerURL, err := url.Parse(constructedURLString) if err != nil { diff --git a/modules/pubmatic/openwrap/tracker/tracker_test.go b/modules/pubmatic/openwrap/tracker/tracker_test.go new file mode 100644 index 00000000000..bd6d5b45da1 --- /dev/null +++ b/modules/pubmatic/openwrap/tracker/tracker_test.go @@ -0,0 +1,87 @@ +package tracker + +import ( + "strconv" + "testing" + "time" + + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/util/ptrutil" +) + +func TestGetTrackerInfo(t *testing.T) { + startTime := int64(time.Now().Unix()) + type args struct { + rCtx models.RequestCtx + responseExt openrtb_ext.ExtBidResponse + } + tests := []struct { + name string + args args + want string + }{ + { + name: "all_tracker_info_without_floors", + args: args{ + rCtx: models.RequestCtx{ + TrackerEndpoint: "localhost:8080/wt", + PubID: 123, + ProfileID: 1, + VersionID: 1, + PageURL: "www.test.com", + LoggerImpressionID: "iid123", + StartTime: startTime, + DevicePlatform: models.DevicePlatformMobileAppAndroid, + Origin: "www.publisher.com", + ABTestConfigApplied: 1, + }, + responseExt: openrtb_ext.ExtBidResponse{}, + }, + want: "localhost:8080/wt?adv=&af=&aps=0&au=%24%7BADUNIT%7D&bc=%24%7BBIDDER_CODE%7D&bidid=%24%7BBID_ID%7D&di=&eg=%24%7BG_ECPM%7D&en=%24%7BN_ECPM%7D&ft=0&iid=iid123&kgpv=%24%7BKGPV%7D&orig=www.publisher.com&origbidid=%24%7BORIGBID_ID%7D&pdvid=0&pid=1&plt=5&pn=%24%7BPARTNER_NAME%7D&psz=&pubid=123&purl=www.test.com&rwrd=%24%7BREWARDED%7D&sl=1&slot=%24%7BSLOT_ID%7D&ss=0&tgid=1&tst=" + strconv.FormatInt(startTime, 10), + }, + { + name: "all_tracker_info_with_floors", + args: args{ + rCtx: models.RequestCtx{ + TrackerEndpoint: "localhost:8080/wt", + PubID: 123, + ProfileID: 1, + VersionID: 1, + PageURL: "www.test.com", + LoggerImpressionID: "iid123", + StartTime: startTime, + DevicePlatform: models.DevicePlatformMobileAppAndroid, + Origin: "www.publisher.com", + ABTestConfigApplied: 1, + }, + responseExt: openrtb_ext.ExtBidResponse{ + Prebid: &openrtb_ext.ExtResponsePrebid{ + Floors: &openrtb_ext.PriceFloorRules{ + Skipped: ptrutil.ToPtr(true), + Data: &openrtb_ext.PriceFloorData{ + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "version 1", + }, + }, + }, + PriceFloorLocation: openrtb_ext.FetchLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: ptrutil.ToPtr(true), + }, + }, + }, + }, + }, + want: "localhost:8080/wt?adv=&af=&aps=0&au=%24%7BADUNIT%7D&bc=%24%7BBIDDER_CODE%7D&bidid=%24%7BBID_ID%7D&di=&eg=%24%7BG_ECPM%7D&en=%24%7BN_ECPM%7D&fmv=version+1&fskp=1&fsrc=2&ft=1&iid=iid123&kgpv=%24%7BKGPV%7D&orig=www.publisher.com&origbidid=%24%7BORIGBID_ID%7D&pdvid=0&pid=1&plt=5&pn=%24%7BPARTNER_NAME%7D&psz=&pubid=123&purl=www.test.com&rwrd=%24%7BREWARDED%7D&sl=1&slot=%24%7BSLOT_ID%7D&ss=0&tgid=1&tst=" + strconv.FormatInt(startTime, 10), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetTrackerInfo(tt.args.rCtx, tt.args.responseExt); got != tt.want { + t.Errorf("GetTrackerInfo() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/modules/pubmatic/openwrap/tracker/video_test.go b/modules/pubmatic/openwrap/tracker/video_test.go new file mode 100644 index 00000000000..0ab73b68071 --- /dev/null +++ b/modules/pubmatic/openwrap/tracker/video_test.go @@ -0,0 +1,565 @@ +package tracker + +import ( + "testing" + + "github.com/beevik/etree" + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/stretchr/testify/assert" +) + +func Test_injectVideoCreativeTrackers(t *testing.T) { + type args struct { + bid openrtb2.Bid + videoParams []models.OWTracker + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "empty_bid", + args: args{ + bid: openrtb2.Bid{}, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + }, + want: ``, + wantErr: true, + }, + { + name: "nil_bid.adm", + args: args{ + + bid: openrtb2.Bid{}, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + }, + want: ``, + wantErr: true, + }, + { + name: "empty_bid.adm", + args: args{ + + bid: openrtb2.Bid{ + AdM: ``, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + }, + want: ``, + wantErr: true, + }, + { + name: "empty_bid.adm.partner_url", + args: args{ + + bid: openrtb2.Bid{ + AdM: `https://stagingnyc.pubmatic.com:8443/test/pub_vast.xml`, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + }, + want: `PubMatic Wrapper`, + wantErr: false, + }, + { + name: "empty_vast_params", + args: args{ + + bid: openrtb2.Bid{ + AdM: ``, + }, + videoParams: []models.OWTracker{}, + }, + want: ``, + wantErr: true, + }, + { + name: "invalid_vast", + args: args{ + + bid: openrtb2.Bid{ + AdM: `invalid_vast_creative`, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + }, + want: `invalid_vast_creative`, + wantErr: true, + }, + { + name: "no_vast_ad", + args: args{ + + bid: openrtb2.Bid{ + AdM: ``, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + }, + want: ``, + wantErr: true, + }, + { + name: "vast_2.0_inline_pricing", + args: args{ + + bid: openrtb2.Bid{ + AdM: ``, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + }, + want: ``, + wantErr: false, + }, + { + name: "vast_3.0_inline_pricing", + args: args{ + + bid: openrtb2.Bid{ + AdM: ``, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + }, + want: ``, + wantErr: false, + }, + { + name: "inline_vast_3.0", + args: args{ + + bid: openrtb2.Bid{ + AdM: `Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1https://dsptracker.com/{PSPM}00:00:04https://www.sample.com`, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracker URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + }, + want: ``, + wantErr: false, + }, + { + name: "wrapper_vast_2.0", + args: args{ + + bid: openrtb2.Bid{ + AdM: `DSP`, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracker URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + }, + want: ``, + wantErr: false, + }, + { + name: "inline_vast_with_no_cdata", + args: args{ + + bid: openrtb2.Bid{ + AdM: `Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1http://172.16.4.213/AdServer/AdDisplayTrackerServlethttps://dsptracker.com/{PSPM}http://172.16.4.213/trackhttps://Errortrack.com00:00:04http://172.16.4.213/trackhttps://www.sample.comhttps://stagingnyc.pubmatic.com:8443/video/Shashank/mediaFileHost/media/mp4-sample-1.mp4]https://stagingnyc.pubmatic.com:8443/video/Shashank/mediaFileHost/media/mp4-sample-2.mp4]`, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracker URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + }, + want: ``, + wantErr: false, + }, + { + name: "wrapper_vast_with_no_cdata", + args: args{ + + bid: openrtb2.Bid{ + AdM: `DSPhttps://stagingnyc.pubmatic.com:8443/test/pub_vast.xmlhttps://track.dsp.com/er=[ERRORCODE]/tracker/errorhttps://track.dsp.com?e=impressionhttp://track.dsp.com/tracker/click`, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracker URL`, + ErrorURL: `Error URL`, + Price: 1.2, + }, + }, + }, + want: ``, + wantErr: false, + }, + { + name: "spaces_in_creative_TET-8024", + args: args{ + + bid: openrtb2.Bid{ + AdM: ` `, + }, + videoParams: []models.OWTracker{ + { + TrackerURL: `Tracking URL`, + ErrorURL: `Error URL`, + }, + }, + }, + want: ``, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := injectVideoCreativeTrackers(tt.args.bid, tt.args.videoParams) + if (err != nil) != tt.wantErr { + t.Errorf("injectVideoCreativeTrackers() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("injectVideoCreativeTrackers() = %v, want %v", got, tt.want) + } + }) + } +} + +func getXMLDocument(tag string) *etree.Document { + doc := etree.NewDocument() + err := doc.ReadFromString(tag) + if err != nil { + return nil + } + return doc +} + +func Test_injectPricingNodeVAST20(t *testing.T) { + type args struct { + doc *etree.Document + price float64 + model string + currency string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "pricing_node_missing", + args: args{ + doc: getXMLDocument(``), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: "USD", + }, + want: ``, + }, + { + name: "extensions_present_pricing_node_missing", + args: args{ + doc: getXMLDocument(``), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: "USD", + }, + want: ``, + }, + { + name: "extension_present_pricing_node_missing", + args: args{ + doc: getXMLDocument(``), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: "USD", + }, + want: ``, + }, + { + name: "override_pricing_cpm", + args: args{ + doc: getXMLDocument(`1.23`), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: "USD", + }, + want: ``, + }, + { + name: "override_pricing_cpm_add_currency", + args: args{ + doc: getXMLDocument(`1.23`), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: "USD", + }, + want: ``, + }, + { + name: "override_pricing_cpm_add_attributes", + args: args{ + doc: getXMLDocument(`1.23`), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: "USD", + }, + want: ``, + }, + { + name: "override_pricing_node", + args: args{ + doc: getXMLDocument(`1.23`), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: "USD", + }, + want: ``, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + injectPricingNodeVAST20(&tt.args.doc.Element, tt.args.price, tt.args.model, tt.args.currency) + actual, _ := tt.args.doc.WriteToString() + assert.Equal(t, tt.want, actual) + }) + } +} + +func Test_injectPricingNodeVAST3x(t *testing.T) { + type args struct { + doc *etree.Document + price float64 + model string + currency string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "override_cpm_pricing", + args: args{ + doc: getXMLDocument(`1.23`), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: "USD", + }, + want: ``, + }, + { + name: "override_cpc_pricing", + args: args{ + doc: getXMLDocument(`1.23`), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: "USD", + }, + want: ``, + }, + { + name: "add_currency", + args: args{ + doc: getXMLDocument(`1.23`), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: "USD", + }, + want: ``, + }, + { + name: "add_all_attributes", + args: args{ + doc: getXMLDocument(`1.23`), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: "USD", + }, + want: ``, + }, + { + name: "pricing_node_not_present", + args: args{ + doc: getXMLDocument(``), + price: 4.5, + model: models.VideoPricingModelCPM, + currency: models.VideoPricingCurrencyUSD, + }, + want: ``, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + injectPricingNodeVAST3x(&tt.args.doc.Element, tt.args.price, tt.args.model, tt.args.currency) + actual, _ := tt.args.doc.WriteToString() + assert.Equal(t, tt.want, actual) + }) + } +} + +func Test_updatePricingNode(t *testing.T) { + type args struct { + doc *etree.Document + price float64 + model string + currency string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "overwrite_existing_price", + args: args{ + doc: getXMLDocument(`4.5`), + price: 1.2, + model: models.VideoPricingModelCPM, + currency: models.VideoPricingCurrencyUSD, + }, + want: ``, + }, + { + name: "empty_attributes", + args: args{ + doc: getXMLDocument(`4.5`), + price: 1.2, + model: models.VideoPricingModelCPM, + currency: models.VideoPricingCurrencyUSD, + }, + want: ``, + }, + { + name: "overwrite_model", + args: args{ + doc: getXMLDocument(`4.5`), + price: 1.2, + model: "CPC", + currency: models.VideoPricingCurrencyUSD, + }, + want: ``, + }, + { + name: "overwrite_currency", + args: args{ + doc: getXMLDocument(`4.5`), + price: 1.2, + model: models.VideoPricingModelCPM, + currency: "INR", + }, + want: ``, + }, + { + name: "default_values_attribute", + args: args{ + doc: getXMLDocument(`4.5`), + price: 1.2, + model: "", + currency: "", + }, + want: ``, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + updatePricingNode(tt.args.doc.ChildElements()[0], tt.args.price, tt.args.model, tt.args.currency) + actual, _ := tt.args.doc.WriteToString() + assert.Equal(t, tt.want, actual) + }) + } +} + +func Test_newPricingNode(t *testing.T) { + type args struct { + price float64 + model string + currency string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "node", + args: args{ + price: 1.2, + model: models.VideoPricingModelCPM, + currency: models.VideoPricingCurrencyUSD, + }, + want: ``, + }, + { + name: "empty_currency", + args: args{ + price: 1.2, + model: models.VideoPricingModelCPM, + currency: "", + }, + want: ``, + }, + { + name: "other_currency", + args: args{ + price: 1.2, + model: models.VideoPricingModelCPM, + currency: `INR`, + }, + want: ``, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := newPricingNode(tt.args.price, tt.args.model, tt.args.currency) + doc := etree.NewDocument() + doc.InsertChild(nil, got) + actual, _ := doc.WriteToString() + assert.Equal(t, tt.want, actual) + }) + } +} diff --git a/modules/pubmatic/openwrap/util.go b/modules/pubmatic/openwrap/util.go index e2522ef6ca8..d7443c5b856 100644 --- a/modules/pubmatic/openwrap/util.go +++ b/modules/pubmatic/openwrap/util.go @@ -7,6 +7,7 @@ import ( "github.com/prebid/openrtb/v19/adcom1" "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v19/openrtb3" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" @@ -49,8 +50,9 @@ func init() { // GetDevicePlatform determines the device from which request has been generated func GetDevicePlatform(rCtx models.RequestCtx, bidRequest *openrtb2.BidRequest) models.DevicePlatform { userAgentString := rCtx.UA - if userAgentString == "" && bidRequest != nil && bidRequest.Device != nil && len(bidRequest.Device.UA) != 0 { + if bidRequest != nil && bidRequest.Device != nil && len(bidRequest.Device.UA) != 0 { userAgentString = bidRequest.Device.UA + rCtx.UA = userAgentString } switch rCtx.Platform { @@ -86,19 +88,11 @@ func GetDevicePlatform(rCtx models.RequestCtx, bidRequest *openrtb2.BidRequest) deviceType = bidRequest.Device.DeviceType } isCtv := isCTV(userAgentString) - regexStatus := models.Failure if deviceType != 0 { if deviceType == adcom1.DeviceTV || deviceType == adcom1.DeviceConnected || deviceType == adcom1.DeviceSetTopBox { - if isCtv { - regexStatus = models.Success - } - rCtx.MetricsEngine.RecordCtvUaAccuracy(rCtx.PubIDStr, regexStatus) return models.DevicePlatformConnectedTv } - if isCtv { - rCtx.MetricsEngine.RecordCtvUaAccuracy(rCtx.PubIDStr, regexStatus) - } } if deviceType == 0 && isCtv { @@ -212,7 +206,7 @@ func getSourceAndOrigin(bidRequest *openrtb2.BidRequest) (string, string) { } // getHostName Generates server name from node and pod name in K8S environment -func getHostName() string { +func GetHostName() string { var ( nodeName string podName string @@ -290,3 +284,18 @@ func getPubmaticErrorCode(standardNBR int) int { func isCTV(userAgent string) bool { return ctvRegex.Match([]byte(userAgent)) } + +func getPlatformFromRequest(request *openrtb2.BidRequest) string { + var platform string + if request.Site != nil { + return models.PLATFORM_DISPLAY + } + if request.App != nil { + return models.PLATFORM_APP + } + return platform +} + +func GetNonBidStatusCodePtr(nbr openrtb3.NonBidStatusCode) *openrtb3.NonBidStatusCode { + return &nbr +} diff --git a/modules/pubmatic/openwrap/util_test.go b/modules/pubmatic/openwrap/util_test.go index ef76716d3f5..cbd5d91a45c 100644 --- a/modules/pubmatic/openwrap/util_test.go +++ b/modules/pubmatic/openwrap/util_test.go @@ -182,19 +182,14 @@ func (fakeSyncer) GetSync([]usersync.SyncType, macros.UserSyncPrivacy) (usersync } func TestGetDevicePlatform(t *testing.T) { - ctrl := gomock.NewController(t) - mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) - defer ctrl.Finish() - type args struct { rCtx models.RequestCtx bidRequest *openrtb2.BidRequest } tests := []struct { - name string - args args - setup func() - want models.DevicePlatform + name string + args args + want models.DevicePlatform }{ { name: "Test_empty_platform", @@ -332,49 +327,37 @@ func TestGetDevicePlatform(t *testing.T) { name: "Test_platform_video_with_deviceType_as_CTV", args: args{ rCtx: models.RequestCtx{ - UA: "", - Platform: "video", - PubIDStr: "5890", - MetricsEngine: mockEngine, + UA: "", + Platform: "video", + PubIDStr: "5890", }, bidRequest: getORTBRequest("", "", adcom1.DeviceTV, true, false), }, want: models.DevicePlatformConnectedTv, - setup: func() { - mockEngine.EXPECT().RecordCtvUaAccuracy("5890", models.Failure).Times(1) - }, }, { name: "Test_platform_video_with_deviceType_as_connected_device", args: args{ rCtx: models.RequestCtx{ - UA: "", - Platform: "video", - PubIDStr: "5890", - MetricsEngine: mockEngine, + UA: "", + Platform: "video", + PubIDStr: "5890", }, bidRequest: getORTBRequest("", "", adcom1.DeviceConnected, true, false), }, want: models.DevicePlatformConnectedTv, - setup: func() { - mockEngine.EXPECT().RecordCtvUaAccuracy("5890", models.Failure).Times(1) - }, }, { name: "Test_platform_video_with_deviceType_as_set_top_box", args: args{ rCtx: models.RequestCtx{ - UA: "", - Platform: "video", - PubIDStr: "5890", - MetricsEngine: mockEngine, + UA: "", + Platform: "video", + PubIDStr: "5890", }, bidRequest: getORTBRequest("", "", adcom1.DeviceSetTopBox, false, true), }, want: models.DevicePlatformConnectedTv, - setup: func() { - mockEngine.EXPECT().RecordCtvUaAccuracy("5890", models.Failure).Times(1) - }, }, { name: "Test_platform_video_with_nil_values", @@ -458,16 +441,12 @@ func TestGetDevicePlatform(t *testing.T) { name: "Test_platform_video_with_CTV_and_device_type", args: args{ rCtx: models.RequestCtx{ - UA: "", - Platform: "video", - PubIDStr: "5890", - MetricsEngine: mockEngine, + UA: "", + Platform: "video", + PubIDStr: "5890", }, bidRequest: getORTBRequest("", "Mozilla/5.0 (SMART-TV; Linux; Tizen 4.0) AppleWebKit/538.1 (KHTML, like Gecko) Version/4.0 TV Safari/538.1", 3, false, true), }, - setup: func() { - mockEngine.EXPECT().RecordCtvUaAccuracy("5890", models.Success).Times(1) - }, want: models.DevicePlatformConnectedTv, }, { @@ -485,24 +464,17 @@ func TestGetDevicePlatform(t *testing.T) { name: "Test_platform_video_for_non_CTV_User_agent_with_device_type_7", args: args{ rCtx: models.RequestCtx{ - UA: "", - Platform: "video", - PubIDStr: "5890", - MetricsEngine: mockEngine, + UA: "", + Platform: "video", + PubIDStr: "5890", }, bidRequest: getORTBRequest("", "AppleCoreMedia/1.0.0.20L498 (iphone ; U; CPU OS 16_4_1 like Mac OS X; en_us)", 7, false, true), }, want: models.DevicePlatformConnectedTv, - setup: func() { - mockEngine.EXPECT().RecordCtvUaAccuracy("5890", models.Failure).Times(1) - }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if tt.setup != nil { - tt.setup() - } got := GetDevicePlatform(tt.args.rCtx, tt.args.bidRequest) assert.Equal(t, tt.want, got) }) @@ -871,7 +843,7 @@ func TestGetHostName(t *testing.T) { os.Setenv(models.ENV_VAR_POD_NAME, tt.args.podName) } - got := getHostName() + got := GetHostName() assert.Equal(t, tt.want, got) resetEnvVarsForServerName() @@ -973,35 +945,3 @@ func TestGetPubmaticErrorCode(t *testing.T) { }) } } - -func TestIsCTV(t *testing.T) { - type args struct { - userAgent string - } - tests := []struct { - name string - args args - want bool - }{ - { - name: "CTV_request", - args: args{ - userAgent: "Mozilla/5.0 (SMART-TV; Linux; Tizen 4.0) AppleWebKit/538.1 (KHTML, like Gecko) Version/4.0 TV Safari/538.1", - }, - want: true, - }, - { - name: "Non_CTV_request", - args: args{ - userAgent: "AppleCoreMedia/1.0.0.20L498 (iphone ; U; CPU OS 16_4_1 like Mac OS X; en_us", - }, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := isCTV(tt.args.userAgent) - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/openrtb_ext/response.go b/openrtb_ext/response.go index 86c05d45dd3..e91adaecad9 100644 --- a/openrtb_ext/response.go +++ b/openrtb_ext/response.go @@ -139,6 +139,7 @@ type NonBidObject struct { Video *ExtBidPrebidVideo `json:"video,omitempty"` BidId string `json:"bidid,omitempty"` Floors *ExtBidPrebidFloors `json:"floors,omitempty"` + OriginalBidCPMUSD float64 `json:"origbidcpmusd,omitempty"` } // ExtResponseNonBidPrebid represents bidresponse.ext.prebid.seatnonbid[].nonbid[].ext diff --git a/router/router.go b/router/router.go index 1a3db84b4e9..a17fb327747 100644 --- a/router/router.go +++ b/router/router.go @@ -193,7 +193,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R } metricsRegistry := metricsConf.NewMetricsRegistry() - moduleDeps := moduledeps.ModuleDeps{HTTPClient: generalHttpClient, MetricsCfg: &cfg.Metrics, MetricsRegistry: metricsRegistry} + moduleDeps := moduledeps.ModuleDeps{HTTPClient: generalHttpClient, MetricsCfg: &cfg.Metrics, MetricsRegistry: metricsRegistry, CurrencyConversion: rateConvertor.Rates()} repo, moduleStageNames, err := modules.NewBuilder().Build(cfg.Hooks.Modules, moduleDeps) if err != nil { glog.Fatalf("Failed to init hook modules: %v", err) From c6c9682f80af906cfdfda12130d71d4b429d2ca6 Mon Sep 17 00:00:00 2001 From: Jaydeep Mohite <30924180+pm-jaydeep-mohite@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:44:15 +0530 Subject: [PATCH 26/30] OTT-1431: Added publisher level stats for vast-wrapper count and response time (#647) --- modules/pubmatic/vastunwrap/entryhook.go | 4 ++ .../vastunwrap/hook_raw_bidder_response.go | 38 +++++++----- .../hook_raw_bidder_response_test.go | 58 +++++++++++++++---- modules/pubmatic/vastunwrap/models/request.go | 5 +- modules/pubmatic/vastunwrap/module.go | 11 ++-- modules/pubmatic/vastunwrap/module_test.go | 6 +- modules/pubmatic/vastunwrap/stats/metrics.go | 21 ++++--- .../pubmatic/vastunwrap/stats/metrics_test.go | 8 ++- .../pubmatic/vastunwrap/stats/mock/mock.go | 24 ++++---- modules/pubmatic/vastunwrap/unwrap_service.go | 12 ++-- .../vastunwrap/unwrap_service_test.go | 17 +++--- 11 files changed, 132 insertions(+), 72 deletions(-) diff --git a/modules/pubmatic/vastunwrap/entryhook.go b/modules/pubmatic/vastunwrap/entryhook.go index 71725cf6a33..6e5b1ad52aa 100644 --- a/modules/pubmatic/vastunwrap/entryhook.go +++ b/modules/pubmatic/vastunwrap/entryhook.go @@ -33,6 +33,10 @@ func handleEntrypointHook( vastRequestContext := models.RequestCtx{ VastUnwrapEnabled: getVastUnwrapperEnable(payload.Request.Context(), VastUnwrapEnabled) && getRandomNumber() < config.TrafficPercentage, } + + if !vastRequestContext.VastUnwrapEnabled { + vastRequestContext.VastUnwrapStatsEnabled = getRandomNumber() < config.StatTrafficPercentage + } result.ModuleContext = make(hookstage.ModuleContext) result.ModuleContext[RequestContext] = vastRequestContext return result, nil 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..83fdc55abff 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) { diff --git a/modules/pubmatic/vastunwrap/module_test.go b/modules/pubmatic/vastunwrap/module_test.go index 749cf4c4aaf..247a3622357 100644 --- a/modules/pubmatic/vastunwrap/module_test.go +++ b/modules/pubmatic/vastunwrap/module_test.go @@ -161,9 +161,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.") } From 92b46a6a0c8159b09c9316d22bf39e6d4919293c Mon Sep 17 00:00:00 2001 From: Nilesh Chate <97721111+pm-nilesh-chate@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:50:52 +0530 Subject: [PATCH 27/30] UOE-9681: fix prebid validation in ow module (#650) --- .../pubmatic/openwrap/beforevalidationhook.go | 8 +++ .../openwrap/beforevalidationhook_test.go | 58 ++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go index 3705c424683..ee9bfb60ca8 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook.go +++ b/modules/pubmatic/openwrap/beforevalidationhook.go @@ -47,6 +47,14 @@ func (m OpenWrap) handleBeforeValidationHook( } }() + // return prebid validation error + if len(payload.BidRequest.Imp) == 0 || (payload.BidRequest.Site == nil && payload.BidRequest.App == nil) { + result.Reject = false + m.metricEngine.RecordBadRequests(rCtx.Endpoint, getPubmaticErrorCode(nbr.InvalidRequestExt)) + m.metricEngine.RecordNobidErrPrebidServerRequests(rCtx.PubIDStr, nbr.InvalidRequestExt) + return result, nil + } + //Do not execute the module for requests processed in SSHB(8001) if rCtx.Sshb == "1" { result.Reject = false diff --git a/modules/pubmatic/openwrap/beforevalidationhook_test.go b/modules/pubmatic/openwrap/beforevalidationhook_test.go index 0dd6f688739..1806961a26a 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook_test.go +++ b/modules/pubmatic/openwrap/beforevalidationhook_test.go @@ -1874,6 +1874,7 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { }, }, }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","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","bidfloor":4.3,"bidfloorcur":"USD","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"}}}`), }, want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ Reject: false, @@ -1882,8 +1883,9 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { { name: "empty_module_context", args: args{ - ctx: context.Background(), - moduleCtx: hookstage.ModuleInvocationContext{}, + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{}, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","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","bidfloor":4.3,"bidfloorcur":"USD","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, @@ -1904,6 +1906,7 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { "test_rctx": "test", }, }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","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","bidfloor":4.3,"bidfloorcur":"USD","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, @@ -1926,6 +1929,7 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { }, }, }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","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","bidfloor":4.3,"bidfloorcur":"USD","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"}}}`), }, want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{ Reject: false, @@ -2531,6 +2535,56 @@ func TestOpenWrap_handleBeforeValidationHook(t *testing.T) { wantErr: false, isRequestNotRejected: true, }, + { + name: "prebid-validation-errors-imp-missing", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + PubIDStr: "1234", + Endpoint: models.EndpointV25, + }, + }, + }, + bidrequest: json.RawMessage(`{}`), + }, + fields: fields{ + cache: mockCache, + metricEngine: mockEngine, + }, + setup: func() { + mockEngine.EXPECT().RecordBadRequests(models.EndpointV25, 18) + mockEngine.EXPECT().RecordNobidErrPrebidServerRequests("1234", 604) + }, + want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{}, + wantErr: false, + }, + { + name: "prebid-validation-errors-site-and-app-missing", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": models.RequestCtx{ + PubIDStr: "1234", + Endpoint: models.EndpointV25, + }, + }, + }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","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","bidfloor":4.3,"bidfloorcur":"USD","ext":{"bidder":{"pubmatic":{"keywords":[{"key":"pmzoneid","value":["val1","val2"]}]}},"prebid":{}}}],"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() { + mockEngine.EXPECT().RecordBadRequests(models.EndpointV25, 18) + mockEngine.EXPECT().RecordNobidErrPrebidServerRequests("1234", 604) + }, + want: hookstage.HookResult[hookstage.BeforeValidationRequestPayload]{}, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 6ca13ee947d1124c4eb39fadd4dffb5b71bd1cd1 Mon Sep 17 00:00:00 2001 From: Jaydeep Mohite <30924180+pm-jaydeep-mohite@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:48:16 +0530 Subject: [PATCH 28/30] Revert "OTT-1431: Added publisher level stats for vast-wrapper count and response time (#647)" (#651) This reverts commit c6c9682f80af906cfdfda12130d71d4b429d2ca6. --- modules/pubmatic/vastunwrap/entryhook.go | 4 -- .../vastunwrap/hook_raw_bidder_response.go | 38 +++++------- .../hook_raw_bidder_response_test.go | 58 ++++--------------- modules/pubmatic/vastunwrap/models/request.go | 5 +- modules/pubmatic/vastunwrap/module.go | 11 ++-- modules/pubmatic/vastunwrap/module_test.go | 6 +- modules/pubmatic/vastunwrap/stats/metrics.go | 21 +++---- .../pubmatic/vastunwrap/stats/metrics_test.go | 8 +-- .../pubmatic/vastunwrap/stats/mock/mock.go | 24 ++++---- modules/pubmatic/vastunwrap/unwrap_service.go | 12 ++-- .../vastunwrap/unwrap_service_test.go | 17 +++--- 11 files changed, 72 insertions(+), 132 deletions(-) diff --git a/modules/pubmatic/vastunwrap/entryhook.go b/modules/pubmatic/vastunwrap/entryhook.go index 6e5b1ad52aa..71725cf6a33 100644 --- a/modules/pubmatic/vastunwrap/entryhook.go +++ b/modules/pubmatic/vastunwrap/entryhook.go @@ -33,10 +33,6 @@ func handleEntrypointHook( vastRequestContext := models.RequestCtx{ VastUnwrapEnabled: getVastUnwrapperEnable(payload.Request.Context(), VastUnwrapEnabled) && getRandomNumber() < config.TrafficPercentage, } - - if !vastRequestContext.VastUnwrapEnabled { - vastRequestContext.VastUnwrapStatsEnabled = getRandomNumber() < config.StatTrafficPercentage - } result.ModuleContext = make(hookstage.ModuleContext) result.ModuleContext[RequestContext] = vastRequestContext return result, nil diff --git a/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go b/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go index a2a725a5806..a51a85d7b98 100644 --- a/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go +++ b/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go @@ -19,38 +19,26 @@ func (m VastUnwrapModule) handleRawBidderResponseHook( result.DebugMessages = append(result.DebugMessages, "error: request-ctx not found in handleRawBidderResponseHook()") return result, nil } - if !vastRequestContext.VastUnwrapEnabled && !vastRequestContext.VastUnwrapStatsEnabled { + if !vastRequestContext.VastUnwrapEnabled { result.DebugMessages = append(result.DebugMessages, "error: vast unwrap flag is not enabled in handleRawBidderResponseHook()") return result, nil } defer func() { miCtx.ModuleContext[RequestContext] = vastRequestContext }() - - // 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 := 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) } - 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 b4677ab47b5..933c1123ec6 100644 --- a/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go +++ b/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go @@ -70,42 +70,6 @@ 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{ @@ -132,8 +96,8 @@ func TestHandleRawBidderResponseHook(t *testing.T) { }, wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "1").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "1").AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()).AnyTimes() }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "1") @@ -168,9 +132,9 @@ func TestHandleRawBidderResponseHook(t *testing.T) { }, 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() + mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "1").AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()).AnyTimes() }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") @@ -218,9 +182,9 @@ func TestHandleRawBidderResponseHook(t *testing.T) { }, 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() + mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "1").AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()).AnyTimes() }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") @@ -292,9 +256,9 @@ func TestHandleRawBidderResponseHook(t *testing.T) { }, }, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordRequestTime("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 60dc655a600..c558fdf0912 100644 --- a/modules/pubmatic/vastunwrap/models/request.go +++ b/modules/pubmatic/vastunwrap/models/request.go @@ -1,7 +1,6 @@ package models type RequestCtx struct { - UA string - VastUnwrapEnabled bool - VastUnwrapStatsEnabled bool + UA string + VastUnwrapEnabled bool } diff --git a/modules/pubmatic/vastunwrap/module.go b/modules/pubmatic/vastunwrap/module.go index 83fdc55abff..aca37238997 100644 --- a/modules/pubmatic/vastunwrap/module.go +++ b/modules/pubmatic/vastunwrap/module.go @@ -17,12 +17,11 @@ import ( ) type VastUnwrapModule struct { - 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) + 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) } func Builder(rawCfg json.RawMessage, deps moduledeps.ModuleDeps) (interface{}, error) { diff --git a/modules/pubmatic/vastunwrap/module_test.go b/modules/pubmatic/vastunwrap/module_test.go index 247a3622357..749cf4c4aaf 100644 --- a/modules/pubmatic/vastunwrap/module_test.go +++ b/modules/pubmatic/vastunwrap/module_test.go @@ -161,9 +161,9 @@ func TestVastUnwrapModuleHandleRawBidderResponseHook(t *testing.T) { wantAdM: true, }, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0") - mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1") - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0") + mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "1") + mockMetricsEngine.EXPECT().RecordRequestTime("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 b56ba2e0a25..af93b333e97 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(accountId, bidder, status string) - RecordWrapperCount(accountId, bidder string, wrapper_count string) - RecordRequestTime(accountId, bidder string, readTime time.Duration) + RecordRequestStatus(bidder, status string) + RecordWrapperCount(bidder string, wrapper_count string) + RecordRequestTime(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{pubIdLabel, bidderLabel, statusLabel}) + []string{bidderLabel, statusLabel}) metrics.wrapperCount = newCounter(cfg.MetricsCfg.Prometheus, metrics.Registry, "vastunwrap_wrapper_count", "Count of vast unwrap levels labeled by bidder", - []string{pubIdLabel, bidderLabel, wrapperCountLabel}) + []string{bidderLabel, wrapperCountLabel}) metrics.requestTime = newHistogramVec(cfg.MetricsCfg.Prometheus, metrics.Registry, "vastunwrap_request_time", - "Time taken to serve the vast unwrap request in Milliseconds", []string{pubIdLabel, bidderLabel}, + "Time taken to serve the vast unwrap request in Milliseconds", []string{bidderLabel}, []float64{50, 100, 200, 300, 500}) return &metrics, nil } @@ -86,27 +86,24 @@ func newHistogramVec(cfg config.PrometheusMetrics, registry *prometheus.Registry } // RecordRequest record counter with vast unwrap status -func (m *Metrics) RecordRequestStatus(accountId, bidder, status string) { +func (m *Metrics) RecordRequestStatus(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(accountId, bidder, wrapper_count string) { +func (m *Metrics) RecordWrapperCount(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(accountId, bidder string, requestTime time.Duration) { +func (m *Metrics) RecordRequestTime(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 4cd87879993..fd0ee09f4f1 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("1234", "pubmatic", time.Millisecond*250) + m.RecordRequestTime("pubmatic", time.Millisecond*250) result := getHistogramFromHistogramVec(m.requestTime, "bidder", "pubmatic") assertHistogram(t, result, 1, 250) @@ -44,10 +44,9 @@ func TestRecordRequestTime(t *testing.T) { func TestRecordRequestStatus(t *testing.T) { m := createMetricsForTesting() - m.RecordRequestStatus("1234", "pubmatic", "0") + m.RecordRequestStatus("pubmatic", "0") assertCounterVecValue(t, "Record_Request_Status", "Record_Request_Status_Success", m.requests, float64(1), prometheus.Labels{ - "pub_id": "1234", "bidder": "pubmatic", "status": "0", }) @@ -56,10 +55,9 @@ func TestRecordRequestStatus(t *testing.T) { func TestRecordWrapperCount(t *testing.T) { m := createMetricsForTesting() - m.RecordWrapperCount("1234", "pubmatic", "1") + m.RecordWrapperCount("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 ae18d8fe3af..b0c899010eb 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, arg2 string) { +func (m *MockMetricsEngine) RecordRequestStatus(arg0, arg1 string) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordRequestStatus", arg0, arg1, arg2) + m.ctrl.Call(m, "RecordRequestStatus", arg0, arg1) } // RecordRequestStatus indicates an expected call of RecordRequestStatus -func (mr *MockMetricsEngineMockRecorder) RecordRequestStatus(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockMetricsEngineMockRecorder) RecordRequestStatus(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordRequestStatus", reflect.TypeOf((*MockMetricsEngine)(nil).RecordRequestStatus), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordRequestStatus", reflect.TypeOf((*MockMetricsEngine)(nil).RecordRequestStatus), arg0, arg1) } // RecordWrapperCount mocks base method -func (m *MockMetricsEngine) RecordWrapperCount(arg0, arg1, arg2 string) { +func (m *MockMetricsEngine) RecordWrapperCount(arg0, arg1 string) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordWrapperCount", arg0, arg1, arg2) + m.ctrl.Call(m, "RecordWrapperCount", arg0, arg1) } // RecordWrapperCount indicates an expected call of RecordRequestStatus -func (mr *MockMetricsEngineMockRecorder) RecordWrapperCount(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockMetricsEngineMockRecorder) RecordWrapperCount(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWrapperCount", reflect.TypeOf((*MockMetricsEngine)(nil).RecordWrapperCount), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWrapperCount", reflect.TypeOf((*MockMetricsEngine)(nil).RecordWrapperCount), arg0, arg1) } // RecordRequestTime mocks base method -func (m *MockMetricsEngine) RecordRequestTime(arg0, arg1 string, arg2 time.Duration) { +func (m *MockMetricsEngine) RecordRequestTime(arg0 string, arg1 time.Duration) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordRequestTime", arg0, arg1, arg2) + m.ctrl.Call(m, "RecordRequestTime", arg0, arg1) } // RecordRequestTime indicates an expected call of RecordRequestTime -func (mr *MockMetricsEngineMockRecorder) RecordRequestTime(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockMetricsEngineMockRecorder) RecordRequestTime(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordRequestTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordRequestTime), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordRequestTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordRequestTime), arg0, arg1) } diff --git a/modules/pubmatic/vastunwrap/unwrap_service.go b/modules/pubmatic/vastunwrap/unwrap_service.go index 9b432f5ebb1..186a5100ce6 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(isStatsEnabled bool, bid *adapters.TypedBid, userAgent string, unwrapURL string, accountID string, bidder string) { +func (m VastUnwrapModule) doUnwrapandUpdateBid(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(isStatsEnabled bool, bid *adapter glog.Errorf("AdM:[%s] Error:[%v] stacktrace:[%s]", bid.Bid.AdM, r, string(debug.Stack())) } respTime := time.Since(startTime) - m.MetricsEngine.RecordRequestTime(accountID, bidder, respTime) - m.MetricsEngine.RecordRequestStatus(accountID, bidder, respStatus) + m.MetricsEngine.RecordRequestTime(bidder, respTime) + m.MetricsEngine.RecordRequestStatus(bidder, respStatus) if respStatus == "0" { - m.MetricsEngine.RecordWrapperCount(accountID, bidder, strconv.Itoa(int(wrapperCnt))) + m.MetricsEngine.RecordWrapperCount(bidder, strconv.Itoa(int(wrapperCnt))) } }() headers := http.Header{} @@ -42,8 +42,8 @@ func (m VastUnwrapModule) doUnwrapandUpdateBid(isStatsEnabled bool, bid *adapter m.unwrapRequest(httpResp, httpReq) respStatus = httpResp.Header().Get(UnwrapStatus) wrapperCnt, _ = strconv.ParseInt(httpResp.Header().Get(UnwrapCount), 10, 0) - if !isStatsEnabled && httpResp.Code == http.StatusOK { - respBody := httpResp.Body.Bytes() + respBody := httpResp.Body.Bytes() + if httpResp.Code == http.StatusOK { 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 4025124ab8e..453861b3700 100644 --- a/modules/pubmatic/vastunwrap/unwrap_service_test.go +++ b/modules/pubmatic/vastunwrap/unwrap_service_test.go @@ -21,7 +21,6 @@ func TestDoUnwrap(t *testing.T) { mockMetricsEngine := mock_stats.NewMockMetricsEngine(ctrl) type args struct { module VastUnwrapModule - statsEnabled bool bid *adapters.TypedBid userAgent string unwrapDefaultTimeout int @@ -84,8 +83,8 @@ func TestDoUnwrap(t *testing.T) { url: "testURL", }, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "2") - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "2") + mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()) }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "2") @@ -114,9 +113,9 @@ func TestDoUnwrap(t *testing.T) { wantAdM: true, }, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0") - mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1") - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0") + mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "1") + mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()) }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "0") @@ -146,8 +145,8 @@ func TestDoUnwrap(t *testing.T) { wantAdM: false, }, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "1") - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "1") + mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()) }, unwrapRequest: func(w http.ResponseWriter, req *http.Request) { w.Header().Add("unwrap-status", "1") @@ -168,7 +167,7 @@ func TestDoUnwrap(t *testing.T) { MetricsEngine: mockMetricsEngine, unwrapRequest: tt.unwrapRequest, } - m.doUnwrapandUpdateBid(tt.args.statsEnabled, tt.args.bid, tt.args.userAgent, tt.args.url, "5890", "pubmatic") + m.doUnwrapandUpdateBid(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.") } From 905bfdc667ef0dd07ff6d2ff62d59e65b1ac99bd Mon Sep 17 00:00:00 2001 From: pm-avinash-kapre <112699665+AvinashKapre@users.noreply.github.com> Date: Thu, 23 Nov 2023 15:06:00 +0530 Subject: [PATCH 29/30] UOE-9680: Execute logger and append tracker pixel in creative for prebid.js DIY setup (#649) * UOE-9680: Execute logger and append tracker pixel in creative for prebid.js DIY setup * test case * test cases 2 * test cases 3 --- analytics/pubmatic/record.go | 2 + analytics/pubmatic/record_test.go | 5 + .../pubmatic/openwrap/auctionresponsehook.go | 20 ++- .../openwrap/auctionresponsehook_test.go | 155 ++++++++++++++++++ .../pubmatic/openwrap/beforevalidationhook.go | 6 +- .../openwrap/beforevalidationhook_test.go | 15 +- modules/pubmatic/openwrap/entrypointhook.go | 2 +- .../pubmatic/openwrap/entrypointhook_test.go | 4 +- modules/pubmatic/openwrap/models/constants.go | 3 +- 9 files changed, 199 insertions(+), 13 deletions(-) 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/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..978f7c56f16 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 == "" { @@ -910,6 +910,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..d683cf6a5ec 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, diff --git a/modules/pubmatic/openwrap/entrypointhook.go b/modules/pubmatic/openwrap/entrypointhook.go index cc5d76cd479..e2a18f4ce91 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") 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 From d5e47df7102eea6bc4974c200093f5bd46939ee5 Mon Sep 17 00:00:00 2001 From: Jaydeep Mohite <30924180+pm-jaydeep-mohite@users.noreply.github.com> Date: Fri, 24 Nov 2023 11:27:00 +0530 Subject: [PATCH 30/30] OTT-1441: Removed random number from VAST un-wrap as it will be done in OW server (#642) --- modules/pubmatic/vastunwrap/entryhook.go | 7 +--- modules/pubmatic/vastunwrap/entryhook_test.go | 41 +------------------ modules/pubmatic/vastunwrap/module_test.go | 5 --- 3 files changed, 2 insertions(+), 51 deletions(-) diff --git a/modules/pubmatic/vastunwrap/entryhook.go b/modules/pubmatic/vastunwrap/entryhook.go index 71725cf6a33..975df37de26 100644 --- a/modules/pubmatic/vastunwrap/entryhook.go +++ b/modules/pubmatic/vastunwrap/entryhook.go @@ -2,7 +2,6 @@ package vastunwrap import ( "context" - "math/rand" "runtime/debug" "github.com/golang/glog" @@ -15,10 +14,6 @@ func getVastUnwrapperEnable(ctx context.Context, field string) bool { return vastEnableUnwrapper == "1" } -var getRandomNumber = func() int { - return rand.Intn(100) -} - func handleEntrypointHook( _ context.Context, _ hookstage.ModuleInvocationContext, @@ -31,7 +26,7 @@ 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), } 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/module_test.go b/modules/pubmatic/vastunwrap/module_test.go index 749cf4c4aaf..57e08496eac 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,