From b12cfb5edcb38742e1c70e4945fd41b0db7027e6 Mon Sep 17 00:00:00 2001
From: Pubmatic-Supriya-Patil
<131644110+Pubmatic-Supriya-Patil@users.noreply.github.com>
Date: Fri, 22 Nov 2024 15:37:16 +0530
Subject: [PATCH] UOE-11332: validate bids as per imp.video.protocols for all
the bidders (#963)
---
errortypes/code.go | 1 +
exchange/exchange.go | 5 +
exchange/exchange_ow.go | 53 ++
exchange/exchange_ow_test.go | 651 ++++++++++++++++++
exchange/exchange_test.go | 156 +++++
.../pubmatic/openwrap/beforevalidationhook.go | 13 +-
.../openwrap/beforevalidationhook_test.go | 517 ++++++++++++++
modules/pubmatic/openwrap/models/constants.go | 1 +
modules/pubmatic/openwrap/models/nbr/codes.go | 13 +-
modules/pubmatic/openwrap/util.go | 15 +
modules/pubmatic/openwrap/util_test.go | 83 +++
openrtb_ext/openwrap.go | 1 +
12 files changed, 1502 insertions(+), 7 deletions(-)
diff --git a/errortypes/code.go b/errortypes/code.go
index b6f4aa33728..7a4017f36dc 100644
--- a/errortypes/code.go
+++ b/errortypes/code.go
@@ -41,6 +41,7 @@ const (
SecCookieDeprecationLenWarningCode
SecBrowsingTopicsWarningCode
AdpodPostFilteringWarningCode
+ InvalidVastVersionWarningCode
)
// Coder provides an error or warning code with severity.
diff --git a/exchange/exchange.go b/exchange/exchange.go
index ccd5fa5a65f..3aba94ab8c7 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -426,6 +426,11 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog
recordBids(ctx, e.me, r.PubID, adapterBids)
recordVastVersion(e.me, adapterBids)
+ if requestExtPrebid.StrictVastMode {
+ validationErrs := filterBidsByVastVersion(adapterBids, &seatNonBid)
+ errs = append(errs, validationErrs...)
+ }
+
if e.priceFloorEnabled {
var rejectedBids []*entities.PbsOrtbSeatBid
var enforceErrs []error
diff --git a/exchange/exchange_ow.go b/exchange/exchange_ow.go
index fc9c52e9d72..f8b1b449983 100644
--- a/exchange/exchange_ow.go
+++ b/exchange/exchange_ow.go
@@ -7,6 +7,7 @@ import (
"fmt"
"net/url"
"regexp"
+ "strconv"
"strings"
"github.com/golang/glog"
@@ -14,9 +15,11 @@ import (
"github.com/prebid/prebid-server/v2/adapters"
"github.com/prebid/prebid-server/v2/config"
"github.com/prebid/prebid-server/v2/currency"
+ "github.com/prebid/prebid-server/v2/errortypes"
"github.com/prebid/prebid-server/v2/exchange/entities"
"github.com/prebid/prebid-server/v2/metrics"
pubmaticstats "github.com/prebid/prebid-server/v2/metrics/pubmatic_stats"
+ "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr"
"github.com/prebid/prebid-server/v2/openrtb_ext"
"github.com/prebid/prebid-server/v2/ortb"
"github.com/prebid/prebid-server/v2/util/jsonutil"
@@ -36,6 +39,11 @@ const (
VASTTypeInLineEndTag = ""
)
+var validVastVersions = map[int]bool{
+ 3: true,
+ 4: true,
+}
+
// VASTTagType describes the allowed values for VASTTagType
type VASTTagType string
@@ -378,3 +386,48 @@ func (e exchange) updateSeatNonBidsPriceThreshold(seatNonBids *openrtb_ext.NonBi
}
}
}
+
+func updateSeatNonBidsInvalidVastVersion(seatNonBids *openrtb_ext.NonBidCollection, seat string, rejectedBids []*entities.PbsOrtbBid) {
+ for _, pbsRejBid := range rejectedBids {
+ nonBidParams := entities.GetNonBidParamsFromPbsOrtbBid(pbsRejBid, seat)
+ nonBidParams.NonBidReason = int(nbr.LossBidLostInVastVersionValidation)
+ seatNonBids.AddBid(openrtb_ext.NewNonBid(nonBidParams), seat)
+ }
+}
+
+func filterBidsByVastVersion(adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, seatNonBid *openrtb_ext.NonBidCollection) []error {
+ errs := []error{}
+ for _, seatBid := range adapterBids {
+ rejectedBid := []*entities.PbsOrtbBid{}
+ validBids := make([]*entities.PbsOrtbBid, 0, len(seatBid.Bids))
+ for _, pbsBid := range seatBid.Bids {
+ if pbsBid.BidType == openrtb_ext.BidTypeVideo && pbsBid.Bid.AdM != "" {
+ isValid, vastVersion := validateVastVersion(pbsBid.Bid.AdM)
+ if !isValid {
+ errs = append(errs, &errortypes.Warning{
+ Message: fmt.Sprintf("%s Bid %s was filtered for Imp %s with Vast Version %s: Incompatible with GAM unwinding requirements", seatBid.Seat, pbsBid.Bid.ID, pbsBid.Bid.ImpID, vastVersion),
+ WarningCode: errortypes.InvalidVastVersionWarningCode,
+ })
+ rejectedBid = append(rejectedBid, pbsBid)
+ continue
+ }
+ }
+ validBids = append(validBids, pbsBid)
+ }
+ updateSeatNonBidsInvalidVastVersion(seatNonBid, seatBid.Seat, rejectedBid)
+ seatBid.Bids = validBids
+ }
+ return errs
+}
+
+func validateVastVersion(adM string) (bool, string) {
+ matches := vastVersionRegex.FindStringSubmatch(adM)
+ if len(matches) != 2 {
+ return false, ""
+ }
+ vastVersionFloat, err := strconv.ParseFloat(matches[1], 64)
+ if err != nil {
+ return false, matches[1]
+ }
+ return validVastVersions[int(vastVersionFloat)], matches[1]
+}
diff --git a/exchange/exchange_ow_test.go b/exchange/exchange_ow_test.go
index 6a4337aad3c..2c3243bbbf4 100644
--- a/exchange/exchange_ow_test.go
+++ b/exchange/exchange_ow_test.go
@@ -15,9 +15,11 @@ import (
"github.com/prebid/prebid-server/v2/adapters"
"github.com/prebid/prebid-server/v2/adapters/vastbidder"
"github.com/prebid/prebid-server/v2/config"
+ "github.com/prebid/prebid-server/v2/errortypes"
"github.com/prebid/prebid-server/v2/exchange/entities"
"github.com/prebid/prebid-server/v2/metrics"
metricsConf "github.com/prebid/prebid-server/v2/metrics/config"
+ "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr"
"github.com/prebid/prebid-server/v2/openrtb_ext"
"github.com/prebid/prebid-server/v2/util/ptrutil"
"github.com/stretchr/testify/assert"
@@ -1875,3 +1877,652 @@ func TestRecordFastXMLMetrics(t *testing.T) {
})
}
}
+
+func TestFilterBidsByVastVersion(t *testing.T) {
+ type args struct {
+ adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid
+ seatNonBid *openrtb_ext.NonBidCollection
+ }
+ tests := []struct {
+ name string
+ args args
+ want map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid
+ errs []error
+ }{
+ {
+ name: "valid_vast_version_banner",
+ args: args{
+ adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeBanner,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ seatNonBid: &openrtb_ext.NonBidCollection{},
+ },
+ want: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeBanner,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ errs: []error{},
+ },
+ {
+ name: "invalid_vast_version_banner",
+ args: args{
+ adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeBanner,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ seatNonBid: &openrtb_ext.NonBidCollection{},
+ },
+ want: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeBanner,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ errs: []error{},
+ },
+ {
+ name: "valid_vast_version_video",
+ args: args{
+ adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ seatNonBid: &openrtb_ext.NonBidCollection{},
+ },
+ want: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ errs: []error{},
+ },
+ {
+ name: "multiple_bids_valid_vast_version",
+ args: args{
+ adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid2",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder1",
+ },
+ "bidder2": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid2",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder2",
+ },
+ },
+ seatNonBid: &openrtb_ext.NonBidCollection{},
+ },
+ want: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid2",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder1",
+ },
+ "bidder2": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid2",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder2",
+ },
+ },
+ errs: []error{},
+ },
+ {
+ name: "invalid_vast_version",
+ args: args{
+ adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ seatNonBid: &openrtb_ext.NonBidCollection{},
+ },
+ want: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{},
+ Seat: "bidder1",
+ },
+ },
+ errs: []error{
+ &errortypes.Warning{
+ Message: "bidder1 Bid bid1 was filtered for Imp with Vast Version 2.0: Incompatible with GAM unwinding requirements",
+ WarningCode: errortypes.InvalidVastVersionWarningCode,
+ },
+ },
+ },
+ {
+ name: "multiple_bids_invalid_vast_version",
+ args: args{
+ adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid2",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder1",
+ },
+ "bidder2": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid2",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder2",
+ },
+ },
+ seatNonBid: &openrtb_ext.NonBidCollection{},
+ },
+ want: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{},
+ Seat: "bidder1",
+ },
+ "bidder2": {
+ Bids: []*entities.PbsOrtbBid{},
+ Seat: "bidder2",
+ },
+ },
+ errs: []error{
+ &errortypes.Warning{
+ Message: "bidder1 Bid bid1 was filtered for Imp with Vast Version 0: Incompatible with GAM unwinding requirements",
+ WarningCode: errortypes.InvalidVastVersionWarningCode,
+ },
+ &errortypes.Warning{
+ Message: "bidder1 Bid bid2 was filtered for Imp with Vast Version 1.0: Incompatible with GAM unwinding requirements",
+ WarningCode: errortypes.InvalidVastVersionWarningCode,
+ },
+ &errortypes.Warning{
+ Message: "bidder2 Bid bid1 was filtered for Imp with Vast Version 2.0: Incompatible with GAM unwinding requirements",
+ WarningCode: errortypes.InvalidVastVersionWarningCode,
+ },
+ &errortypes.Warning{
+ Message: "bidder2 Bid bid2 was filtered for Imp with Vast Version 1.0: Incompatible with GAM unwinding requirements",
+ WarningCode: errortypes.InvalidVastVersionWarningCode,
+ },
+ },
+ },
+ {
+ name: "multiple_bids_valid_invalid_vast_version",
+ args: args{
+ adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid2",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder1",
+ },
+ "bidder2": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid2",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder2",
+ },
+ },
+ seatNonBid: &openrtb_ext.NonBidCollection{},
+ },
+ want: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder1",
+ },
+ "bidder2": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid2",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder2",
+ },
+ },
+ errs: []error{
+ &errortypes.Warning{
+ Message: "bidder1 Bid bid2 was filtered for Imp with Vast Version 1.0: Incompatible with GAM unwinding requirements",
+ WarningCode: errortypes.InvalidVastVersionWarningCode,
+ },
+ &errortypes.Warning{
+ Message: "bidder2 Bid bid1 was filtered for Imp with Vast Version 2.0: Incompatible with GAM unwinding requirements",
+ WarningCode: errortypes.InvalidVastVersionWarningCode,
+ },
+ },
+ },
+ {
+ name: "non_video_bid",
+ args: args{
+ adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeBanner,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ seatNonBid: &openrtb_ext.NonBidCollection{},
+ },
+ want: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeBanner,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ errs: []error{},
+ },
+ {
+ name: "empty_adm",
+ args: args{
+ adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: "",
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ seatNonBid: &openrtb_ext.NonBidCollection{},
+ },
+ want: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: "",
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ errs: []error{},
+ },
+ {
+ name: "invalid_vast_version_format",
+ args: args{
+ adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{
+ {
+ Bid: &openrtb2.Bid{
+ ID: "bid1",
+ AdM: ``,
+ },
+ BidType: openrtb_ext.BidTypeVideo,
+ },
+ },
+ Seat: "bidder1",
+ },
+ },
+ seatNonBid: &openrtb_ext.NonBidCollection{},
+ },
+ want: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+ "bidder1": {
+ Bids: []*entities.PbsOrtbBid{},
+ Seat: "bidder1",
+ },
+ },
+ errs: []error{
+ &errortypes.Warning{
+ Message: "bidder1 Bid bid1 was filtered for Imp with Vast Version : Incompatible with GAM unwinding requirements",
+ WarningCode: errortypes.InvalidVastVersionWarningCode,
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ errs := filterBidsByVastVersion(tt.args.adapterBids, tt.args.seatNonBid)
+ want := mapToSlice(tt.want)
+ got := mapToSlice(tt.args.adapterBids)
+ assert.ElementsMatch(t, want, got)
+ assert.ElementsMatch(t, tt.errs, errs)
+ })
+ }
+}
+
+func mapToSlice(bidMap map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid) []*entities.PbsOrtbSeatBid {
+ var result []*entities.PbsOrtbSeatBid
+ for _, bid := range bidMap {
+ result = append(result, bid)
+ }
+ return result
+}
+
+func TestValidateVastVersion(t *testing.T) {
+ tests := []struct {
+ name string
+ adM string
+ expectedValid bool
+ expectedVersion string
+ }{
+ {
+ name: "Valid VAST version 3",
+ adM: ``,
+ expectedValid: true,
+ expectedVersion: "3.0",
+ },
+ {
+ name: "Valid VAST version 4",
+ adM: ``,
+ expectedValid: true,
+ expectedVersion: "4.0",
+ },
+ {
+ name: "Invalid VAST version 2",
+ adM: ``,
+ expectedValid: false,
+ expectedVersion: "2.0",
+ },
+ {
+ name: "Invalid VAST version 5",
+ adM: ``,
+ expectedValid: false,
+ expectedVersion: "5.0",
+ },
+ {
+ name: "No VAST version",
+ adM: ``,
+ expectedValid: false,
+ expectedVersion: "",
+ },
+ {
+ name: "Malformed VAST tag",
+ adM: `"},
+ },
+ },
+ }},
+ expected: testResults{
+ bidFloorCur: "USD",
+ resolvedReq: `{"id":"some-request-id","imp":[{"id":"some-impression-id","bidfloor":15,"bidfloorcur":"USD","ext":{"prebid":{"bidder":{"appnexus":{"placementId":1}}}}}],"site":{"domain":"www.website.com","page":"prebid.org","ext":{"amp":0}},"test":1,"cur":["USD"],"ext":{"prebid":{"strictvastmode":true}}}`,
+ },
+ },
+ {
+ desc: "requestext_has_strict_vast_mode_vast_version_2",
+ req: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+ ID: "some-request-id",
+ Imp: []openrtb2.Imp{{
+ ID: "some-impression-id",
+ BidFloor: 15,
+ BidFloorCur: "USD",
+ Ext: json.RawMessage(`{"prebid":{"bidder":{"appnexus": {"placementId": 1}}}}`),
+ }},
+ Site: &openrtb2.Site{
+ Page: "prebid.org",
+ Ext: json.RawMessage(`{"amp":0}`),
+ Domain: "www.website.com",
+ },
+ Test: 1,
+ Cur: []string{"USD"},
+ Ext: json.RawMessage(`{"prebid":{"strictvastmode":true}}`),
+ }},
+ bidderImpl: &goodSingleBidder{
+ httpRequest: &adapters.RequestData{
+ Method: "POST",
+ Uri: server.URL,
+ Body: []byte(`{"key":"val"}`),
+ Headers: http.Header{},
+ },
+ bidResponse: &adapters.BidderResponse{
+ Bids: []*adapters.TypedBid{
+ {
+ BidType: openrtb_ext.BidTypeVideo,
+ Bid: &openrtb2.Bid{ID: "some-bid-id", AdM: ""},
+ },
+ },
+ }},
+ expected: testResults{
+ bidFloorCur: "USD",
+ resolvedReq: `{"id":"some-request-id","imp":[{"id":"some-impression-id","bidfloor":15,"bidfloorcur":"USD","ext":{"prebid":{"bidder":{"appnexus":{"placementId":1}}}}}],"site":{"domain":"www.website.com","page":"prebid.org","ext":{"amp":0}},"test":1,"cur":["USD"],"ext":{"prebid":{"strictvastmode":true}}}`,
+ errMessage: "appnexus Bid some-bid-id was filtered for Imp with Vast Version 2.0: Incompatible with GAM unwinding requirements",
+ errCode: 10014,
+ },
+ },
+ }
+
+ for _, test := range testCases {
+ auctionRequest := &AuctionRequest{
+ BidRequestWrapper: test.req,
+ Account: config.Account{DebugAllow: true},
+ UserSyncs: &emptyUsersync{},
+ HookExecutor: &hookexecution.EmptyHookExecutor{},
+ TCF2Config: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
+ }
+
+ e.adapterMap = map[openrtb_ext.BidderName]AdaptedBidder{
+ openrtb_ext.BidderAppnexus: AdaptBidder(test.bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderAppnexus, nil, ""),
+ }
+ outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, &DebugLog{})
+ assert.Equal(t, test.expected.err, err, "Error")
+ actualExt := &openrtb_ext.ExtBidResponse{}
+ _ = jsonutil.UnmarshalValid(outBidResponse.Ext, actualExt)
+ if test.expected.errMessage != "" && actualExt.Warnings != nil {
+ assert.NotNil(t, actualExt.Warnings["prebid"], "prebid warning should be present")
+ assert.Equal(t, actualExt.Warnings["prebid"][0].Message, test.expected.errMessage, "Warning Message")
+ assert.Equal(t, actualExt.Warnings["prebid"][0].Code, test.expected.errCode, "Warning Code")
+ }
+ actualResolvedRequest, _, _, _ := jsonparser.Get(outBidResponse.Ext, "debug", "resolvedrequest")
+ assert.JSONEq(t, test.expected.resolvedReq, string(actualResolvedRequest), "Resolved request is incorrect")
+ }
+}
func TestReturnCreativeEndToEnd(t *testing.T) {
sampleAd := ""
diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go
index c500d5970a8..b16d0e8814e 100644
--- a/modules/pubmatic/openwrap/beforevalidationhook.go
+++ b/modules/pubmatic/openwrap/beforevalidationhook.go
@@ -719,7 +719,18 @@ func (m *OpenWrap) applyProfileChanges(rctx models.RequestCtx, bidRequest *openr
bidRequest.App.StoreURL = rctx.AppLovinMax.AppStoreUrl
}
}
-
+ strictVastMode := models.GetVersionLevelPropertyFromPartnerConfig(rctx.PartnerConfigMap, models.StrictVastModeKey) == models.Enabled
+ if strictVastMode {
+ if rctx.NewReqExt == nil {
+ rctx.NewReqExt = &models.RequestExt{}
+ }
+ rctx.NewReqExt.Prebid.StrictVastMode = strictVastMode
+ for i := 0; i < len(bidRequest.Imp); i++ {
+ if bidRequest.Imp[i].Video != nil {
+ bidRequest.Imp[i].Video.Protocols = UpdateImpProtocols(bidRequest.Imp[i].Video.Protocols)
+ }
+ }
+ }
if cur, ok := rctx.PartnerConfigMap[models.VersionLevelConfigID][models.AdServerCurrency]; ok {
bidRequest.Cur = append(bidRequest.Cur, cur)
}
diff --git a/modules/pubmatic/openwrap/beforevalidationhook_test.go b/modules/pubmatic/openwrap/beforevalidationhook_test.go
index 4ea909b1918..878768573e0 100644
--- a/modules/pubmatic/openwrap/beforevalidationhook_test.go
+++ b/modules/pubmatic/openwrap/beforevalidationhook_test.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
+
"net/http"
"sort"
"testing"
@@ -1840,6 +1841,522 @@ func TestOpenWrapApplyProfileChanges(t *testing.T) {
},
wantErr: false,
},
+ {
+ name: "GAM_Unwinding_Enabled",
+ args: args{
+ rctx: models.RequestCtx{
+ IsTestRequest: 1,
+ PartnerConfigMap: map[int]map[string]string{
+ -1: {
+ models.AdServerCurrency: "USD",
+ models.SChainDBKey: "1",
+ models.StrictVastModeKey: models.Enabled,
+ },
+ },
+ TMax: 500,
+ IP: "127.0.0.1",
+ Platform: models.PLATFORM_APP,
+ KADUSERCookie: &http.Cookie{
+ Name: "KADUSERCOOKIE",
+ Value: "123456789",
+ },
+ },
+ bidRequest: &openrtb2.BidRequest{
+ ID: "testID",
+ Test: 1,
+ Cur: []string{"EUR"},
+ TMax: 500,
+ Source: &openrtb2.Source{
+ TID: "testID",
+ },
+ Imp: []openrtb2.Imp{
+ {
+ ID: "testImp1",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ Protocols: []adcom1.MediaCreativeSubtype{1, 2, 3},
+ },
+ },
+ },
+ Device: &openrtb2.Device{
+ IP: "127.0.0.1",
+ Language: "en",
+ DeviceType: 1,
+ },
+ WLang: []string{"en", "hi"},
+ User: &openrtb2.User{
+ CustomData: "123456789",
+ Ext: json.RawMessage(`{"eids":[{"source":"uidapi.com","uids":[{"id":"UID2:"},{"id":""}]},{"source":"euid.eu","uids":[{"id":""}]},{"source":"liveramp.com","uids":[{"id":"IDL:"}]}]}`),
+ },
+ Site: &openrtb2.Site{
+ Publisher: &openrtb2.Publisher{
+ ID: "1010",
+ },
+ Content: &openrtb2.Content{
+ Language: "en",
+ },
+ },
+ },
+ },
+ want: &openrtb2.BidRequest{
+ ID: "testID",
+ Test: 1,
+ Cur: []string{"EUR", "USD"},
+ TMax: 500,
+ Source: &openrtb2.Source{
+ TID: "testID",
+ },
+ Imp: []openrtb2.Imp{
+ {
+ ID: "testImp1",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ Protocols: []adcom1.MediaCreativeSubtype{1, 2, 3, 6, 7, 8},
+ },
+ },
+ },
+ Device: &openrtb2.Device{
+ IP: "127.0.0.1",
+ Language: "en",
+ DeviceType: 1,
+ },
+ WLang: []string{"en", "hi"},
+ User: &openrtb2.User{
+ CustomData: "123456789",
+ Ext: json.RawMessage(`{}`),
+ },
+ Site: &openrtb2.Site{
+ Publisher: &openrtb2.Publisher{
+ ID: "1010",
+ },
+ Content: &openrtb2.Content{
+ Language: "en",
+ },
+ },
+ Ext: json.RawMessage(`{"prebid":{"strictvastmode":true}}`),
+ },
+ wantErr: false,
+ },
+ {
+ name: "GAM_Unwinding_Enabled_Multi_Imp",
+ args: args{
+ rctx: models.RequestCtx{
+ IsTestRequest: 1,
+ PartnerConfigMap: map[int]map[string]string{
+ -1: {
+ models.AdServerCurrency: "USD",
+ models.SChainDBKey: "1",
+ models.StrictVastModeKey: models.Enabled,
+ },
+ },
+ TMax: 500,
+ IP: "127.0.0.1",
+ Platform: models.PLATFORM_APP,
+ KADUSERCookie: &http.Cookie{
+ Name: "KADUSERCOOKIE",
+ Value: "123456789",
+ },
+ },
+ bidRequest: &openrtb2.BidRequest{
+ ID: "testID",
+ Test: 1,
+ Cur: []string{"EUR"},
+ TMax: 500,
+ Source: &openrtb2.Source{
+ TID: "testID",
+ },
+ Imp: []openrtb2.Imp{
+ {
+ ID: "testImp1",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ Protocols: []adcom1.MediaCreativeSubtype{1, 2, 3},
+ },
+ },
+ {
+ ID: "testImp2",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ Protocols: []adcom1.MediaCreativeSubtype{1},
+ },
+ },
+ },
+ Device: &openrtb2.Device{
+ IP: "127.0.0.1",
+ Language: "en",
+ DeviceType: 1,
+ },
+ WLang: []string{"en", "hi"},
+ User: &openrtb2.User{
+ CustomData: "123456789",
+ Ext: json.RawMessage(`{"eids":[{"source":"uidapi.com","uids":[{"id":"UID2:"},{"id":""}]},{"source":"euid.eu","uids":[{"id":""}]},{"source":"liveramp.com","uids":[{"id":"IDL:"}]}]}`),
+ },
+ Site: &openrtb2.Site{
+ Publisher: &openrtb2.Publisher{
+ ID: "1010",
+ },
+ Content: &openrtb2.Content{
+ Language: "en",
+ },
+ },
+ },
+ },
+ want: &openrtb2.BidRequest{
+ ID: "testID",
+ Test: 1,
+ Cur: []string{"EUR", "USD"},
+ TMax: 500,
+ Source: &openrtb2.Source{
+ TID: "testID",
+ },
+ Imp: []openrtb2.Imp{
+ {
+ ID: "testImp1",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ Protocols: []adcom1.MediaCreativeSubtype{1, 2, 3, 6, 7, 8},
+ },
+ },
+ {
+ ID: "testImp2",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ Protocols: []adcom1.MediaCreativeSubtype{1, 3, 6, 7, 8},
+ },
+ },
+ },
+ Device: &openrtb2.Device{
+ IP: "127.0.0.1",
+ Language: "en",
+ DeviceType: 1,
+ },
+ WLang: []string{"en", "hi"},
+ User: &openrtb2.User{
+ CustomData: "123456789",
+ Ext: json.RawMessage(`{}`),
+ },
+ Site: &openrtb2.Site{
+ Publisher: &openrtb2.Publisher{
+ ID: "1010",
+ },
+ Content: &openrtb2.Content{
+ Language: "en",
+ },
+ },
+ Ext: json.RawMessage(`{"prebid":{"strictvastmode":true}}`),
+ },
+ wantErr: false,
+ },
+ {
+ name: "GAM_Unwinding_Enabled_Empty_Protocols",
+ args: args{
+ rctx: models.RequestCtx{
+ IsTestRequest: 1,
+ PartnerConfigMap: map[int]map[string]string{
+ -1: {
+ models.AdServerCurrency: "USD",
+ models.SChainDBKey: "1",
+ models.StrictVastModeKey: models.Enabled,
+ },
+ },
+ TMax: 500,
+ IP: "127.0.0.1",
+ Platform: models.PLATFORM_APP,
+ KADUSERCookie: &http.Cookie{
+ Name: "KADUSERCOOKIE",
+ Value: "123456789",
+ },
+ },
+ bidRequest: &openrtb2.BidRequest{
+ ID: "testID",
+ Test: 1,
+ Cur: []string{"EUR"},
+ TMax: 500,
+ Source: &openrtb2.Source{
+ TID: "testID",
+ },
+ Imp: []openrtb2.Imp{
+ {
+ ID: "testImp1",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ Protocols: []adcom1.MediaCreativeSubtype{},
+ },
+ },
+ },
+ Device: &openrtb2.Device{
+ IP: "127.0.0.1",
+ Language: "en",
+ DeviceType: 1,
+ },
+ WLang: []string{"en", "hi"},
+ User: &openrtb2.User{
+ CustomData: "123456789",
+ Ext: json.RawMessage(`{"eids":[{"source":"uidapi.com","uids":[{"id":"UID2:"},{"id":""}]},{"source":"euid.eu","uids":[{"id":""}]},{"source":"liveramp.com","uids":[{"id":"IDL:"}]}]}`),
+ },
+ Site: &openrtb2.Site{
+ Publisher: &openrtb2.Publisher{
+ ID: "1010",
+ },
+ Content: &openrtb2.Content{
+ Language: "en",
+ },
+ },
+ },
+ },
+ want: &openrtb2.BidRequest{
+ ID: "testID",
+ Test: 1,
+ Cur: []string{"EUR", "USD"},
+ TMax: 500,
+ Source: &openrtb2.Source{
+ TID: "testID",
+ },
+ Imp: []openrtb2.Imp{
+ {
+ ID: "testImp1",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ Protocols: []adcom1.MediaCreativeSubtype{3, 6, 7, 8},
+ },
+ },
+ },
+ Device: &openrtb2.Device{
+ IP: "127.0.0.1",
+ Language: "en",
+ DeviceType: 1,
+ },
+ WLang: []string{"en", "hi"},
+ User: &openrtb2.User{
+ CustomData: "123456789",
+ Ext: json.RawMessage(`{}`),
+ },
+ Site: &openrtb2.Site{
+ Publisher: &openrtb2.Publisher{
+ ID: "1010",
+ },
+ Content: &openrtb2.Content{
+ Language: "en",
+ },
+ },
+ Ext: json.RawMessage(`{"prebid":{"strictvastmode":true}}`),
+ },
+ wantErr: false,
+ },
+ {
+ name: "GAM_Unwinding_Enabled_Protocols_Not_Present",
+ args: args{
+ rctx: models.RequestCtx{
+ IsTestRequest: 1,
+ PartnerConfigMap: map[int]map[string]string{
+ -1: {
+ models.AdServerCurrency: "USD",
+ models.SChainDBKey: "1",
+ models.StrictVastModeKey: models.Enabled,
+ },
+ },
+ TMax: 500,
+ IP: "127.0.0.1",
+ Platform: models.PLATFORM_APP,
+ KADUSERCookie: &http.Cookie{
+ Name: "KADUSERCOOKIE",
+ Value: "123456789",
+ },
+ },
+ bidRequest: &openrtb2.BidRequest{
+ ID: "testID",
+ Test: 1,
+ Cur: []string{"EUR"},
+ TMax: 500,
+ Source: &openrtb2.Source{
+ TID: "testID",
+ },
+ Imp: []openrtb2.Imp{
+ {
+ ID: "testImp1",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ },
+ },
+ },
+ Device: &openrtb2.Device{
+ IP: "127.0.0.1",
+ Language: "en",
+ DeviceType: 1,
+ },
+ WLang: []string{"en", "hi"},
+ User: &openrtb2.User{
+ CustomData: "123456789",
+ Ext: json.RawMessage(`{"eids":[{"source":"uidapi.com","uids":[{"id":"UID2:"},{"id":""}]},{"source":"euid.eu","uids":[{"id":""}]},{"source":"liveramp.com","uids":[{"id":"IDL:"}]}]}`),
+ },
+ Site: &openrtb2.Site{
+ Publisher: &openrtb2.Publisher{
+ ID: "1010",
+ },
+ Content: &openrtb2.Content{
+ Language: "en",
+ },
+ },
+ },
+ },
+ want: &openrtb2.BidRequest{
+ ID: "testID",
+ Test: 1,
+ Cur: []string{"EUR", "USD"},
+ TMax: 500,
+ Source: &openrtb2.Source{
+ TID: "testID",
+ },
+ Imp: []openrtb2.Imp{
+ {
+ ID: "testImp1",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ Protocols: []adcom1.MediaCreativeSubtype{3, 6, 7, 8},
+ },
+ },
+ },
+ Device: &openrtb2.Device{
+ IP: "127.0.0.1",
+ Language: "en",
+ DeviceType: 1,
+ },
+ WLang: []string{"en", "hi"},
+ User: &openrtb2.User{
+ CustomData: "123456789",
+ Ext: json.RawMessage(`{}`),
+ },
+ Site: &openrtb2.Site{
+ Publisher: &openrtb2.Publisher{
+ ID: "1010",
+ },
+ Content: &openrtb2.Content{
+ Language: "en",
+ },
+ },
+ Ext: json.RawMessage(`{"prebid":{"strictvastmode":true}}`),
+ },
+ wantErr: false,
+ },
+ {
+ name: "GAM_Unwinding_Disabled",
+ args: args{
+ rctx: models.RequestCtx{
+ IsTestRequest: 1,
+ PartnerConfigMap: map[int]map[string]string{
+ -1: {
+ models.AdServerCurrency: "USD",
+ models.SChainDBKey: "1",
+ models.StrictVastModeKey: "0",
+ },
+ },
+ TMax: 500,
+ IP: "127.0.0.1",
+ Platform: models.PLATFORM_APP,
+ KADUSERCookie: &http.Cookie{
+ Name: "KADUSERCOOKIE",
+ Value: "123456789",
+ },
+ },
+ bidRequest: &openrtb2.BidRequest{
+ ID: "testID",
+ Test: 1,
+ Cur: []string{"EUR"},
+ TMax: 500,
+ Source: &openrtb2.Source{
+ TID: "testID",
+ },
+ Imp: []openrtb2.Imp{
+ {
+ ID: "testImp1",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ Protocols: []adcom1.MediaCreativeSubtype{1, 2, 3},
+ },
+ },
+ },
+ Device: &openrtb2.Device{
+ IP: "127.0.0.1",
+ Language: "en",
+ DeviceType: 1,
+ },
+ WLang: []string{"en", "hi"},
+ User: &openrtb2.User{
+ CustomData: "123456789",
+ Ext: json.RawMessage(`{"eids":[{"source":"uidapi.com","uids":[{"id":"UID2:"},{"id":""}]},{"source":"euid.eu","uids":[{"id":""}]},{"source":"liveramp.com","uids":[{"id":"IDL:"}]}]}`),
+ },
+ Site: &openrtb2.Site{
+ Publisher: &openrtb2.Publisher{
+ ID: "1010",
+ },
+ Content: &openrtb2.Content{
+ Language: "en",
+ },
+ },
+ },
+ },
+ want: &openrtb2.BidRequest{
+ ID: "testID",
+ Test: 1,
+ Cur: []string{"EUR", "USD"},
+ TMax: 500,
+ Source: &openrtb2.Source{
+ TID: "testID",
+ },
+ Imp: []openrtb2.Imp{
+ {
+ ID: "testImp1",
+ Video: &openrtb2.Video{
+ W: ptrutil.ToPtr[int64](200),
+ H: ptrutil.ToPtr[int64](300),
+ Plcmt: 1,
+ Protocols: []adcom1.MediaCreativeSubtype{1, 2, 3},
+ },
+ },
+ },
+ Device: &openrtb2.Device{
+ IP: "127.0.0.1",
+ Language: "en",
+ DeviceType: 1,
+ },
+ WLang: []string{"en", "hi"},
+ User: &openrtb2.User{
+ CustomData: "123456789",
+ Ext: json.RawMessage(`{}`),
+ },
+ Site: &openrtb2.Site{
+ Publisher: &openrtb2.Publisher{
+ ID: "1010",
+ },
+ Content: &openrtb2.Content{
+ Language: "en",
+ },
+ },
+ },
+ wantErr: false,
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
diff --git a/modules/pubmatic/openwrap/models/constants.go b/modules/pubmatic/openwrap/models/constants.go
index 02812466bea..6ce0d9e277b 100755
--- a/modules/pubmatic/openwrap/models/constants.go
+++ b/modules/pubmatic/openwrap/models/constants.go
@@ -34,6 +34,7 @@ const (
PLATFORM_KEY = "platform"
SendAllBidsKey = "sendAllBids"
VastUnwrapperEnableKey = "enableVastUnwrapper"
+ StrictVastModeKey = "strictVastMode"
VastUnwrapTrafficPercentKey = "vastUnwrapTrafficPercent"
SSTimeoutKey = "ssTimeout"
PWC = "awc"
diff --git a/modules/pubmatic/openwrap/models/nbr/codes.go b/modules/pubmatic/openwrap/models/nbr/codes.go
index df584806b78..0a238ec95f2 100644
--- a/modules/pubmatic/openwrap/models/nbr/codes.go
+++ b/modules/pubmatic/openwrap/models/nbr/codes.go
@@ -4,12 +4,13 @@ import "github.com/prebid/openrtb/v20/openrtb3"
// vendor specific NoBidReasons (500+)
const (
- LossBidLostToHigherBid openrtb3.NoBidReason = 501 // Response Rejected - Lost to Higher Bid
- LossBidLostToDealBid openrtb3.NoBidReason = 502 // Response Rejected - Lost to a Bid for a Deal
- RequestBlockedSlotNotMapped openrtb3.NoBidReason = 503
- RequestBlockedPartnerThrottle openrtb3.NoBidReason = 504
- RequestBlockedPartnerFiltered openrtb3.NoBidReason = 505
- LossBidLostInVastUnwrap openrtb3.NoBidReason = 506
+ LossBidLostToHigherBid openrtb3.NoBidReason = 501 // Response Rejected - Lost to Higher Bid
+ LossBidLostToDealBid openrtb3.NoBidReason = 502 // Response Rejected - Lost to a Bid for a Deal
+ RequestBlockedSlotNotMapped openrtb3.NoBidReason = 503
+ RequestBlockedPartnerThrottle openrtb3.NoBidReason = 504
+ RequestBlockedPartnerFiltered openrtb3.NoBidReason = 505
+ LossBidLostInVastUnwrap openrtb3.NoBidReason = 506
+ LossBidLostInVastVersionValidation openrtb3.NoBidReason = 507
)
// Openwrap module specific codes
diff --git a/modules/pubmatic/openwrap/util.go b/modules/pubmatic/openwrap/util.go
index ea348f9ad98..6bbd24f6523 100644
--- a/modules/pubmatic/openwrap/util.go
+++ b/modules/pubmatic/openwrap/util.go
@@ -10,6 +10,8 @@ import (
"strconv"
"strings"
+ "golang.org/x/exp/slices" // Use standard library in next prebid upgrade
+
"github.com/buger/jsonparser"
"github.com/golang/glog"
"github.com/prebid/openrtb/v20/adcom1"
@@ -56,6 +58,10 @@ const (
test = "_test"
)
+var (
+ protocols = []adcom1.MediaCreativeSubtype{adcom1.CreativeVAST30, adcom1.CreativeVAST30Wrapper, adcom1.CreativeVAST40, adcom1.CreativeVAST40Wrapper}
+)
+
func init() {
widthRegEx = regexp.MustCompile(models.MACRO_WIDTH)
heightRegEx = regexp.MustCompile(models.MACRO_HEIGHT)
@@ -548,3 +554,12 @@ func UpdateUserExtWithValidValues(user *openrtb2.User) {
}
}
}
+
+func UpdateImpProtocols(impProtocols []adcom1.MediaCreativeSubtype) []adcom1.MediaCreativeSubtype {
+ for _, protocol := range protocols {
+ if !slices.Contains(impProtocols, protocol) {
+ impProtocols = append(impProtocols, protocol)
+ }
+ }
+ return impProtocols
+}
diff --git a/modules/pubmatic/openwrap/util_test.go b/modules/pubmatic/openwrap/util_test.go
index 61407b9403e..ff1a8b02b57 100644
--- a/modules/pubmatic/openwrap/util_test.go
+++ b/modules/pubmatic/openwrap/util_test.go
@@ -1925,3 +1925,86 @@ func TestUpdateUserExtWithValidValues(t *testing.T) {
})
}
}
+
+func TestUpdateImpProtocols(t *testing.T) {
+ tests := []struct {
+ name string
+ impProtocols []adcom1.MediaCreativeSubtype
+ want []adcom1.MediaCreativeSubtype
+ }{
+ {
+ name: "Empty_Protocols",
+ impProtocols: []adcom1.MediaCreativeSubtype{},
+ want: []adcom1.MediaCreativeSubtype{
+ adcom1.CreativeVAST30,
+ adcom1.CreativeVAST30Wrapper,
+ adcom1.CreativeVAST40,
+ adcom1.CreativeVAST40Wrapper,
+ },
+ },
+ {
+ name: "VAST20_Protocols_Present",
+ impProtocols: []adcom1.MediaCreativeSubtype{
+ adcom1.CreativeVAST20,
+ },
+ want: []adcom1.MediaCreativeSubtype{
+ adcom1.CreativeVAST20,
+ adcom1.CreativeVAST30,
+ adcom1.CreativeVAST30Wrapper,
+ adcom1.CreativeVAST40,
+ adcom1.CreativeVAST40Wrapper,
+ },
+ },
+ {
+ name: "VAST30_Protocols_Present",
+ impProtocols: []adcom1.MediaCreativeSubtype{
+ adcom1.CreativeVAST30,
+ },
+ want: []adcom1.MediaCreativeSubtype{
+ adcom1.CreativeVAST30,
+ adcom1.CreativeVAST30Wrapper,
+ adcom1.CreativeVAST40,
+ adcom1.CreativeVAST40Wrapper,
+ },
+ },
+ {
+ name: "All_Protocols_Present",
+ impProtocols: []adcom1.MediaCreativeSubtype{
+ adcom1.CreativeVAST30,
+ adcom1.CreativeVAST30Wrapper,
+ adcom1.CreativeVAST40,
+ adcom1.CreativeVAST40Wrapper,
+ },
+ want: []adcom1.MediaCreativeSubtype{
+ adcom1.CreativeVAST30,
+ adcom1.CreativeVAST30Wrapper,
+ adcom1.CreativeVAST40,
+ adcom1.CreativeVAST40Wrapper,
+ },
+ },
+ {
+ name: "Additional_Protocols_Present",
+ impProtocols: []adcom1.MediaCreativeSubtype{
+ adcom1.CreativeVAST30,
+ adcom1.CreativeVAST30Wrapper,
+ adcom1.CreativeVAST40,
+ adcom1.CreativeVAST40Wrapper,
+ adcom1.CreativeVAST20,
+ },
+ want: []adcom1.MediaCreativeSubtype{
+ adcom1.CreativeVAST30,
+ adcom1.CreativeVAST30Wrapper,
+ adcom1.CreativeVAST40,
+ adcom1.CreativeVAST40Wrapper,
+ adcom1.CreativeVAST20,
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := UpdateImpProtocols(tt.impProtocols)
+ assert.ElementsMatch(t, tt.want, got)
+ })
+ }
+}
diff --git a/openrtb_ext/openwrap.go b/openrtb_ext/openwrap.go
index 5371c78cf44..6c71f527150 100644
--- a/openrtb_ext/openwrap.go
+++ b/openrtb_ext/openwrap.go
@@ -43,6 +43,7 @@ type ExtOWRequestPrebid struct {
Transparency *TransparencyExt `json:"transparency,omitempty"`
KeyVal map[string]interface{} `json:"keyval,omitempty"`
TrackerDisabled bool `json:"tracker_disabled,omitempty"`
+ StrictVastMode bool `json:"strictvastmode,omitempty"`
}
// ExtCTVBid defines the contract for bidresponse.seatbid.bid[i].ext