diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index fa61c484f0d..e4faa7e9cb8 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -1132,12 +1132,17 @@ func validateRequestExt(req *openrtb_ext.RequestWrapper) []error { } func validateTargeting(t *openrtb_ext.ExtRequestTargeting) error { - if t != nil { - if t.PriceGranularity != nil { - if err := validatePriceGranularity(t.PriceGranularity); err != nil { - return err - } + if t == nil { + return nil + } + + if t.PriceGranularity != nil { + if err := validatePriceGranularity(t.PriceGranularity); err != nil { + return err } + } + + if t.MediaTypePriceGranularity != nil { if t.MediaTypePriceGranularity.Video != nil { if err := validatePriceGranularity(t.MediaTypePriceGranularity.Video); err != nil { return err @@ -1154,6 +1159,7 @@ func validateTargeting(t *openrtb_ext.ExtRequestTargeting) error { } } } + return nil } diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 2df4813d157..69ba0da9adb 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -1987,7 +1987,7 @@ func TestValidateTargeting(t *testing.T) { expectedError: nil, }, { - name: "price granularity ranges out of order", + name: "pricegranularity-ranges-out-of-order", givenTargeting: &openrtb_ext.ExtRequestTargeting{ PriceGranularity: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(2), @@ -2000,9 +2000,16 @@ func TestValidateTargeting(t *testing.T) { expectedError: errors.New(`Price granularity error: range list must be ordered with increasing "max"`), }, { - name: "media type price granularity video correct", + name: "mediatypepricegranularity-nil", givenTargeting: &openrtb_ext.ExtRequestTargeting{ - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: nil, + }, + expectedError: nil, + }, + { + name: "mediatypepricegranularity-video-ok", + givenTargeting: &openrtb_ext.ExtRequestTargeting{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Video: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{ @@ -2014,9 +2021,9 @@ func TestValidateTargeting(t *testing.T) { expectedError: nil, }, { - name: "media type price granularity banner correct", + name: "mediatypepricegranularity-banner-ok", givenTargeting: &openrtb_ext.ExtRequestTargeting{ - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Banner: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{ @@ -2028,9 +2035,9 @@ func TestValidateTargeting(t *testing.T) { expectedError: nil, }, { - name: "media type price granularity native correct", + name: "mediatypepricegranularity-native-ok", givenTargeting: &openrtb_ext.ExtRequestTargeting{ - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Native: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{ @@ -2042,9 +2049,9 @@ func TestValidateTargeting(t *testing.T) { expectedError: nil, }, { - name: "media type price granularity video and banner correct", + name: "mediatypepricegranularity-video+banner-ok", givenTargeting: &openrtb_ext.ExtRequestTargeting{ - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Banner: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{ @@ -2062,9 +2069,9 @@ func TestValidateTargeting(t *testing.T) { expectedError: nil, }, { - name: "media type price granularity video incorrect", + name: "mediatypepricegranularity-video-invalid", givenTargeting: &openrtb_ext.ExtRequestTargeting{ - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Video: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{ @@ -2076,9 +2083,9 @@ func TestValidateTargeting(t *testing.T) { expectedError: errors.New("Price granularity error: increment must be a nonzero positive number"), }, { - name: "media type price granularity banner incorrect", + name: "mediatypepricegranularity-banner-invalid", givenTargeting: &openrtb_ext.ExtRequestTargeting{ - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Banner: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{ @@ -2090,9 +2097,9 @@ func TestValidateTargeting(t *testing.T) { expectedError: errors.New("Price granularity error: range list must be ordered with increasing \"max\""), }, { - name: "media type price granularity native incorrect", + name: "mediatypepricegranularity-native-invalid", givenTargeting: &openrtb_ext.ExtRequestTargeting{ - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Native: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{ @@ -2104,9 +2111,9 @@ func TestValidateTargeting(t *testing.T) { expectedError: errors.New("Price granularity error: range list must be ordered with increasing \"max\""), }, { - name: "media type price granularity video correct and banner incorrect", + name: "mediatypepricegranularity-video-ok-banner-invalid", givenTargeting: &openrtb_ext.ExtRequestTargeting{ - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Banner: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{ @@ -2124,9 +2131,9 @@ func TestValidateTargeting(t *testing.T) { expectedError: errors.New("Price granularity error: range list must be ordered with increasing \"max\""), }, { - name: "media type price granularity native incorrect and banner correct", + name: "mediatypepricegranularity-native-invalid-banner-ok", givenTargeting: &openrtb_ext.ExtRequestTargeting{ - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Native: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(2), Ranges: []openrtb_ext.GranularityRange{ @@ -2147,7 +2154,7 @@ func TestValidateTargeting(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - assert.Equal(t, tc.expectedError, validateTargeting(tc.givenTargeting), "Targeting") + assert.Equal(t, tc.expectedError, validateTargeting(tc.givenTargeting)) }) } } diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/addtl-consent-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/addtl-consent-through-query.json index 034d3235e15..c775d332ecd 100644 --- a/endpoints/openrtb2/sample-requests/amp/consent-through-query/addtl-consent-through-query.json +++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/addtl-consent-through-query.json @@ -105,8 +105,7 @@ ] }, "includewinners": true, - "includebidderkeys": true, - "mediatypepricegranularity": {} + "includebidderkeys": true } } } diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-ccpa-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-ccpa-through-query.json index 30d8b9ca240..ab9657d51f5 100644 --- a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-ccpa-through-query.json +++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-ccpa-through-query.json @@ -97,8 +97,7 @@ ] }, "includewinners": true, - "includebidderkeys": true, - "mediatypepricegranularity": {} + "includebidderkeys": true } } } diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-legacy-tcf2-consent-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-legacy-tcf2-consent-through-query.json index 8c232192a63..3d6c0bc0157 100644 --- a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-legacy-tcf2-consent-through-query.json +++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-legacy-tcf2-consent-through-query.json @@ -100,8 +100,7 @@ ] }, "includewinners": true, - "includebidderkeys": true, - "mediatypepricegranularity": {} + "includebidderkeys": true } } } diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf1-consent-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf1-consent-through-query.json index 0249bbc3f96..c4f3da140a3 100644 --- a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf1-consent-through-query.json +++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf1-consent-through-query.json @@ -102,8 +102,7 @@ ] }, "includewinners": true, - "includebidderkeys": true, - "mediatypepricegranularity": {} + "includebidderkeys": true } } } diff --git a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf2-consent-through-query.json b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf2-consent-through-query.json index 6f0f5780b43..2be461cdf6c 100644 --- a/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf2-consent-through-query.json +++ b/endpoints/openrtb2/sample-requests/amp/consent-through-query/gdpr-tcf2-consent-through-query.json @@ -100,8 +100,7 @@ ] }, "includewinners": true, - "includebidderkeys": true, - "mediatypepricegranularity": {} + "includebidderkeys": true } } } diff --git a/endpoints/openrtb2/sample-requests/amp/valid-supplementary/ortb-2.5-to-2.6-upconvert.json b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/ortb-2.5-to-2.6-upconvert.json index 766b7fc6ba9..1a2db473cdd 100644 --- a/endpoints/openrtb2/sample-requests/amp/valid-supplementary/ortb-2.5-to-2.6-upconvert.json +++ b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/ortb-2.5-to-2.6-upconvert.json @@ -180,8 +180,7 @@ ] }, "includewinners": true, - "includebidderkeys": true, - "mediatypepricegranularity": {} + "includebidderkeys": true } } } diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-backward-compatible-merge.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-backward-compatible-merge.json index 908e20e5b79..9e9fd538112 100644 --- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-backward-compatible-merge.json +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-backward-compatible-merge.json @@ -74,7 +74,6 @@ } ] }, - "mediatypepricegranularity": {}, "includewinners": true, "includebidderkeys": true }, diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-merge.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-merge.json index 1e2f58cbfd2..9f0e78be352 100644 --- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-merge.json +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-merge.json @@ -76,7 +76,6 @@ } ] }, - "mediatypepricegranularity": {}, "includewinners": true, "includebidderkeys": true }, diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params.json index 311d4e11485..27ee9da486c 100644 --- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params.json +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params.json @@ -69,7 +69,6 @@ } ] }, - "mediatypepricegranularity": {}, "includewinners": true, "includebidderkeys": true }, diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index 79eaaab980c..93f7f6fcf33 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -156,7 +156,7 @@ func TestCreateBidExtensionTargeting(t *testing.T) { require.NotNil(t, ex.lastRequest, "The request never made it into the Exchange.") // assert targeting set to default - expectedRequestExt := `{"prebid":{"cache":{"vastxml":{}},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"mediatypepricegranularity":{},"includebidderkeys":true,"includewinners":true,"includebrandcategory":{"primaryadserver":1,"publisher":"","withcategory":true}}}}` + expectedRequestExt := `{"prebid":{"cache":{"vastxml":{}},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includebidderkeys":true,"includewinners":true,"includebrandcategory":{"primaryadserver":1,"withcategory":true}}}}` assert.JSONEq(t, expectedRequestExt, string(ex.lastRequest.Ext)) } diff --git a/exchange/utils.go b/exchange/utils.go index b24f78c673a..aa2c14ba039 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -498,33 +498,30 @@ func buildRequestExtForBidder(bidder string, req *openrtb_ext.RequestWrapper, re } prebid := reqExt.GetPrebid() - // Resolve alternatebiddercode + // Resolve Alternate Bidder Codes var reqABC *openrtb_ext.ExtAlternateBidderCodes if prebid != nil && prebid.AlternateBidderCodes != nil { reqABC = prebid.AlternateBidderCodes } alternateBidderCodes := buildRequestExtAlternateBidderCodes(bidder, cfgABC, reqABC) - var prebidNew openrtb_ext.ExtRequestPrebid - if prebid == nil { - prebidNew = openrtb_ext.ExtRequestPrebid{ - BidderParams: reqExtBidderParams[bidder], - AlternateBidderCodes: alternateBidderCodes, - } - } else { - // Copy Allowed Fields - // Per: https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-auction.html#prebid-server-ortb2-extension-summary - prebidNew = openrtb_ext.ExtRequestPrebid{ - BidderParams: reqExtBidderParams[bidder], - AlternateBidderCodes: alternateBidderCodes, - Channel: prebid.Channel, - CurrencyConversions: prebid.CurrencyConversions, - Debug: prebid.Debug, - Integration: prebid.Integration, - MultiBid: buildRequestExtMultiBid(bidder, prebid.MultiBid, alternateBidderCodes), - Sdk: prebid.Sdk, - Server: prebid.Server, - } + // Build New/Filtered Prebid Ext + prebidNew := openrtb_ext.ExtRequestPrebid{ + BidderParams: reqExtBidderParams[bidder], + AlternateBidderCodes: alternateBidderCodes, + } + + // Copy Allowed Fields + // Per: https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-auction.html#prebid-server-ortb2-extension-summary + if prebid != nil { + prebidNew.Channel = prebid.Channel + prebidNew.CurrencyConversions = prebid.CurrencyConversions + prebidNew.Debug = prebid.Debug + prebidNew.Integration = prebid.Integration + prebidNew.MultiBid = buildRequestExtMultiBid(bidder, prebid.MultiBid, alternateBidderCodes) + prebidNew.Sdk = prebid.Sdk + prebidNew.Server = prebid.Server + prebidNew.Targeting = buildRequestExtTargeting(prebid.Targeting) } reqExt.SetPrebid(&prebidNew) @@ -586,6 +583,18 @@ func buildRequestExtMultiBid(adapter string, reqMultiBid []*openrtb_ext.ExtMulti return nil } +func buildRequestExtTargeting(t *openrtb_ext.ExtRequestTargeting) *openrtb_ext.ExtRequestTargeting { + if t == nil || t.IncludeBrandCategory == nil { + return nil + } + + // only include fields bidders can use to influence their response and which does + // not expose information about other bidders or restricted auction processing + return &openrtb_ext.ExtRequestTargeting{ + IncludeBrandCategory: t.IncludeBrandCategory, + } +} + func isBidderInExtAlternateBidderCodes(adapter, currentMultiBidBidder string, adapterABC *openrtb_ext.ExtAlternateBidderCodes) bool { if adapterABC != nil { if abc, ok := adapterABC.Bidders[adapter]; ok { @@ -924,15 +933,15 @@ func getExtCacheInstructions(requestExtPrebid *openrtb_ext.ExtRequestPrebid) ext func getExtTargetData(requestExtPrebid *openrtb_ext.ExtRequestPrebid, cacheInstructions extCacheInstructions) *targetData { if requestExtPrebid != nil && requestExtPrebid.Targeting != nil { return &targetData{ - includeWinners: *requestExtPrebid.Targeting.IncludeWinners, - includeBidderKeys: *requestExtPrebid.Targeting.IncludeBidderKeys, + alwaysIncludeDeals: requestExtPrebid.Targeting.AlwaysIncludeDeals, + includeBidderKeys: ptrutil.ValueOrDefault(requestExtPrebid.Targeting.IncludeBidderKeys), includeCacheBids: cacheInstructions.cacheBids, includeCacheVast: cacheInstructions.cacheVAST, includeFormat: requestExtPrebid.Targeting.IncludeFormat, - priceGranularity: *requestExtPrebid.Targeting.PriceGranularity, - mediaTypePriceGranularity: requestExtPrebid.Targeting.MediaTypePriceGranularity, + includeWinners: ptrutil.ValueOrDefault(requestExtPrebid.Targeting.IncludeWinners), + mediaTypePriceGranularity: ptrutil.ValueOrDefault(requestExtPrebid.Targeting.MediaTypePriceGranularity), preferDeals: requestExtPrebid.Targeting.PreferDeals, - alwaysIncludeDeals: requestExtPrebid.Targeting.AlwaysIncludeDeals, + priceGranularity: ptrutil.ValueOrDefault(requestExtPrebid.Targeting.PriceGranularity), } } diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 475e4564b02..a77004217d9 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -731,7 +731,7 @@ func TestCleanOpenRTBRequests(t *testing.T) { consentedVendors: map[string]bool{"appnexus": true}, }, { - req: AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest(t)}, UserSyncs: &emptyUsersync{}, TCF2Config: emptyTCF2Config}, + req: AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest()}, UserSyncs: &emptyUsersync{}, TCF2Config: emptyTCF2Config}, bidReqAssertions: assertReq, hasError: false, applyCOPPA: false, @@ -798,17 +798,17 @@ func TestCleanOpenRTBRequestsWithFPD(t *testing.T) { }, { description: "Pass valid FPD data for bidders specified in request", - req: AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest(t)}, UserSyncs: &emptyUsersync{}, FirstPartyData: fpd, TCF2Config: emptyTCF2Config}, + req: AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest()}, UserSyncs: &emptyUsersync{}, FirstPartyData: fpd, TCF2Config: emptyTCF2Config}, fpdExpected: true, }, { description: "Bidders specified in request but there is no fpd data for this bidder", - req: AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest(t)}, UserSyncs: &emptyUsersync{}, FirstPartyData: make(map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData), TCF2Config: emptyTCF2Config}, + req: AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest()}, UserSyncs: &emptyUsersync{}, FirstPartyData: make(map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData), TCF2Config: emptyTCF2Config}, fpdExpected: false, }, { description: "No FPD data passed", - req: AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest(t)}, UserSyncs: &emptyUsersync{}, FirstPartyData: nil, TCF2Config: emptyTCF2Config}, + req: AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest()}, UserSyncs: &emptyUsersync{}, FirstPartyData: nil, TCF2Config: emptyTCF2Config}, fpdExpected: false, }, } @@ -1273,7 +1273,7 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { } for _, test := range testCases { - req := newBidRequest(t) + req := newBidRequest() req.Ext = test.reqExt req.Regs = &openrtb2.Regs{ USPrivacy: test.ccpaConsent, @@ -1359,7 +1359,7 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) { } for _, test := range testCases { - req := newBidRequest(t) + req := newBidRequest() req.Ext = test.reqExt req.Regs = &openrtb2.Regs{USPrivacy: test.reqRegsPrivacy} @@ -1428,7 +1428,7 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) { } for _, test := range testCases { - req := newBidRequest(t) + req := newBidRequest() req.Regs = &openrtb2.Regs{COPPA: test.coppa} auctionReq := AuctionRequest{ @@ -1582,7 +1582,7 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) { } for _, test := range testCases { - req := newBidRequest(t) + req := newBidRequest() if test.inSChain != nil { req.Source.SChain = test.inSChain } @@ -1657,7 +1657,7 @@ func TestCleanOpenRTBRequestsBidderParams(t *testing.T) { } for _, test := range testCases { - req := newBidRequestWithBidderParams(t) + req := newBidRequestWithBidderParams() var extRequest *openrtb_ext.ExtRequest if test.inExt != nil { req.Ext = test.inExt @@ -1964,86 +1964,95 @@ func TestGetExtCacheInstructions(t *testing.T) { } func TestGetExtTargetData(t *testing.T) { - type inTest struct { - requestExtPrebid *openrtb_ext.ExtRequestPrebid - cacheInstructions extCacheInstructions - } - type outTest struct { - targetData *targetData - nilTargetData bool - } testCases := []struct { - desc string - in inTest - out outTest + name string + givenRequestExtPrebid *openrtb_ext.ExtRequestPrebid + givenCacheInstructions extCacheInstructions + expectTargetData *targetData }{ { - "nil requestExt, nil outTargetData", - inTest{ - requestExtPrebid: nil, - cacheInstructions: extCacheInstructions{ - cacheBids: true, - cacheVAST: true, - }, - }, - outTest{targetData: nil, nilTargetData: true}, + name: "nil", + givenRequestExtPrebid: nil, + givenCacheInstructions: extCacheInstructions{cacheBids: true, cacheVAST: true}, + expectTargetData: nil, }, { - "Valid requestExt, nil Targeting field, nil outTargetData", - inTest{ - requestExtPrebid: &openrtb_ext.ExtRequestPrebid{ - Targeting: nil, - }, - cacheInstructions: extCacheInstructions{ - cacheBids: true, - cacheVAST: true, - }, - }, - outTest{targetData: nil, nilTargetData: true}, + name: "nil-targeting", + givenRequestExtPrebid: &openrtb_ext.ExtRequestPrebid{Targeting: nil}, + givenCacheInstructions: extCacheInstructions{cacheBids: true, cacheVAST: true}, + expectTargetData: nil, }, { - "Valid targeting data in requestExt, valid outTargetData", - inTest{ - requestExtPrebid: &openrtb_ext.ExtRequestPrebid{ - Targeting: &openrtb_ext.ExtRequestTargeting{ - PriceGranularity: &openrtb_ext.PriceGranularity{ - Precision: ptrutil.ToPtr(2), - Ranges: []openrtb_ext.GranularityRange{{Min: 0.00, Max: 5.00, Increment: 1.00}}, - }, - IncludeWinners: ptrutil.ToPtr(true), - IncludeBidderKeys: ptrutil.ToPtr(true), + name: "populated-full", + givenRequestExtPrebid: &openrtb_ext.ExtRequestPrebid{ + Targeting: &openrtb_ext.ExtRequestTargeting{ + AlwaysIncludeDeals: true, + IncludeBidderKeys: ptrutil.ToPtr(true), + IncludeFormat: true, + IncludeWinners: ptrutil.ToPtr(true), + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{}, + PreferDeals: true, + PriceGranularity: &openrtb_ext.PriceGranularity{ + Precision: ptrutil.ToPtr(2), + Ranges: []openrtb_ext.GranularityRange{{Min: 0.00, Max: 5.00, Increment: 1.00}}, }, }, - cacheInstructions: extCacheInstructions{ - cacheBids: true, - cacheVAST: true, + }, + givenCacheInstructions: extCacheInstructions{ + cacheBids: true, + cacheVAST: true, + }, + expectTargetData: &targetData{ + alwaysIncludeDeals: true, + includeBidderKeys: true, + includeCacheBids: true, + includeCacheVast: true, + includeFormat: true, + includeWinners: true, + mediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{}, + preferDeals: true, + priceGranularity: openrtb_ext.PriceGranularity{ + Precision: ptrutil.ToPtr(2), + Ranges: []openrtb_ext.GranularityRange{{Min: 0.00, Max: 5.00, Increment: 1.00}}, }, }, - outTest{ - targetData: &targetData{ - priceGranularity: openrtb_ext.PriceGranularity{ - Precision: ptrutil.ToPtr(2), - Ranges: []openrtb_ext.GranularityRange{{Min: 0.00, Max: 5.00, Increment: 1.00}}, - }, - includeWinners: true, - includeBidderKeys: true, - includeCacheBids: true, - includeCacheVast: true, + }, + { + name: "populated-pointers-nil", + givenRequestExtPrebid: &openrtb_ext.ExtRequestPrebid{ + Targeting: &openrtb_ext.ExtRequestTargeting{ + AlwaysIncludeDeals: true, + IncludeBidderKeys: nil, + IncludeFormat: true, + IncludeWinners: nil, + MediaTypePriceGranularity: nil, + PreferDeals: true, + PriceGranularity: nil, }, - nilTargetData: false, + }, + givenCacheInstructions: extCacheInstructions{ + cacheBids: true, + cacheVAST: true, + }, + expectTargetData: &targetData{ + alwaysIncludeDeals: true, + includeBidderKeys: false, + includeCacheBids: true, + includeCacheVast: true, + includeFormat: true, + includeWinners: false, + mediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{}, + preferDeals: true, + priceGranularity: openrtb_ext.PriceGranularity{}, }, }, } for _, test := range testCases { - actualTargetData := getExtTargetData(test.in.requestExtPrebid, test.in.cacheInstructions) - - if test.out.nilTargetData { - assert.Nil(t, actualTargetData, "%s. Targeting data should be nil. \n", test.desc) - } else { - assert.NotNil(t, actualTargetData, "%s. Targeting data should NOT be nil. \n", test.desc) - assert.Equal(t, *test.out.targetData, *actualTargetData, "%s. Unexpected targeting data value. \n", test.desc) - } + t.Run(test.name, func(t *testing.T) { + result := getExtTargetData(test.givenRequestExtPrebid, test.givenCacheInstructions) + assert.Equal(t, test.expectTargetData, result) + }) } } @@ -2249,7 +2258,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) { } for _, test := range testCases { - req := newBidRequest(t) + req := newBidRequest() req.Device.Lmt = test.lmt auctionReq := AuctionRequest{ @@ -2355,7 +2364,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { } for _, test := range testCases { - req := newBidRequest(t) + req := newBidRequest() req.User.Consent = test.gdprConsent privacyConfig := config.Privacy{} @@ -2446,7 +2455,7 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) { } for _, test := range testCases { - req := newBidRequest(t) + req := newBidRequest() req.Regs = &openrtb2.Regs{ Ext: json.RawMessage(`{"gdpr":1}`), } @@ -2510,7 +2519,7 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) { func TestCleanOpenRTBRequestsWithOpenRTBDowngrade(t *testing.T) { emptyTCF2Config := gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}) - bidReq := newBidRequest(t) + bidReq := newBidRequest() bidReq.Regs = &openrtb2.Regs{} bidReq.Regs.GPP = "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1NYN" bidReq.Regs.GPPSID = []int8{6} @@ -2713,6 +2722,11 @@ func TestBuildRequestExtForBidder(t *testing.T) { requestExt: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["foo2"]}}},"multibid":[{"bidder":"foo3","maxbids":3,"targetbiddercodeprefix":"fmb"},{"bidders":["pubmatic","groupm"],"maxbids":4}]}}`), expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["foo2"]}}}}}`), }, + { + name: "targeting", + requestExt: json.RawMessage(`{"prebid":{"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"mediatypepricegranularity":{},"includebidderkeys":true,"includewinners":true,"includebrandcategory":{"primaryadserver":1,"publisher":"anyPublisher","withcategory":true}}}}`), + expectedJson: json.RawMessage(`{"prebid":{"targeting":{"includebrandcategory":{"primaryadserver":1,"publisher":"anyPublisher","withcategory":true}}}}`), + }, } for _, test := range testCases { @@ -2772,8 +2786,46 @@ func TestBuildRequestExtForBidder_RequestExtMalformed(t *testing.T) { assert.EqualError(t, err, "expect { or n, but found m") } +func TestBuildRequestExtTargeting(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := buildRequestExtTargeting(nil) + assert.Nil(t, result) + }) + + t.Run("brandcategory-nil", func(t *testing.T) { + given := &openrtb_ext.ExtRequestTargeting{} + + result := buildRequestExtTargeting(given) + assert.Nil(t, result) + }) + + t.Run("brandcategory-populated", func(t *testing.T) { + brandCatgory := &openrtb_ext.ExtIncludeBrandCategory{ + PrimaryAdServer: 1, + Publisher: "anyPublisher", + WithCategory: true, + TranslateCategories: ptrutil.ToPtr(true), + } + + given := &openrtb_ext.ExtRequestTargeting{ + PriceGranularity: &openrtb_ext.PriceGranularity{}, + IncludeBrandCategory: brandCatgory, + IncludeWinners: ptrutil.ToPtr(true), + } + + expected := &openrtb_ext.ExtRequestTargeting{ + PriceGranularity: nil, + IncludeBrandCategory: brandCatgory, + IncludeWinners: nil, + } + + result := buildRequestExtTargeting(given) + assert.Equal(t, expected, result) + }) +} + // newAdapterAliasBidRequest builds a BidRequest with aliases -func newAdapterAliasBidRequest(t *testing.T) *openrtb2.BidRequest { +func newAdapterAliasBidRequest() *openrtb2.BidRequest { dnt := int8(1) return &openrtb2.BidRequest{ Site: &openrtb2.Site{ @@ -2819,7 +2871,7 @@ func newAdapterAliasBidRequest(t *testing.T) *openrtb2.BidRequest { } } -func newBidRequest(t *testing.T) *openrtb2.BidRequest { +func newBidRequest() *openrtb2.BidRequest { return &openrtb2.BidRequest{ Site: &openrtb2.Site{ Page: "www.some.domain.com", @@ -2873,7 +2925,7 @@ func newBidRequest(t *testing.T) *openrtb2.BidRequest { } } -func newBidRequestWithBidderParams(t *testing.T) *openrtb2.BidRequest { +func newBidRequestWithBidderParams() *openrtb2.BidRequest { return &openrtb2.BidRequest{ Site: &openrtb2.Site{ Page: "www.some.domain.com", @@ -3386,7 +3438,7 @@ func TestCleanOpenRTBRequestsBidAdjustment(t *testing.T) { }, } for _, test := range testCases { - req := newBidRequest(t) + req := newBidRequest() accountConfig := config.Account{ GDPR: config.AccountGDPR{ Enabled: &falseValue, @@ -4017,7 +4069,7 @@ func TestCleanOpenRTBRequestsFilterBidderRequestExt(t *testing.T) { } for _, test := range testCases { - req := newBidRequestWithBidderParams(t) + req := newBidRequestWithBidderParams() req.Ext = nil var extRequest *openrtb_ext.ExtRequest if test.inExt != nil { @@ -4973,7 +5025,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { }{ { name: "fetch_bids_request_with_one_bidder_allowed", - req: newBidRequest(t), + req: newBidRequest(), privacyConfig: getFetchBidsActivityConfig("appnexus", true), ortbVersion: "2.6", expectedReqNumber: 1, @@ -4983,7 +5035,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { }, { name: "fetch_bids_request_with_one_bidder_not_allowed", - req: newBidRequest(t), + req: newBidRequest(), privacyConfig: getFetchBidsActivityConfig("appnexus", false), expectedReqNumber: 0, expectedUser: expectedUserDefault, @@ -4992,7 +5044,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { }, { name: "transmit_ufpd_allowed", - req: newBidRequest(t), + req: newBidRequest(), privacyConfig: getTransmitUFPDActivityConfig("appnexus", true), ortbVersion: "2.6", expectedReqNumber: 1, @@ -5004,7 +5056,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { //remove user.eids, user.ext.data.*, user.data.*, user.{id, buyeruid, yob, gender} //and device-specific IDs name: "transmit_ufpd_deny", - req: newBidRequest(t), + req: newBidRequest(), privacyConfig: getTransmitUFPDActivityConfig("appnexus", false), expectedReqNumber: 1, expectedUser: openrtb2.User{ @@ -5034,7 +5086,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { }, { name: "transmit_precise_geo_allowed", - req: newBidRequest(t), + req: newBidRequest(), privacyConfig: getTransmitPreciseGeoActivityConfig("appnexus", true), ortbVersion: "2.6", expectedReqNumber: 1, @@ -5046,7 +5098,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { //round user's geographic location by rounding off IP address and lat/lng data. //this applies to both device.geo and user.geo name: "transmit_precise_geo_deny", - req: newBidRequest(t), + req: newBidRequest(), privacyConfig: getTransmitPreciseGeoActivityConfig("appnexus", false), ortbVersion: "2.6", expectedReqNumber: 1, @@ -5079,7 +5131,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { }, { name: "transmit_tid_allowed", - req: newBidRequest(t), + req: newBidRequest(), privacyConfig: getTransmitTIDActivityConfig("appnexus", true), ortbVersion: "2.6", expectedReqNumber: 1, @@ -5090,7 +5142,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { { //remove source.tid and imp.ext.tid name: "transmit_tid_deny", - req: newBidRequest(t), + req: newBidRequest(), privacyConfig: getTransmitTIDActivityConfig("appnexus", false), ortbVersion: "2.6", expectedReqNumber: 1, diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go index eb01098c097..46cdb1a674a 100644 --- a/openrtb_ext/request.go +++ b/openrtb_ext/request.go @@ -191,21 +191,21 @@ type Adjustment struct { // ExtRequestTargeting defines the contract for bidrequest.ext.prebid.targeting type ExtRequestTargeting struct { - PriceGranularity *PriceGranularity `json:"pricegranularity,omitempty"` - MediaTypePriceGranularity MediaTypePriceGranularity `json:"mediatypepricegranularity,omitempty"` - IncludeWinners *bool `json:"includewinners,omitempty"` - IncludeBidderKeys *bool `json:"includebidderkeys,omitempty"` - IncludeBrandCategory *ExtIncludeBrandCategory `json:"includebrandcategory,omitempty"` - IncludeFormat bool `json:"includeformat,omitempty"` - DurationRangeSec []int `json:"durationrangesec,omitempty"` - PreferDeals bool `json:"preferdeals,omitempty"` - AppendBidderNames bool `json:"appendbiddernames,omitempty"` - AlwaysIncludeDeals bool `json:"alwaysincludedeals,omitempty"` + PriceGranularity *PriceGranularity `json:"pricegranularity,omitempty"` + MediaTypePriceGranularity *MediaTypePriceGranularity `json:"mediatypepricegranularity,omitempty"` + IncludeWinners *bool `json:"includewinners,omitempty"` + IncludeBidderKeys *bool `json:"includebidderkeys,omitempty"` + IncludeBrandCategory *ExtIncludeBrandCategory `json:"includebrandcategory,omitempty"` + IncludeFormat bool `json:"includeformat,omitempty"` + DurationRangeSec []int `json:"durationrangesec,omitempty"` + PreferDeals bool `json:"preferdeals,omitempty"` + AppendBidderNames bool `json:"appendbiddernames,omitempty"` + AlwaysIncludeDeals bool `json:"alwaysincludedeals,omitempty"` } type ExtIncludeBrandCategory struct { - PrimaryAdServer int `json:"primaryadserver"` - Publisher string `json:"publisher"` + PrimaryAdServer int `json:"primaryadserver,omitempty"` + Publisher string `json:"publisher,omitempty"` WithCategory bool `json:"withcategory"` TranslateCategories *bool `json:"translatecategories,omitempty"` } diff --git a/ortb/default.go b/ortb/default.go index 4a25051e78a..ed2523b147a 100644 --- a/ortb/default.go +++ b/ortb/default.go @@ -55,22 +55,24 @@ func setDefaultsTargeting(targeting *openrtb_ext.ExtRequestTargeting) bool { // Default price granularity can be overwritten for video, banner or native bid type // only in case targeting.MediaTypePriceGranularity.Video|Banner|Native != nil. - if targeting.MediaTypePriceGranularity.Video != nil { - if newVideoPG, updated := setDefaultsPriceGranularity(targeting.MediaTypePriceGranularity.Video); updated { - modified = true - targeting.MediaTypePriceGranularity.Video = newVideoPG + if targeting.MediaTypePriceGranularity != nil { + if targeting.MediaTypePriceGranularity.Video != nil { + if newVideoPG, updated := setDefaultsPriceGranularity(targeting.MediaTypePriceGranularity.Video); updated { + modified = true + targeting.MediaTypePriceGranularity.Video = newVideoPG + } } - } - if targeting.MediaTypePriceGranularity.Banner != nil { - if newBannerPG, updated := setDefaultsPriceGranularity(targeting.MediaTypePriceGranularity.Banner); updated { - modified = true - targeting.MediaTypePriceGranularity.Banner = newBannerPG + if targeting.MediaTypePriceGranularity.Banner != nil { + if newBannerPG, updated := setDefaultsPriceGranularity(targeting.MediaTypePriceGranularity.Banner); updated { + modified = true + targeting.MediaTypePriceGranularity.Banner = newBannerPG + } } - } - if targeting.MediaTypePriceGranularity.Native != nil { - if newNativePG, updated := setDefaultsPriceGranularity(targeting.MediaTypePriceGranularity.Native); updated { - modified = true - targeting.MediaTypePriceGranularity.Native = newNativePG + if targeting.MediaTypePriceGranularity.Native != nil { + if newNativePG, updated := setDefaultsPriceGranularity(targeting.MediaTypePriceGranularity.Native); updated { + modified = true + targeting.MediaTypePriceGranularity.Native = newNativePG + } } } diff --git a/ortb/default_test.go b/ortb/default_test.go index a52fc508cb7..fd77ba4c547 100644 --- a/ortb/default_test.go +++ b/ortb/default_test.go @@ -38,7 +38,7 @@ func TestSetDefaults(t *testing.T) { { name: "targeting", // tests integration with setDefaultsTargeting givenRequest: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"targeting":{}}}`)}, - expectedRequest: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"mediatypepricegranularity":{},"includewinners":true,"includebidderkeys":true}}}`)}, + expectedRequest: openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true}}}`)}, }, { name: "imp", // tests integration with setDefaultsImp @@ -162,7 +162,7 @@ func TestSetDefaultsTargeting(t *testing.T) { Precision: ptrutil.ToPtr(4), Ranges: nil, }, - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Video: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(4), Ranges: nil, @@ -179,7 +179,7 @@ func TestSetDefaultsTargeting(t *testing.T) { }, expectedTargeting: &openrtb_ext.ExtRequestTargeting{ PriceGranularity: &defaultGranularity, - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Video: &defaultGranularity, Banner: &defaultGranularity, Native: &defaultGranularity, @@ -189,6 +189,23 @@ func TestSetDefaultsTargeting(t *testing.T) { }, expectedModified: true, }, + { + name: "populated-ranges-nil-mediatypepricegranularity-nil", + givenTargeting: &openrtb_ext.ExtRequestTargeting{ + PriceGranularity: &openrtb_ext.PriceGranularity{ + Precision: ptrutil.ToPtr(4), + Ranges: nil, + }, + MediaTypePriceGranularity: nil, + }, + expectedTargeting: &openrtb_ext.ExtRequestTargeting{ + PriceGranularity: &defaultGranularity, + MediaTypePriceGranularity: nil, + IncludeWinners: ptrutil.ToPtr(DefaultTargetingIncludeWinners), + IncludeBidderKeys: ptrutil.ToPtr(DefaultTargetingIncludeBidderKeys), + }, + expectedModified: true, + }, { name: "populated-ranges-empty", givenTargeting: &openrtb_ext.ExtRequestTargeting{ @@ -211,7 +228,7 @@ func TestSetDefaultsTargeting(t *testing.T) { Precision: ptrutil.ToPtr(4), Ranges: []openrtb_ext.GranularityRange{}, }, - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Video: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(4), Ranges: []openrtb_ext.GranularityRange{}, @@ -228,7 +245,7 @@ func TestSetDefaultsTargeting(t *testing.T) { }, expectedTargeting: &openrtb_ext.ExtRequestTargeting{ PriceGranularity: &defaultGranularity, - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Video: &defaultGranularity, Banner: &defaultGranularity, Native: &defaultGranularity, @@ -265,7 +282,7 @@ func TestSetDefaultsTargeting(t *testing.T) { Precision: ptrutil.ToPtr(4), Ranges: []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}}, }, - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Video: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(4), Ranges: []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}}, @@ -287,7 +304,7 @@ func TestSetDefaultsTargeting(t *testing.T) { Precision: ptrutil.ToPtr(4), Ranges: []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}}, }, - MediaTypePriceGranularity: openrtb_ext.MediaTypePriceGranularity{ + MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ Video: &openrtb_ext.PriceGranularity{ Precision: ptrutil.ToPtr(4), Ranges: []openrtb_ext.GranularityRange{{Min: 0, Max: 10, Increment: 1}}},