diff --git a/adapters/pubmatic/pubmatic.go b/adapters/pubmatic/pubmatic.go index 84eec48e74..5e533080f4 100644 --- a/adapters/pubmatic/pubmatic.go +++ b/adapters/pubmatic/pubmatic.go @@ -7,7 +7,6 @@ import ( "math" "net/http" "net/url" - "regexp" "strconv" "strings" @@ -24,22 +23,18 @@ import ( const MAX_IMPRESSIONS_PUBMATIC = 30 const MAX_MULTIFLOORS_PUBMATIC = 3 -var re = regexp.MustCompile(appLovinMaxImpressionPattern) - const ( - ae = "ae" - PUBMATIC = "[PUBMATIC]" - buyId = "buyid" - buyIdTargetingKey = "hb_buyid_" - skAdnetworkKey = "skadn" - rewardKey = "reward" - dctrKeywordName = "dctr" - urlEncodedEqualChar = "%3D" - AdServerKey = "adserver" - PBAdslotKey = "pbadslot" - bidViewability = "bidViewability" - multiFloors = "_mf" - appLovinMaxImpressionPattern = "_mf.*" + ae = "ae" + PUBMATIC = "[PUBMATIC]" + buyId = "buyid" + buyIdTargetingKey = "hb_buyid_" + skAdnetworkKey = "skadn" + rewardKey = "reward" + dctrKeywordName = "dctr" + urlEncodedEqualChar = "%3D" + AdServerKey = "adserver" + PBAdslotKey = "pbadslot" + bidViewability = "bidViewability" ) type PubmaticAdapter struct { @@ -264,44 +259,6 @@ func (a *PubmaticAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ad return requestData, errs } -// buildMultiFloorRequests builds multiple requests for each floor value -func (a *PubmaticAdapter) buildMultiFloorRequests(request *openrtb2.BidRequest, impFloorsMap map[string][]float64, cookies []string) ([]*adapters.RequestData, []error) { - requestData := []*adapters.RequestData{} - errs := make([]error, 0, MAX_MULTIFLOORS_PUBMATIC*len(request.Imp)) - - for i := 0; i < MAX_MULTIFLOORS_PUBMATIC; i++ { - isFloorsUpdated := false - newImps := make([]openrtb2.Imp, len(request.Imp)) - copy(newImps, request.Imp) - //TODO-AK: Remove the imp from the request if the floor is not present except for the first floor - for j := range newImps { - floors, ok := impFloorsMap[request.Imp[j].ID] - if !ok || len(floors) <= i { - continue - } - isFloorsUpdated = true - newImps[j].BidFloor = floors[i] - newImps[j].ID = fmt.Sprintf("%s"+multiFloors+"%d", newImps[j].ID, i+1) - } - - if !isFloorsUpdated { - continue - } - - newRequest := *request - newRequest.Imp = newImps - - newRequestData, errData := a.buildAdapterRequest(&newRequest, cookies) - if errData != nil { - errs = append(errs, errData) - } - if len(newRequestData) > 0 { - requestData = append(requestData, newRequestData...) - } - } - return requestData, errs -} - // buildAdapterRequest builds the request for Pubmatic func (a *PubmaticAdapter) buildAdapterRequest(request *openrtb2.BidRequest, cookies []string) ([]*adapters.RequestData, error) { reqJSON, err := json.Marshal(request) @@ -645,7 +602,12 @@ func (a *PubmaticAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa targets := getTargetingKeys(sb.Ext, string(externalRequest.BidderName)) for i := 0; i < len(sb.Bid); i++ { bid := sb.Bid[i] - bid.ImpID = trimSuffixWithPattern(bid.ImpID) + + //If imp is multi-floored then update the bid.ext.mbmfv with the floor value + if appLovinMaxImpressionRegex.MatchString(bid.ImpID) { + bid.Ext = updateBidExtWithMultiFloor(bid.ImpID, bid.Ext, externalRequest.Body) + bid.ImpID = trimSuffixWithPattern(bid.ImpID) + } bid.Ext = renameTransparencyParamsKey(bid.Ext) // Copy SeatBid Ext to Bid.Ext @@ -709,10 +671,6 @@ func (a *PubmaticAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa return bidResponse, errs } -func trimSuffixWithPattern(input string) string { - return re.ReplaceAllString(input, "") -} - func getNativeAdm(adm string) (string, error) { var err error nativeAdm := make(map[string]interface{}) diff --git a/adapters/pubmatic/pubmatic_ow.go b/adapters/pubmatic/pubmatic_ow.go index cc52e1d182..7a3e99a19b 100644 --- a/adapters/pubmatic/pubmatic_ow.go +++ b/adapters/pubmatic/pubmatic_ow.go @@ -4,16 +4,21 @@ import ( "bytes" "encoding/json" "fmt" + "regexp" "strconv" "github.com/buger/jsonparser" "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" "github.com/prebid/prebid-server/v2/openrtb_ext" ) const ( - dsaKey = "dsa" - transparencyKey = "transparency" + dsaKey = "dsa" + transparencyKey = "transparency" + multiFloors = "_mf" + appLovinMaxImpressionPattern = `_mf[0-9]+$` + multiBidMultiFloorValueKey = "mbmfv" ) var ( @@ -21,6 +26,8 @@ var ( dsaParamKey = []byte(`"dsaparams"`) ) +var appLovinMaxImpressionRegex = regexp.MustCompile(appLovinMaxImpressionPattern) + func getTargetingKeys(bidExt json.RawMessage, bidderName string) map[string]string { targets := map[string]string{} if bidExt != nil { @@ -102,3 +109,77 @@ func renameTransparencyParamsKey(bidExt []byte) []byte { return bidExt } + +// buildMultiFloorRequests builds multiple requests for each floor value +func (a *PubmaticAdapter) buildMultiFloorRequests(request *openrtb2.BidRequest, impFloorsMap map[string][]float64, cookies []string) ([]*adapters.RequestData, []error) { + requestData := make([]*adapters.RequestData, 0, MAX_MULTIFLOORS_PUBMATIC*len(request.Imp)) + errs := make([]error, 0, MAX_MULTIFLOORS_PUBMATIC*len(request.Imp)) + + for i := 0; i < MAX_MULTIFLOORS_PUBMATIC; i++ { + isFloorsUpdated := false + newImps := make([]openrtb2.Imp, len(request.Imp)) + copy(newImps, request.Imp) + //TODO-AK: Remove the imp from the request if the floor is not present except for the first floor + for j := range newImps { + floors, ok := impFloorsMap[request.Imp[j].ID] + if !ok || len(floors) <= i { + continue + } + isFloorsUpdated = true + newImps[j].BidFloor = floors[i] + newImps[j].ID = fmt.Sprintf("%s"+multiFloors+"%d", newImps[j].ID, i+1) + } + + if !isFloorsUpdated { + continue + } + + newRequest := *request + newRequest.Imp = newImps + + newRequestData, errData := a.buildAdapterRequest(&newRequest, cookies) + if errData != nil { + errs = append(errs, errData) + } + if len(newRequestData) > 0 { + requestData = append(requestData, newRequestData...) + } + } + return requestData, errs +} + +func trimSuffixWithPattern(input string) string { + return appLovinMaxImpressionRegex.ReplaceAllString(input, "") +} + +func updateBidExtWithMultiFloor(bidImpID string, bidExt, reqBody []byte) []byte { + var externalRequest openrtb2.BidRequest + + if err := json.Unmarshal(reqBody, &externalRequest); err != nil { + return bidExt + } + + updatedBidExt := bidExt + if bidExt == nil { + updatedBidExt = json.RawMessage(`{}`) + } + + for _, imp := range externalRequest.Imp { + if imp.ID != bidImpID { + continue + } + if imp.BidFloor <= 0 { + continue + } + var err error + updatedBidExt, err = jsonparser.Set(updatedBidExt, []byte(fmt.Sprintf("%f", imp.BidFloor)), multiBidMultiFloorValueKey) + if err != nil { + return bidExt + } + } + + if string(updatedBidExt) != "{}" { + return updatedBidExt + } + return bidExt +} diff --git a/adapters/pubmatic/pubmatic_ow_test.go b/adapters/pubmatic/pubmatic_ow_test.go index e46ccc70a5..f62b92d782 100644 --- a/adapters/pubmatic/pubmatic_ow_test.go +++ b/adapters/pubmatic/pubmatic_ow_test.go @@ -9,6 +9,7 @@ import ( "github.com/prebid/openrtb/v20/openrtb2" "github.com/prebid/prebid-server/v2/adapters" "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -474,6 +475,48 @@ func TestPubmaticMakeBids(t *testing.T) { Currency: "USD", }, }, + { + name: "MultiBid MultiFloor request", + args: args{ + response: &adapters.ResponseData{ + StatusCode: http.StatusOK, + Body: []byte(`{"id": "test-request-id", "seatbid":[{"seat": "958", "bid":[{"id": "7706636740145184841", "impid": "test-imp-id_mf1", "price": 0.500000, "adid": "29681110", "adm": "some-test-ad", "adomain":["pubmatic.com"], "crid": "29681110", "h": 250, "w": 300, "dealid": "testdeal", "ext":{}}]}], "bidid": "5778926625248726496", "cur": "USD"}`), + }, + externalRequest: &adapters.RequestData{ + BidderName: openrtb_ext.BidderPubmatic, + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":0.12,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + }, + }, + wantErr: nil, + wantResp: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + ID: "7706636740145184841", + ImpID: "test-imp-id", + Price: 0.500000, + AdID: "29681110", + AdM: "some-test-ad", + ADomain: []string{"pubmatic.com"}, + CrID: "29681110", + H: 250, + W: 300, + DealID: "testdeal", + Ext: json.RawMessage(`{"mbmfv":0.120000}`), + }, + BidType: openrtb_ext.BidTypeBanner, + BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, + BidTargets: map[string]string{}, + BidMeta: &openrtb_ext.ExtBidPrebidMeta{ + AdvertiserID: 958, + AgencyID: 958, + MediaType: "banner", + }, + }, + }, + Currency: "USD", + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -486,3 +529,545 @@ func TestPubmaticMakeBids(t *testing.T) { }) } } + +func TestPubmaticAdapter_buildMultiFloorRequests(t *testing.T) { + type fields struct { + URI string + bidderName string + } + type args struct { + request *openrtb2.BidRequest + impFloorsMap map[string][]float64 + cookies []string + } + tests := []struct { + name string + fields fields + args args + wantRequestData []*adapters.RequestData + wantError []error + }{ + { + name: "request with single imp and single floor", + fields: fields{ + URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + bidderName: "pubmatic", + }, + args: args{ + request: &openrtb2.BidRequest{ + ID: "test-request-id", + App: &openrtb2.App{ + Name: "AutoScout24", + Bundle: "com.autoscout24", + StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", + }, + Imp: []openrtb2.Imp{ + { + ID: "test-imp-id", + BidFloor: 0.12, + Banner: &openrtb2.Banner{ + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), + }, + Ext: json.RawMessage(`{}`), + }, + }, + }, + impFloorsMap: map[string][]float64{ + "test-imp-id": {1.2}, + }, + cookies: []string{"test-cookie"}, + }, + wantRequestData: []*adapters.RequestData{ + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf1"}, + }, + }, + wantError: []error{}, + }, + { + name: "request with single imp and two floors", + fields: fields{ + URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + bidderName: "pubmatic", + }, + args: args{ + request: &openrtb2.BidRequest{ + ID: "test-request-id", + App: &openrtb2.App{ + Name: "AutoScout24", + Bundle: "com.autoscout24", + StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", + }, + Imp: []openrtb2.Imp{ + { + ID: "test-imp-id", + BidFloor: 0.12, + Banner: &openrtb2.Banner{ + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), + }, + Ext: json.RawMessage(`{}`), + }, + }, + }, + impFloorsMap: map[string][]float64{ + "test-imp-id": {1.2, 1.3}, + }, + cookies: []string{"test-cookie"}, + }, + wantRequestData: []*adapters.RequestData{ + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf1"}, + }, + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf2","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf2"}, + }, + }, + wantError: []error{}, + }, + { + name: "request with single imp and max multi floors(3)", + fields: fields{ + URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + bidderName: "pubmatic", + }, + args: args{ + request: &openrtb2.BidRequest{ + ID: "test-request-id", + App: &openrtb2.App{ + Name: "AutoScout24", + Bundle: "com.autoscout24", + StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", + }, + Imp: []openrtb2.Imp{ + { + ID: "test-imp-id", + BidFloor: 0.12, + Banner: &openrtb2.Banner{ + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), + }, + Ext: json.RawMessage(`{}`), + }, + }, + }, + impFloorsMap: map[string][]float64{ + "test-imp-id": {1.2, 1.3, 1.4}, + }, + cookies: []string{"test-cookie"}, + }, + wantRequestData: []*adapters.RequestData{ + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf1"}, + }, + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf2","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf2"}, + }, + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf3","banner":{"w":300,"h":250},"bidfloor":1.4,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf3"}, + }, + }, + wantError: []error{}, + }, + { + name: "request with multiple imp and single floor", + fields: fields{ + URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + bidderName: "pubmatic", + }, + args: args{ + request: &openrtb2.BidRequest{ + ID: "test-request-id", + App: &openrtb2.App{ + Name: "AutoScout24", + Bundle: "com.autoscout24", + StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", + }, + Imp: []openrtb2.Imp{ + { + ID: "test-imp-id", + BidFloor: 0.12, + Banner: &openrtb2.Banner{ + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), + }, + Ext: json.RawMessage(`{}`), + }, + { + ID: "test-imp-id2", + BidFloor: 0.13, + Banner: &openrtb2.Banner{ + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), + }, + Ext: json.RawMessage(`{}`), + }, + }, + }, + impFloorsMap: map[string][]float64{ + "test-imp-id": {1.2}, + "test-imp-id2": {1.3}, + }, + cookies: []string{"test-cookie"}, + }, + wantRequestData: []*adapters.RequestData{ + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}},{"id":"test-imp-id2_mf1","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf1", "test-imp-id2_mf1"}, + }, + }, + wantError: []error{}, + }, + { + name: "request with multiple imp and 3 floors (max) for only one imp", + fields: fields{ + URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + bidderName: "pubmatic", + }, + args: args{ + request: &openrtb2.BidRequest{ + ID: "test-request-id", + App: &openrtb2.App{ + Name: "AutoScout24", + Bundle: "com.autoscout24", + StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", + }, + Imp: []openrtb2.Imp{ + { + ID: "test-imp-id", + BidFloor: 0.12, + Banner: &openrtb2.Banner{ + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), + }, + Ext: json.RawMessage(`{}`), + }, + { + ID: "test-imp-id2", + BidFloor: 0.34, + Banner: &openrtb2.Banner{ + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), + }, + Ext: json.RawMessage(`{}`), + }, + }, + }, + impFloorsMap: map[string][]float64{ + "test-imp-id": {1.2, 1.3, 1.4}, + }, + cookies: []string{"test-cookie"}, + }, + wantRequestData: []*adapters.RequestData{ + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}},{"id":"test-imp-id2","banner":{"w":300,"h":250},"bidfloor":0.34,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf1", "test-imp-id2"}, + }, + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf2","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}},{"id":"test-imp-id2","banner":{"w":300,"h":250},"bidfloor":0.34,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf2", "test-imp-id2"}, + }, + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf3","banner":{"w":300,"h":250},"bidfloor":1.4,"ext":{}},{"id":"test-imp-id2","banner":{"w":300,"h":250},"bidfloor":0.34,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf3", "test-imp-id2"}, + }, + }, + wantError: []error{}, + }, + { + name: "request with multiple imp with 3 floors for one imp and 2 floors for another imp", + fields: fields{ + URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + bidderName: "pubmatic", + }, + args: args{ + request: &openrtb2.BidRequest{ + ID: "test-request-id", + App: &openrtb2.App{ + Name: "AutoScout24", + Bundle: "com.autoscout24", + StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", + }, + Imp: []openrtb2.Imp{ + { + ID: "test-imp-id", + BidFloor: 0.12, + Banner: &openrtb2.Banner{ + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), + }, + Ext: json.RawMessage(`{}`), + }, + { + ID: "test-imp-id2", + BidFloor: 0.34, + Banner: &openrtb2.Banner{ + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), + }, + Ext: json.RawMessage(`{}`), + }, + }, + }, + impFloorsMap: map[string][]float64{ + "test-imp-id": {1.2, 1.3, 1.4}, + "test-imp-id2": {1.2, 1.3}, + }, + cookies: []string{"test-cookie"}, + }, + wantRequestData: []*adapters.RequestData{ + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}},{"id":"test-imp-id2_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf1", "test-imp-id2_mf1"}, + }, + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf2","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}},{"id":"test-imp-id2_mf2","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf2", "test-imp-id2_mf2"}, + }, + { + Method: "POST", + Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", + Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf3","banner":{"w":300,"h":250},"bidfloor":1.4,"ext":{}},{"id":"test-imp-id2","banner":{"w":300,"h":250},"bidfloor":0.34,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), + Headers: http.Header{ + "Content-Type": []string{"application/json;charset=utf-8"}, + "Accept": []string{"application/json"}, + "Cookie": []string{"test-cookie"}, + }, + ImpIDs: []string{"test-imp-id_mf3", "test-imp-id2"}, + }, + }, + wantError: []error{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &PubmaticAdapter{ + URI: tt.fields.URI, + bidderName: tt.fields.bidderName, + } + gotRequestData, gotError := a.buildMultiFloorRequests(tt.args.request, tt.args.impFloorsMap, tt.args.cookies) + assert.Equal(t, tt.wantRequestData, gotRequestData) + assert.Equal(t, tt.wantError, gotError) + }) + } +} + +func TestTrimSuffixWithPattern(t *testing.T) { + type args struct { + input string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "input string is empty", + args: args{ + input: "", + }, + want: "", + }, + { + name: "input string does not contain pattern", + args: args{ + input: "div123456789", + }, + want: "div123456789", + }, + { + name: "input string contains pattern", + args: args{ + input: "div123456789_mf1", + }, + want: "div123456789", + }, + { + name: "input string contains pattern at the end", + args: args{ + input: "div123456789_mf1_mf2", + }, + want: "div123456789_mf1", + }, + { + name: "input string contains pattern at the start", + args: args{ + input: "mf1_mf2_mf123456789", + }, + want: "mf1_mf2", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := trimSuffixWithPattern(tt.args.input) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_updateBidExtWithMultiFloor(t *testing.T) { + type args struct { + bidImpID string + bidExt []byte + reqBody []byte + } + tests := []struct { + name string + args args + want []byte + }{ + { + name: "empty request body", + args: args{ + bidImpID: "test-imp-id", + reqBody: []byte(``), + bidExt: []byte(`{"buyid":"testBuyId","deal_channel":1,"dsa":{"transparency":[{"dsaparams":[1,2]}]},"dspid":6,"prebiddealpriority":1}`), + }, + want: []byte(`{"buyid":"testBuyId","deal_channel":1,"dsa":{"transparency":[{"dsaparams":[1,2]}]},"dspid":6,"prebiddealpriority":1}`), + }, + { + name: "request body with no imp", + args: args{ + bidImpID: "test-imp-id", + reqBody: []byte(`{"id":"test-request-id"}`), + bidExt: []byte(`{"buyid":"testBuyId","deal_channel":1,"dsa":{"transparency":[{"dsaparams":[1,2]}]},"dspid":6,"prebiddealpriority":1}`), + }, + want: []byte(`{"buyid":"testBuyId","deal_channel":1,"dsa":{"transparency":[{"dsaparams":[1,2]}]},"dspid":6,"prebiddealpriority":1}`), + }, + { + name: "request body with imp but no matching imp with bidImpID", + args: args{ + bidImpID: "test-imp-id", + reqBody: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id2","banner":{"w":300,"h":250},"bidfloor":0.12,"ext":{}}]}`), + bidExt: []byte(`{"buyid":"testBuyId","deal_channel":1,"dsa":{"transparency":[{"dsaparams":[1,2]}]},"dspid":6,"prebiddealpriority":1}`), + }, + want: []byte(`{"buyid":"testBuyId","deal_channel":1,"dsa":{"transparency":[{"dsaparams":[1,2]}]},"dspid":6,"prebiddealpriority":1}`), + }, + { + name: "request body with imp and matching imp with bidImpID", + args: args{ + bidImpID: "test-imp-id", + reqBody: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id","banner":{"w":300,"h":250},"bidfloor":0.12,"ext":{}}]}`), + bidExt: []byte(`{"buyid":"testBuyId","deal_channel":1,"dsa":{"transparency":[{"dsaparams":[1,2]}]},"dspid":6,"prebiddealpriority":1}`), + }, + want: []byte(`{"buyid":"testBuyId","deal_channel":1,"dsa":{"transparency":[{"dsaparams":[1,2]}]},"dspid":6,"prebiddealpriority":1,"mbmfv":0.120000}`), + }, + { + name: "request body with multiple imp and matching imp with bidImpID", + args: args{ + bidImpID: "test-imp-id", + reqBody: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id","banner":{"w":300,"h":250},"bidfloor":0.12,"ext":{}},{"id":"test-imp-id2","banner":{"w":300,"h":250},"bidfloor":0.13,"ext":{}}]}`), + bidExt: []byte(`{"buyid":"testBuyId","deal_channel":1,"dsa":{"transparency":[{"dsaparams":[1,2]}]},"dspid":6,"prebiddealpriority":1}`), + }, + want: []byte(`{"buyid":"testBuyId","deal_channel":1,"dsa":{"transparency":[{"dsaparams":[1,2]}]},"dspid":6,"prebiddealpriority":1,"mbmfv":0.120000}`), + }, + { + name: "request body with imp and matching imp with bidImpID and no bidExt", + args: args{ + bidImpID: "test-imp-id", + reqBody: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id","banner":{"w":300,"h":250},"bidfloor":0.12,"ext":{}}]}`), + bidExt: nil, + }, + want: []byte(`{"mbmfv":0.120000}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := updateBidExtWithMultiFloor(tt.args.bidImpID, tt.args.bidExt, tt.args.reqBody) + assert.Equal(t, tt.want, got) + }) + } +} + +//Need to write happy path test cases with nil bidExt diff --git a/adapters/pubmatic/pubmatic_test.go b/adapters/pubmatic/pubmatic_test.go index 984458017e..6bf8a8bf06 100644 --- a/adapters/pubmatic/pubmatic_test.go +++ b/adapters/pubmatic/pubmatic_test.go @@ -659,40 +659,6 @@ func TestPubmaticAdapter_MakeBids(t *testing.T) { Currency: "USD", }, }, - { - name: "response impid has suffix(mf1)", - args: args{ - response: &adapters.ResponseData{ - StatusCode: http.StatusOK, - Body: []byte(`{"id": "test-request-id", "seatbid":[{"seat": "958", "bid":[{"id": "7706636740145184841", "impid": "test-imp-id_mf1", "price": 0.500000, "adid": "29681110", "adm": "some-test-ad", "adomain":["pubmatic.com"], "crid": "29681110", "h": 250, "w": 300, "dealid": "testdeal", "ext":null}]}], "bidid": "5778926625248726496", "cur": "USD"}`), - }, - externalRequest: &adapters.RequestData{BidderName: openrtb_ext.BidderPubmatic}, - }, - wantErr: nil, - wantResp: &adapters.BidderResponse{ - Bids: []*adapters.TypedBid{ - { - Bid: &openrtb2.Bid{ - ID: "7706636740145184841", - ImpID: "test-imp-id", - Price: 0.500000, - AdID: "29681110", - AdM: "some-test-ad", - ADomain: []string{"pubmatic.com"}, - CrID: "29681110", - H: 250, - W: 300, - DealID: "testdeal", - Ext: json.RawMessage(`null`), - }, - BidType: openrtb_ext.BidTypeBanner, - BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, - BidTargets: map[string]string{}, - }, - }, - Currency: "USD", - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -996,419 +962,6 @@ func TestGetMapFromJSON(t *testing.T) { } } -func TestPubmaticAdapter_buildMultiFloorRequests(t *testing.T) { - type fields struct { - URI string - bidderName string - } - type args struct { - request *openrtb2.BidRequest - impFloorsMap map[string][]float64 - cookies []string - } - tests := []struct { - name string - fields fields - args args - wantRequestData []*adapters.RequestData - wantError []error - }{ - { - name: "request with single imp and single floor", - fields: fields{ - URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - bidderName: "pubmatic", - }, - args: args{ - request: &openrtb2.BidRequest{ - ID: "test-request-id", - App: &openrtb2.App{ - Name: "AutoScout24", - Bundle: "com.autoscout24", - StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", - }, - Imp: []openrtb2.Imp{ - { - ID: "test-imp-id", - BidFloor: 0.12, - Banner: &openrtb2.Banner{ - W: ptrutil.ToPtr[int64](300), - H: ptrutil.ToPtr[int64](250), - }, - Ext: json.RawMessage(`{}`), - }, - }, - }, - impFloorsMap: map[string][]float64{ - "test-imp-id": {1.2}, - }, - cookies: []string{"test-cookie"}, - }, - wantRequestData: []*adapters.RequestData{ - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf1"}, - }, - }, - wantError: []error{}, - }, - { - name: "request with single imp and two floors", - fields: fields{ - URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - bidderName: "pubmatic", - }, - args: args{ - request: &openrtb2.BidRequest{ - ID: "test-request-id", - App: &openrtb2.App{ - Name: "AutoScout24", - Bundle: "com.autoscout24", - StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", - }, - Imp: []openrtb2.Imp{ - { - ID: "test-imp-id", - BidFloor: 0.12, - Banner: &openrtb2.Banner{ - W: ptrutil.ToPtr[int64](300), - H: ptrutil.ToPtr[int64](250), - }, - Ext: json.RawMessage(`{}`), - }, - }, - }, - impFloorsMap: map[string][]float64{ - "test-imp-id": {1.2, 1.3}, - }, - cookies: []string{"test-cookie"}, - }, - wantRequestData: []*adapters.RequestData{ - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf1"}, - }, - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf2","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf2"}, - }, - }, - wantError: []error{}, - }, - { - name: "request with single imp and max multi floors(3)", - fields: fields{ - URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - bidderName: "pubmatic", - }, - args: args{ - request: &openrtb2.BidRequest{ - ID: "test-request-id", - App: &openrtb2.App{ - Name: "AutoScout24", - Bundle: "com.autoscout24", - StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", - }, - Imp: []openrtb2.Imp{ - { - ID: "test-imp-id", - BidFloor: 0.12, - Banner: &openrtb2.Banner{ - W: ptrutil.ToPtr[int64](300), - H: ptrutil.ToPtr[int64](250), - }, - Ext: json.RawMessage(`{}`), - }, - }, - }, - impFloorsMap: map[string][]float64{ - "test-imp-id": {1.2, 1.3, 1.4}, - }, - cookies: []string{"test-cookie"}, - }, - wantRequestData: []*adapters.RequestData{ - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf1"}, - }, - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf2","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf2"}, - }, - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf3","banner":{"w":300,"h":250},"bidfloor":1.4,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf3"}, - }, - }, - wantError: []error{}, - }, - { - name: "request with multiple imp and single floor", - fields: fields{ - URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - bidderName: "pubmatic", - }, - args: args{ - request: &openrtb2.BidRequest{ - ID: "test-request-id", - App: &openrtb2.App{ - Name: "AutoScout24", - Bundle: "com.autoscout24", - StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", - }, - Imp: []openrtb2.Imp{ - { - ID: "test-imp-id", - BidFloor: 0.12, - Banner: &openrtb2.Banner{ - W: ptrutil.ToPtr[int64](300), - H: ptrutil.ToPtr[int64](250), - }, - Ext: json.RawMessage(`{}`), - }, - { - ID: "test-imp-id2", - BidFloor: 0.13, - Banner: &openrtb2.Banner{ - W: ptrutil.ToPtr[int64](300), - H: ptrutil.ToPtr[int64](250), - }, - Ext: json.RawMessage(`{}`), - }, - }, - }, - impFloorsMap: map[string][]float64{ - "test-imp-id": {1.2}, - "test-imp-id2": {1.3}, - }, - cookies: []string{"test-cookie"}, - }, - wantRequestData: []*adapters.RequestData{ - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}},{"id":"test-imp-id2_mf1","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf1", "test-imp-id2_mf1"}, - }, - }, - wantError: []error{}, - }, - { - name: "request with multiple imp and 3 floors (max) for only one imp", - fields: fields{ - URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - bidderName: "pubmatic", - }, - args: args{ - request: &openrtb2.BidRequest{ - ID: "test-request-id", - App: &openrtb2.App{ - Name: "AutoScout24", - Bundle: "com.autoscout24", - StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", - }, - Imp: []openrtb2.Imp{ - { - ID: "test-imp-id", - BidFloor: 0.12, - Banner: &openrtb2.Banner{ - W: ptrutil.ToPtr[int64](300), - H: ptrutil.ToPtr[int64](250), - }, - Ext: json.RawMessage(`{}`), - }, - { - ID: "test-imp-id2", - BidFloor: 0.34, - Banner: &openrtb2.Banner{ - W: ptrutil.ToPtr[int64](300), - H: ptrutil.ToPtr[int64](250), - }, - Ext: json.RawMessage(`{}`), - }, - }, - }, - impFloorsMap: map[string][]float64{ - "test-imp-id": {1.2, 1.3, 1.4}, - }, - cookies: []string{"test-cookie"}, - }, - wantRequestData: []*adapters.RequestData{ - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}},{"id":"test-imp-id2","banner":{"w":300,"h":250},"bidfloor":0.34,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf1", "test-imp-id2"}, - }, - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf2","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}},{"id":"test-imp-id2","banner":{"w":300,"h":250},"bidfloor":0.34,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf2", "test-imp-id2"}, - }, - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf3","banner":{"w":300,"h":250},"bidfloor":1.4,"ext":{}},{"id":"test-imp-id2","banner":{"w":300,"h":250},"bidfloor":0.34,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf3", "test-imp-id2"}, - }, - }, - wantError: []error{}, - }, - { - name: "request with multiple imp with 3 floors for one imp and 2 floors for another imp", - fields: fields{ - URI: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - bidderName: "pubmatic", - }, - args: args{ - request: &openrtb2.BidRequest{ - ID: "test-request-id", - App: &openrtb2.App{ - Name: "AutoScout24", - Bundle: "com.autoscout24", - StoreURL: "https://play.google.com/store/apps/details?id=com.autoscout24&hl=fr", - }, - Imp: []openrtb2.Imp{ - { - ID: "test-imp-id", - BidFloor: 0.12, - Banner: &openrtb2.Banner{ - W: ptrutil.ToPtr[int64](300), - H: ptrutil.ToPtr[int64](250), - }, - Ext: json.RawMessage(`{}`), - }, - { - ID: "test-imp-id2", - BidFloor: 0.34, - Banner: &openrtb2.Banner{ - W: ptrutil.ToPtr[int64](300), - H: ptrutil.ToPtr[int64](250), - }, - Ext: json.RawMessage(`{}`), - }, - }, - }, - impFloorsMap: map[string][]float64{ - "test-imp-id": {1.2, 1.3, 1.4}, - "test-imp-id2": {1.2, 1.3}, - }, - cookies: []string{"test-cookie"}, - }, - wantRequestData: []*adapters.RequestData{ - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}},{"id":"test-imp-id2_mf1","banner":{"w":300,"h":250},"bidfloor":1.2,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf1", "test-imp-id2_mf1"}, - }, - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf2","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}},{"id":"test-imp-id2_mf2","banner":{"w":300,"h":250},"bidfloor":1.3,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf2", "test-imp-id2_mf2"}, - }, - { - Method: "POST", - Uri: "https://hbopenbid.pubmatic.com/translator?source=prebid-server", - Body: []byte(`{"id":"test-request-id","imp":[{"id":"test-imp-id_mf3","banner":{"w":300,"h":250},"bidfloor":1.4,"ext":{}},{"id":"test-imp-id2","banner":{"w":300,"h":250},"bidfloor":0.34,"ext":{}}],"app":{"name":"AutoScout24","bundle":"com.autoscout24","storeurl":"https://play.google.com/store/apps/details?id=com.autoscout24\u0026hl=fr"}}`), - Headers: http.Header{ - "Content-Type": []string{"application/json;charset=utf-8"}, - "Accept": []string{"application/json"}, - "Cookie": []string{"test-cookie"}, - }, - ImpIDs: []string{"test-imp-id_mf3", "test-imp-id2"}, - }, - }, - wantError: []error{}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - a := &PubmaticAdapter{ - URI: tt.fields.URI, - bidderName: tt.fields.bidderName, - } - gotRequestData, gotError := a.buildMultiFloorRequests(tt.args.request, tt.args.impFloorsMap, tt.args.cookies) - assert.Equal(t, tt.wantRequestData, gotRequestData) - assert.Equal(t, tt.wantError, gotError) - }) - } -} - func TestPubmaticAdapter_buildAdapterRequest(t *testing.T) { type fields struct { URI string @@ -1550,59 +1103,6 @@ func TestPubmaticAdapter_buildAdapterRequest(t *testing.T) { } } -func TestTrimSuffixWithPattern(t *testing.T) { - type args struct { - input string - } - tests := []struct { - name string - args args - want string - }{ - { - name: "input string is empty", - args: args{ - input: "", - }, - want: "", - }, - { - name: "input string does not contain pattern", - args: args{ - input: "div123456789", - }, - want: "div123456789", - }, - { - name: "input string contains pattern", - args: args{ - input: "div123456789_mf1", - }, - want: "div123456789", - }, - { - name: "input string contains pattern at the end", - args: args{ - input: "div123456789_mf1_mf2", - }, - want: "div123456789", - }, - { - name: "input string contains pattern at the start", - args: args{ - input: "mf1_mf2_div123456789", - }, - want: "mf1", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := trimSuffixWithPattern(tt.args.input) - assert.Equal(t, tt.want, got) - }) - } -} - func TestGetDisplayManagerAndVer(t *testing.T) { type args struct { app *openrtb2.App diff --git a/modules/pubmatic/openwrap/auctionresponsehook.go b/modules/pubmatic/openwrap/auctionresponsehook.go index cacda092cf..481842d19a 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook.go +++ b/modules/pubmatic/openwrap/auctionresponsehook.go @@ -327,6 +327,7 @@ func (m OpenWrap) handleAuctionResponseHook( rctx.ResponseExt = responseExt rctx.DefaultBids = m.addDefaultBids(&rctx, payload.BidResponse, responseExt) + rctx.DefaultBids = m.addDefaultBidsForMultiFloorsConfig(&rctx, payload.BidResponse, responseExt) rctx.Trackers = tracker.CreateTrackers(rctx, payload.BidResponse) diff --git a/modules/pubmatic/openwrap/auctionresponsehook_test.go b/modules/pubmatic/openwrap/auctionresponsehook_test.go index 55d11e8c55..a8d7ea1ab9 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook_test.go +++ b/modules/pubmatic/openwrap/auctionresponsehook_test.go @@ -1724,6 +1724,9 @@ func TestOpenWrapHandleAuctionResponseHook(t *testing.T) { Banner: false, }, }, + PrebidBidderCode: map[string]string{ + "pubmatic": "pubmatic", + }, BidderResponseTimeMillis: map[string]int{}, SeatNonBids: map[string][]openrtb_ext.NonBid{}, ReturnAllBidStatus: true, @@ -1757,7 +1760,7 @@ func TestOpenWrapHandleAuctionResponseHook(t *testing.T) { ImpID: "Div1", Price: 5, AdM: "", - Ext: json.RawMessage(`{"bidtype":0,"deal_channel":1,"dspid":6,"origbidcpm":8,"origbidcur":"USD","prebid":{"bidid":"bb57a9e3-fdc2-4772-8071-112dd7f50a6a","meta":{"adaptercode":"pubmatic","advertiserId":4098,"agencyId":4098,"demandSource":"6","mediaType":"banner","networkId":6},"targeting":{"hb_bidder_pubmatic":"pubmatic","hb_deal_pubmatic":"PUBDEAL1","hb_pb_pubmatic":"8.00","hb_size_pubmatic":"728x90"},"type":"video","video":{"duration":0,"primary_category":"","vasttagid":""}}}`), + Ext: json.RawMessage(`{"bidtype":0,"deal_channel":1,"dspid":6,"mbmfv":4,"origbidcpm":8,"origbidcur":"USD","prebid":{"bidid":"bb57a9e3-fdc2-4772-8071-112dd7f50a6a","meta":{"adaptercode":"pubmatic","advertiserId":4098,"agencyId":4098,"demandSource":"6","mediaType":"banner","networkId":6},"targeting":{"hb_bidder_pubmatic":"pubmatic","hb_deal_pubmatic":"PUBDEAL1","hb_pb_pubmatic":"8.00","hb_size_pubmatic":"728x90"},"type":"video","video":{"duration":0,"primary_category":"","vasttagid":""}}}`), }, }, Seat: "pubmatic", @@ -1781,7 +1784,7 @@ func TestOpenWrapHandleAuctionResponseHook(t *testing.T) { want: want{ result: hookstage.HookResult[hookstage.AuctionResponsePayload]{}, err: nil, - bidResponse: json.RawMessage(`{"id":"12345","seatbid":[{"bid":[{"id":"bid-id-1","impid":"Div1","price":5,"adm":"\u003cVAST version=\"3.0\"\u003e\u003cAd\u003e\u003cWrapper\u003e\u003cImpression\u003e\u003c![CDATA[https:?adv=\u0026af=video\u0026aps=0\u0026au=\u0026bc=pubmatic\u0026bidid=bb57a9e3-fdc2-4772-8071-112dd7f50a6a\u0026di=-1\u0026dur=20\u0026eg=0\u0026en=0\u0026ft=0\u0026iid=\u0026kgpv=\u0026orig=\u0026origbidid=bid-id-1\u0026pdvid=0\u0026pid=0\u0026plt=0\u0026pn=pubmatic\u0026psz=0x0\u0026pubid=5890\u0026purl=\u0026sl=1\u0026slot=\u0026ss=1\u0026tgid=0\u0026tst=0]]\u003e\u003c/Impression\u003e\u003cExtensions\u003e\u003cExtension\u003e\u003cPricing model=\"CPM\" currency=\"USD\"\u003e\u003c![CDATA[5]]\u003e\u003c/Pricing\u003e\u003c/Extension\u003e\u003c/Extensions\u003e\u003c/Wrapper\u003e\u003c/Ad\u003e\u003c/VAST\u003e","ext":{"prebid":{"meta":{"adaptercode":"pubmatic","advertiserId":4098,"agencyId":4098,"demandSource":"6","mediaType":"banner","networkId":6},"type":"video","video":{"duration":20,"primary_category":"","vasttagid":""},"bidid":"bb57a9e3-fdc2-4772-8071-112dd7f50a6a"},"refreshInterval":30,"crtype":"video","video":{"minduration":10,"maxduration":20,"skip":1,"skipmin":1,"skipafter":2,"battr":[1],"playbackmethod":[1]},"dspid":6,"netecpm":5,"origbidcpm":8,"origbidcur":"USD"}}],"seat":"pubmatic"}],"ext":{"responsetimemillis":{"pubmatic":8},"matchedimpression":{"pubmatic":0}}}`), + bidResponse: json.RawMessage(`{"id":"12345","seatbid":[{"bid":[{"id":"bid-id-1","impid":"Div1","price":5,"adm":"\u003cVAST version=\"3.0\"\u003e\u003cAd\u003e\u003cWrapper\u003e\u003cImpression\u003e\u003c![CDATA[https:?adv=\u0026af=video\u0026aps=0\u0026au=\u0026bc=pubmatic\u0026bidid=bb57a9e3-fdc2-4772-8071-112dd7f50a6a\u0026di=-1\u0026dur=20\u0026eg=0\u0026en=0\u0026frv=4\u0026ft=0\u0026fv=4\u0026iid=\u0026kgpv=\u0026orig=\u0026origbidid=bid-id-1\u0026pdvid=0\u0026pid=0\u0026plt=0\u0026pn=pubmatic\u0026psz=0x0\u0026pubid=5890\u0026purl=\u0026sl=1\u0026slot=\u0026ss=1\u0026tgid=0\u0026tst=0]]\u003e\u003c/Impression\u003e\u003cExtensions\u003e\u003cExtension\u003e\u003cPricing model=\"CPM\" currency=\"USD\"\u003e\u003c![CDATA[5]]\u003e\u003c/Pricing\u003e\u003c/Extension\u003e\u003c/Extensions\u003e\u003c/Wrapper\u003e\u003c/Ad\u003e\u003c/VAST\u003e","ext":{"prebid":{"meta":{"adaptercode":"pubmatic","advertiserId":4098,"agencyId":4098,"demandSource":"6","mediaType":"banner","networkId":6},"type":"video","video":{"duration":20,"primary_category":"","vasttagid":""},"bidid":"bb57a9e3-fdc2-4772-8071-112dd7f50a6a"},"refreshInterval":30,"crtype":"video","video":{"minduration":10,"maxduration":20,"skip":1,"skipmin":1,"skipafter":2,"battr":[1],"playbackmethod":[1]},"dspid":6,"netecpm":5,"origbidcpm":8,"origbidcur":"USD","mbmfv":4}}],"seat":"pubmatic"}],"ext":{"responsetimemillis":{"pubmatic":8},"matchedimpression":{"pubmatic":0}}}`), }, }, } diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go index b16d0e8814..332756ad24 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook.go +++ b/modules/pubmatic/openwrap/beforevalidationhook.go @@ -466,7 +466,7 @@ func (m OpenWrap) handleBeforeValidationHook( // prebidBidderCode is equivalent of PBS-Core's bidderCode prebidBidderCode := partnerConfig[models.PREBID_PARTNER_NAME] // - rCtx.PrebidBidderCode[prebidBidderCode] = bidderCode + rCtx.PrebidBidderCode[bidderCode] = prebidBidderCode if _, ok := rCtx.AdapterFilteredMap[bidderCode]; ok { result.Warnings = append(result.Warnings, "Dropping adapter due to bidder filtering: "+bidderCode) diff --git a/modules/pubmatic/openwrap/defaultbids.go b/modules/pubmatic/openwrap/defaultbids.go index ebc3af57ea..c7f128e3ce 100644 --- a/modules/pubmatic/openwrap/defaultbids.go +++ b/modules/pubmatic/openwrap/defaultbids.go @@ -2,6 +2,7 @@ package openwrap import ( "encoding/json" + "fmt" "strconv" "github.com/prebid/openrtb/v20/openrtb2" @@ -178,6 +179,67 @@ func (m *OpenWrap) addDefaultBids(rctx *models.RequestCtx, bidResponse *openrtb2 return defaultBids } +func (m *OpenWrap) addDefaultBidsForMultiFloorsConfig(rctx *models.RequestCtx, bidResponse *openrtb2.BidResponse, bidResponseExt openrtb_ext.ExtBidResponse) map[string]map[string][]openrtb2.Bid { + + // MultiBidMultiFloor is only supported for AppLovinMax + if rctx.Endpoint != models.EndpointAppLovinMax || !rctx.AppLovinMax.MultiFloorsConfig.Enabled { + return rctx.DefaultBids + } + + defaultBids := rctx.DefaultBids + bidderExcludeFloors := make(map[string]struct{}, len(bidResponse.SeatBid)) //exclude floors which are already present in bidresponse + var adunitFloors []float64 //floors for each adunit + + for _, seatBid := range bidResponse.SeatBid { + if rctx.PrebidBidderCode[seatBid.Seat] == models.BidderPubMatic || rctx.PrebidBidderCode[seatBid.Seat] == models.BidderPubMaticSecondaryAlias { + for _, bid := range seatBid.Bid { + impId, _ := models.GetImpressionID(bid.ImpID) + floorValue := rctx.ImpBidCtx[impId].BidCtx[bid.ID].BidExt.MultiBidMultiFloorValue + if floorValue > 0 { + key := fmt.Sprintf("%s-%s-%.2f", impId, seatBid.Seat, floorValue) + bidderExcludeFloors[key] = struct{}{} + } + } + } + } + + for impID, impCtx := range rctx.ImpBidCtx { + adunitFloors = rctx.AppLovinMax.MultiFloorsConfig.Config[impCtx.TagID] + for bidder := range impCtx.Bidders { + if prebidBidderCode := rctx.PrebidBidderCode[bidder]; prebidBidderCode != models.BidderPubMatic && prebidBidderCode != models.BidderPubMaticSecondaryAlias { + continue + } + + if defaultBids[impID] == nil { + defaultBids[impID] = make(map[string][]openrtb2.Bid) + } + + //if defaultbid is already present for pubmatic, then reset it, as we are adding new defaultbids with MultiBidMultiFloor + if _, ok := defaultBids[impID][bidder]; ok { + defaultBids[impID][bidder] = make([]openrtb2.Bid, 0) + } + + //exclude floors which are already present in bidresponse for defaultbids + for _, floor := range adunitFloors { + key := fmt.Sprintf("%s-%s-%.2f", impID, bidder, floor) + if _, ok := bidderExcludeFloors[key]; !ok { + uuid, _ := m.uuidGenerator.Generate() + bidExt := newDefaultBidExtMultiFloors(floor, bidder, bidResponseExt) + bidExtJson, _ := json.Marshal(bidExt) + defaultBids[impID][bidder] = append(defaultBids[impID][bidder], openrtb2.Bid{ + ID: uuid, + ImpID: impID, + Ext: bidExtJson, + }) + } + + } + + } + } + return defaultBids +} + // getNonBRCodeFromBidRespExt maps the error-code present in prebid partner response with standard nonBR code func getNonBRCodeFromBidRespExt(bidder string, bidResponseExt openrtb_ext.ExtBidResponse) *openrtb3.NoBidReason { errs := bidResponseExt.Errors[openrtb_ext.BidderName(bidder)] @@ -223,6 +285,13 @@ func newDefaultBidExt(rctx models.RequestCtx, impID, bidder string, bidResponseE return &bidExt } +func newDefaultBidExtMultiFloors(floor float64, bidder string, bidResponseExt openrtb_ext.ExtBidResponse) *models.BidExt { + return &models.BidExt{ + Nbr: getNonBRCodeFromBidRespExt(bidder, bidResponseExt), + MultiBidMultiFloorValue: floor, + } +} + // TODO : Check if we need this? // func newDefaultVastTagBidExt(rctx models.RequestCtx, impID, bidder, vastTag string, bidResponseExt openrtb_ext.ExtBidResponse) *models.BidExt { // bidExt := models.BidExt{ diff --git a/modules/pubmatic/openwrap/defaultbids_test.go b/modules/pubmatic/openwrap/defaultbids_test.go index b321aa9687..8452d7c2c8 100644 --- a/modules/pubmatic/openwrap/defaultbids_test.go +++ b/modules/pubmatic/openwrap/defaultbids_test.go @@ -218,3 +218,475 @@ func TestOpenWrap_addDefaultBids(t *testing.T) { }) } } + +func TestOpenWrap_addDefaultBidsForMultiFloorsConfig(t *testing.T) { + type fields struct { + cfg config.Config + metricEngine metrics.MetricsEngine + rateConvertor *currency.RateConverter + geoInfoFetcher geodb.Geography + pubFeatures publisherfeature.Feature + unwrap unwrap.Unwrap + profileMetaData profilemetadata.ProfileMetaData + uuidGenerator uuidutil.UUIDGenerator + } + type args struct { + rctx *models.RequestCtx + bidResponse *openrtb2.BidResponse + bidResponseExt openrtb_ext.ExtBidResponse + } + tests := []struct { + name string + fields fields + args args + want map[string]map[string][]openrtb2.Bid + }{ + { + name: "request is other than applovinmax", + args: args{ + rctx: &models.RequestCtx{ + Endpoint: models.EndpointWebS2S, + DefaultBids: map[string]map[string][]openrtb2.Bid{}, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "bid-1", + SeatBid: []openrtb2.SeatBid{}, + }, + }, + want: map[string]map[string][]openrtb2.Bid{}, + }, + { + name: "request is applovinmax but the multi-floors config is not enabled from DB", + args: args{ + rctx: &models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + DefaultBids: map[string]map[string][]openrtb2.Bid{}, + AppLovinMax: models.AppLovinMax{ + MultiFloorsConfig: models.MultiFloorsConfig{ + Enabled: false, + }, + }, + }, + }, + want: map[string]map[string][]openrtb2.Bid{}, + }, + { + name: "mulit-floors config have three floors and no bids in the response", + args: args{ + rctx: &models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + DefaultBids: map[string]map[string][]openrtb2.Bid{ + "test-impID-1": { + "pubmatic": { + { + ID: "dbbsdhkldks1234", + ImpID: "test-impID-1", + Ext: []byte(`{}`), + }, + }, + }, + }, + AppLovinMax: models.AppLovinMax{ + MultiFloorsConfig: models.MultiFloorsConfig{ + Enabled: true, + Config: models.ApplovinAdUnitFloors{ + "adunit-1": []float64{1.1, 2.1, 3.1}, + }, + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "test-impID-1": { + TagID: "adunit-1", + Bidders: map[string]models.PartnerData{ + "pubmatic": { + PrebidBidderCode: "pubmatic", + }, + }, + }, + }, + PrebidBidderCode: map[string]string{ + "pubmatic": "pubmatic", + }, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "bid-1", + }, + }, + fields: fields{ + uuidGenerator: TestUUIDGenerator{}, + }, + want: map[string]map[string][]openrtb2.Bid{ + "test-impID-1": { + "pubmatic": { + { + ID: "30470a14-2949-4110-abce-b62d57304ad5", + ImpID: "test-impID-1", + Ext: []byte(`{"mbmfv":1.1}`), + }, + { + ID: "30470a14-2949-4110-abce-b62d57304ad5", + ImpID: "test-impID-1", + Ext: []byte(`{"mbmfv":2.1}`), + }, + { + ID: "30470a14-2949-4110-abce-b62d57304ad5", + ImpID: "test-impID-1", + Ext: []byte(`{"mbmfv":3.1}`), + }, + }, + }, + }, + }, + { + name: "mulit-floors config have three floors and only one bid in the response", + args: args{ + rctx: &models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + DefaultBids: map[string]map[string][]openrtb2.Bid{}, + AppLovinMax: models.AppLovinMax{ + MultiFloorsConfig: models.MultiFloorsConfig{ + Enabled: true, + Config: models.ApplovinAdUnitFloors{ + "adunit-1": []float64{1.1, 2.1, 3.1}, + }, + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "test-impID-1": { + TagID: "adunit-1", + Bidders: map[string]models.PartnerData{ + "pubmatic": { + PrebidBidderCode: "pubmatic", + }, + }, + BidCtx: map[string]models.BidCtx{ + "pubmatic-bid-1": { + BidExt: models.BidExt{ + MultiBidMultiFloorValue: 1.1, + }, + }, + }, + }, + }, + PrebidBidderCode: map[string]string{ + "pubmatic": "pubmatic", + }, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "bid-1", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "pubmatic-bid-1", + ImpID: "test-impID-1", + Price: 1.5, + Ext: []byte(`{"mbmfv":1.1}`), + }, + }, + }, + }, + }, + }, + fields: fields{ + uuidGenerator: TestUUIDGenerator{}, + }, + want: map[string]map[string][]openrtb2.Bid{ + "test-impID-1": { + "pubmatic": { + { + ID: "30470a14-2949-4110-abce-b62d57304ad5", + ImpID: "test-impID-1", + Ext: []byte(`{"mbmfv":2.1}`), + }, + { + ID: "30470a14-2949-4110-abce-b62d57304ad5", + ImpID: "test-impID-1", + Ext: []byte(`{"mbmfv":3.1}`), + }, + }, + }, + }, + }, + { + name: "mulit-floors config have three floors and all three bid in the response", + args: args{ + rctx: &models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + DefaultBids: map[string]map[string][]openrtb2.Bid{}, + AppLovinMax: models.AppLovinMax{ + MultiFloorsConfig: models.MultiFloorsConfig{ + Enabled: true, + Config: models.ApplovinAdUnitFloors{ + "adunit-1": []float64{1.1, 2.1, 3.1}, + }, + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "test-impID-1": { + TagID: "adunit-1", + Bidders: map[string]models.PartnerData{ + "pubmatic": { + PrebidBidderCode: "pubmatic", + }, + }, + BidCtx: map[string]models.BidCtx{ + "pubmatic-bid-1": { + BidExt: models.BidExt{ + MultiBidMultiFloorValue: 1.1, + }, + }, + "pubmatic-bid-2": { + BidExt: models.BidExt{ + MultiBidMultiFloorValue: 2.1, + }, + }, + "pubmatic-bid-3": { + BidExt: models.BidExt{ + MultiBidMultiFloorValue: 3.1, + }, + }, + }, + }, + }, + PrebidBidderCode: map[string]string{ + "pubmatic": "pubmatic", + }, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "bid-1", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "pubmatic-bid-1", + ImpID: "test-impID-1", + Price: 1.5, + Ext: []byte(`{"mbmfv":1.1}`), + }, + { + ID: "pubmatic-bid-2", + ImpID: "test-impID-1", + Price: 2.5, + Ext: []byte(`{"mbmfv":2.1}`), + }, + { + ID: "pubmatic-bid-3", + ImpID: "test-impID-1", + Price: 3.5, + }, + }, + }, + }, + }, + }, + fields: fields{ + uuidGenerator: TestUUIDGenerator{}, + }, + want: map[string]map[string][]openrtb2.Bid{ + "test-impID-1": {}, + }, + }, + { + name: "mulit-floors config have three floors and only one bid in the response for both partner pubmatic and pubmatic_1123", + args: args{ + rctx: &models.RequestCtx{ + Endpoint: models.EndpointAppLovinMax, + DefaultBids: map[string]map[string][]openrtb2.Bid{}, + AppLovinMax: models.AppLovinMax{ + MultiFloorsConfig: models.MultiFloorsConfig{ + Enabled: true, + Config: models.ApplovinAdUnitFloors{ + "adunit-1": []float64{1.1, 2.1, 3.1}, + }, + }, + }, + ImpBidCtx: map[string]models.ImpCtx{ + "test-impID-1": { + TagID: "adunit-1", + Bidders: map[string]models.PartnerData{ + "pubmatic": { + PrebidBidderCode: "pubmatic", + }, + "pubmatic_1123": { + PrebidBidderCode: "pubmatic", + }, + }, + BidCtx: map[string]models.BidCtx{ + "pubmatic-bid-1": { + BidExt: models.BidExt{ + MultiBidMultiFloorValue: 1.1, + }, + }, + "pubmatic-bid-2": { + BidExt: models.BidExt{ + MultiBidMultiFloorValue: 1.1, + }, + }, + }, + }, + }, + PrebidBidderCode: map[string]string{ + "pubmatic_1123": "pubmatic", + "pubmatic": "pubmatic", + }, + }, + bidResponse: &openrtb2.BidResponse{ + ID: "bid-1", + SeatBid: []openrtb2.SeatBid{ + { + Seat: "pubmatic", + Bid: []openrtb2.Bid{ + { + ID: "pubmatic-bid-1", + ImpID: "test-impID-1", + Price: 1.5, + Ext: []byte(`{"mbmfv":1.1}`), + }, + }, + }, + { + Seat: "pubmatic_1123", + Bid: []openrtb2.Bid{ + { + ID: "pubmatic-bid-2", + ImpID: "test-impID-1", + Price: 1.6, + Ext: []byte(`{"mbmfv":1.1}`), + }, + }, + }, + }, + }, + }, + fields: fields{ + uuidGenerator: TestUUIDGenerator{}, + }, + want: map[string]map[string][]openrtb2.Bid{ + "test-impID-1": { + "pubmatic": { + { + ID: "30470a14-2949-4110-abce-b62d57304ad5", + ImpID: "test-impID-1", + Ext: []byte(`{"mbmfv":2.1}`), + }, + { + ID: "30470a14-2949-4110-abce-b62d57304ad5", + ImpID: "test-impID-1", + Ext: []byte(`{"mbmfv":3.1}`), + }, + }, + "pubmatic_1123": { + { + ID: "30470a14-2949-4110-abce-b62d57304ad5", + ImpID: "test-impID-1", + Ext: []byte(`{"mbmfv":2.1}`), + }, + { + ID: "30470a14-2949-4110-abce-b62d57304ad5", + ImpID: "test-impID-1", + Ext: []byte(`{"mbmfv":3.1}`), + }, + }, + }, + }, + }, + // { + // name: "mulit-floors config have three floors and no bid in the response for both partner pubmatic and pubmatic_1123", + // args: args{ + // rctx: &models.RequestCtx{ + // Endpoint: models.EndpointAppLovinMax, + // DefaultBids: map[string]map[string][]openrtb2.Bid{}, + // AppLovinMax: models.AppLovinMax{ + // MultiFloorsConfig: models.MultiFloorsConfig{ + // Enabled: true, + // Config: models.ApplovinAdUnitFloors{ + // "adunit-1": []float64{1.1, 2.1, 3.1}, + // }, + // }, + // }, + // ImpBidCtx: map[string]models.ImpCtx{ + // "test-impID-1": { + // TagID: "adunit-1", + // Bidders: map[string]models.PartnerData{ + // "pubmatic": { + // PrebidBidderCode: "pubmatic", + // }, + // "pubmatic_1123": { + // PrebidBidderCode: "pubmatic", + // }, + // }, + // }, + // }, + // PrebidBidderCode: map[string]string{ + // "pubmatic_1123": "pubmatic", + // "pubmatic": "pubmatic", + // }, + // }, + // bidResponse: &openrtb2.BidResponse{ + // ID: "bid-1", + // SeatBid: []openrtb2.SeatBid{}, + // }, + // }, + // fields: fields{ + // uuidGenerator: TestUUIDGenerator{}, + // }, + // want: map[string]map[string][]openrtb2.Bid{ + // "test-impID-1": { + // "pubmatic": { + // { + // ID: "30470a14-2949-4110-abce-b62d57304ad5", + // ImpID: "test-impID-1", + // Ext: []byte(`{"mbmfv":1.1}`), + // }, + // { + // ID: "30470a14-2949-4110-abce-b62d57304ad5", + // ImpID: "test-impID-1", + // Ext: []byte(`{"mbmfv":2.1}`), + // }, + // { + // ID: "30470a14-2949-4110-abce-b62d57304ad5", + // ImpID: "test-impID-1", + // Ext: []byte(`{"mbmfv":3.1}`), + // }, + // }, + // "pubmatic_1123": { + // { + // ID: "30470a14-2949-4110-abce-b62d57304ad5", + // ImpID: "test-impID-1", + // Ext: []byte(`{"mbmfv":1.1}`), + // }, + // { + // ID: "30470a14-2949-4110-abce-b62d57304ad5", + // ImpID: "test-impID-1", + // Ext: []byte(`{"mbmfv":2.1}`), + // }, + // { + // ID: "30470a14-2949-4110-abce-b62d57304ad5", + // ImpID: "test-impID-1", + // Ext: []byte(`{"mbmfv":3.1}`), + // }, + // }, + // }, + // }, + // }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &OpenWrap{ + cfg: tt.fields.cfg, + metricEngine: tt.fields.metricEngine, + rateConvertor: tt.fields.rateConvertor, + geoInfoFetcher: tt.fields.geoInfoFetcher, + pubFeatures: tt.fields.pubFeatures, + unwrap: tt.fields.unwrap, + profileMetaData: tt.fields.profileMetaData, + uuidGenerator: tt.fields.uuidGenerator, + } + got := m.addDefaultBidsForMultiFloorsConfig(tt.args.rctx, tt.args.bidResponse, tt.args.bidResponseExt) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/modules/pubmatic/openwrap/models/response.go b/modules/pubmatic/openwrap/models/response.go index 438908f8cb..2f535cb7e3 100644 --- a/modules/pubmatic/openwrap/models/response.go +++ b/modules/pubmatic/openwrap/models/response.go @@ -11,23 +11,24 @@ import ( type BidExt struct { openrtb_ext.ExtBid - ErrorCode int `json:"errorCode,omitempty"` - ErrorMsg string `json:"errorMessage,omitempty"` - RefreshInterval int `json:"refreshInterval,omitempty"` - CreativeType string `json:"crtype,omitempty"` - Summary []Summary `json:"summary,omitempty"` - SKAdnetwork json.RawMessage `json:"skadn,omitempty"` - Video *ExtBidVideo `json:"video,omitempty"` - Banner *ExtBidBanner `json:"banner,omitempty"` - DspId int `json:"dspid,omitempty"` - 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"` - Nbr *openrtb3.NoBidReason `json:"-"` // Reason for not bidding - Fsc int `json:"fsc,omitempty"` - AdPod *AdpodBidExt `json:"adpod,omitempty"` + ErrorCode int `json:"errorCode,omitempty"` + ErrorMsg string `json:"errorMessage,omitempty"` + RefreshInterval int `json:"refreshInterval,omitempty"` + CreativeType string `json:"crtype,omitempty"` + Summary []Summary `json:"summary,omitempty"` + SKAdnetwork json.RawMessage `json:"skadn,omitempty"` + Video *ExtBidVideo `json:"video,omitempty"` + Banner *ExtBidBanner `json:"banner,omitempty"` + DspId int `json:"dspid,omitempty"` + 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"` + Nbr *openrtb3.NoBidReason `json:"-"` // Reason for not bidding + Fsc int `json:"fsc,omitempty"` + AdPod *AdpodBidExt `json:"adpod,omitempty"` + MultiBidMultiFloorValue float64 `json:"mbmfv,omitempty"` } type AdpodBidExt struct { diff --git a/modules/pubmatic/openwrap/models/utils.go b/modules/pubmatic/openwrap/models/utils.go index 4775b93426..96ec618c95 100644 --- a/modules/pubmatic/openwrap/models/utils.go +++ b/modules/pubmatic/openwrap/models/utils.go @@ -377,6 +377,13 @@ func GetBidLevelFloorsDetails(bidExt BidExt, impCtx ImpCtx, var floorCurrency string frv = NotSet + //Set the fv from bid.ext.mbmf if it is set + if bidExt.MultiBidMultiFloorValue > 0 { + fv = RoundToTwoDigit(bidExt.MultiBidMultiFloorValue) + frv = RoundToTwoDigit(bidExt.MultiBidMultiFloorValue) + return + } + if bidExt.Prebid != nil && bidExt.Prebid.Floors != nil { floorCurrency = bidExt.Prebid.Floors.FloorCurrency fv = RoundToTwoDigit(bidExt.Prebid.Floors.FloorValue) diff --git a/modules/pubmatic/openwrap/models/utils_test.go b/modules/pubmatic/openwrap/models/utils_test.go index afff46c2bf..883b78236c 100644 --- a/modules/pubmatic/openwrap/models/utils_test.go +++ b/modules/pubmatic/openwrap/models/utils_test.go @@ -954,6 +954,18 @@ func TestGetBidLevelFloorsDetails(t *testing.T) { frv: 0, }, }, + { + name: "floor_values_set_from_bidExt_mbmfv_for_applovinmax", + args: args{ + bidExt: BidExt{ + MultiBidMultiFloorValue: 5.0, + }, + }, + want: want{ + fv: 5.0, + frv: 5.0, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/modules/pubmatic/openwrap/tracker/create.go b/modules/pubmatic/openwrap/tracker/create.go index 9ed7bae433..8354fd7030 100644 --- a/modules/pubmatic/openwrap/tracker/create.go +++ b/modules/pubmatic/openwrap/tracker/create.go @@ -115,7 +115,6 @@ func createTrackers(rctx models.RequestCtx, trackers map[string]models.OWTracker 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 { diff --git a/modules/pubmatic/openwrap/tracker/create_test.go b/modules/pubmatic/openwrap/tracker/create_test.go index e49f4865bc..6b899335ac 100644 --- a/modules/pubmatic/openwrap/tracker/create_test.go +++ b/modules/pubmatic/openwrap/tracker/create_test.go @@ -196,7 +196,7 @@ func Test_createTrackers(t *testing.T) { rctx: func() models.RequestCtx { testRctx := rctx testRctx.StartTime = startTime - testRctx.PrebidBidderCode["pubmatic"] = "pubmatic2" + testRctx.PrebidBidderCode["pubmatic2"] = "pubmatic" return testRctx }(), bidResponse: &openrtb2.BidResponse{