Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions adapters/bidder.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ type ExtraRequestInfo struct {
GlobalPrivacyControlHeader string
CurrencyConversions currency.Conversions
PreferredMediaType openrtb_ext.BidType
PageViewId string
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fields in this structure need to be passed this way because they are not present in the request body. The PageViewID is in the request body. I understand it needs to be scoped to each bidder, but why can't the bidder access that information within the request?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, do you recommend removing all the other page view IDs from the bid request and only keeping the one specific to that adapter?

}

func NewExtraRequestInfo(c currency.Conversions) ExtraRequestInfo {
Expand Down
23 changes: 23 additions & 0 deletions adapters/kobler/kobler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kobler

import (
"encoding/json"
"fmt"
"net/http"
"slices"
Expand Down Expand Up @@ -33,6 +34,14 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server co
return bidder, nil
}

type RequestExt struct {
Kobler *KoblerRequestExt `json:"kobler,omitempty"`
}

type KoblerRequestExt struct {
PageViewId string `json:"page_view_id,omitempty"`
}

func (a adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
var requestData []*adapters.RequestData
var errors []error
Expand All @@ -45,6 +54,20 @@ func (a adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.Ex
sanitizedRequest.Cur = append(sanitizedRequest.Cur, supportedCurrency)
}

if reqInfo.PageViewId != "" {
var ext RequestExt
ext.Kobler = &KoblerRequestExt{
reqInfo.PageViewId,
}
jsonExt, err := json.Marshal(ext)
if err != nil {
errors = append(errors, err)
return nil, errors
}

sanitizedRequest.Ext = jsonExt
}

for i := range sanitizedRequest.Imp {
if err := convertImpCurrency(&sanitizedRequest.Imp[i], reqInfo); err != nil {
errors = append(errors, err)
Expand Down
4 changes: 3 additions & 1 deletion exchange/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ type AuctionRequest struct {
GDPREnforced bool
}

// BidderRequest holds the bidder specific request and all other
// BidderRequest holds the bidder-specific request and all other
// information needed to process that bidder request.
type BidderRequest struct {
BidRequest *openrtb2.BidRequest
Expand All @@ -230,6 +230,7 @@ type BidderRequest struct {
BidderStoredResponses map[string]json.RawMessage
IsRequestAlias bool
ImpReplaceImpId map[string]bool
PageViewId string
}

func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog *DebugLog) (*AuctionResponse, error) {
Expand Down Expand Up @@ -758,6 +759,7 @@ func (e *exchange) getAllBids(
reqInfo := adapters.NewExtraRequestInfo(conversions)
reqInfo.PbsEntryPoint = bidderRequest.BidderLabels.RType
reqInfo.GlobalPrivacyControlHeader = globalPrivacyControlHeader
reqInfo.PageViewId = bidderRequest.PageViewId

if len(liveAdaptersPreferredMediaType) > 0 {
if mtype, found := liveAdaptersPreferredMediaType[bidder.BidderName]; found {
Expand Down
41 changes: 41 additions & 0 deletions exchange/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type requestSplitter struct {
// 1. BidRequest.Imp[].Ext will only contain the "prebid" field and a "bidder" field which has the params for the intended Bidder.
// 2. Every BidRequest.Imp[] requested Bids from the Bidder who keys it.
// 3. BidRequest.User.BuyerUID will be set to that Bidder's ID.
// 4. BidRequest.Ext.PageViewId will be set to that Bidder's ID.
func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
auctionReq AuctionRequest,
requestExt *openrtb_ext.ExtRequest,
Expand Down Expand Up @@ -92,6 +93,18 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
lowerCaseExplicitBuyerUIDs[lowerKey] = uid
}

pageViewIds, err := extractAndCleanPageViewIds(req)
if err != nil {
errs = []error{err}
return
}

lowerCasePageViewIds := make(map[string]string)
for bidder, pageViewId := range pageViewIds {
lowerKey := strings.ToLower(bidder)
lowerCasePageViewIds[lowerKey] = pageViewId
}

bidderParamsInReqExt, err := ExtractReqExtBidderParamsMap(req.BidRequest)
if err != nil {
errs = []error{err}
Expand Down Expand Up @@ -246,6 +259,8 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
bidderLabels.AdapterBids = metrics.AdapterBidPresent
}

pageViewId := pageViewIds[strings.ToLower(bidder)]

bidderRequest := BidderRequest{
BidderName: openrtb_ext.BidderName(bidder),
BidderCoreName: coreBidder,
Expand All @@ -254,6 +269,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
BidderStoredResponses: bidderImpWithBidResp[openrtb_ext.BidderName(bidder)],
ImpReplaceImpId: auctionReq.BidderImpReplaceImpID[bidder],
BidderLabels: bidderLabels,
PageViewId: pageViewId,
}
bidderRequests = append(bidderRequests, bidderRequest)
}
Expand Down Expand Up @@ -638,6 +654,31 @@ func extractAndCleanBuyerUIDs(req *openrtb_ext.RequestWrapper) (map[string]strin
return buyerUIDs, nil
}

// extractAndCleanPageViewIds parses the values from ext.prebid.page_view_ids, and then deletes those values from the ext.
// This prevents a Bidder from getting access to page view IDs of other Bidders.
func extractAndCleanPageViewIds(req *openrtb_ext.RequestWrapper) (map[string]string, error) {
if req.Ext == nil {
return nil, nil
}

requestExt, err := req.GetRequestExt()
if err != nil {
return nil, err
}

prebid := requestExt.GetPrebid()
if prebid == nil {
return nil, nil
}

pageViewIds := prebid.PageViewIds

prebid.PageViewIds = nil
requestExt.SetPrebid(prebid)

return pageViewIds, nil
}

// splitImps takes a list of Imps and returns a map of imps which have been sanitized for each bidder.
//
// For example, suppose imps has two elements. One goes to rubicon, while the other goes to appnexus and index.
Expand Down
120 changes: 119 additions & 1 deletion exchange/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3976,7 +3976,125 @@ func TestBuildExtData(t *testing.T) {
}
}

func TestCleanOpenRTBRequestsFilterBidderRequestExt(t *testing.T) {
func TestCleanOpenRTBRequestsFilterBidderRequestExtForPageViewIds(t *testing.T) {
pageViewId1 := "a147eab5-9b1f-4fdc-b1f8-900a58c87a39"
pageViewId2 := "468dd24e-de62-4d2b-bdef-1daaea079e4c"
testCases := []struct {
desc string
inExt json.RawMessage
wantExt []json.RawMessage
wantPageViewIds []string
}{
{
desc: "Nil request ext, empty page view IDs",
inExt: nil,
wantExt: nil,
wantPageViewIds: []string{"", ""},
},
{
desc: "Missing prebid in request ext, empty page view IDs",
inExt: json.RawMessage(`{}`),
wantExt: nil,
wantPageViewIds: []string{"", ""},
},
{
desc: "Missing page_view_ids in prebid in request ext, empty page view IDs",
inExt: json.RawMessage(`{"prebid": {}}`),
wantExt: nil,
wantPageViewIds: []string{"", ""},
},
{
desc: "Empty page_view_ids in prebid in request ext, empty page view IDs",
inExt: json.RawMessage(`{"prebid": {"page_view_ids":{}, "channel":{"name":"b","version":"c"}}}`),
wantExt: []json.RawMessage{
json.RawMessage(`{"prebid":{"channel":{"name":"b","version":"c"}}}`),
json.RawMessage(`{"prebid":{"channel":{"name":"b","version":"c"}}}`),
},
wantPageViewIds: []string{"", ""},
},
{
desc: "Non-matching page_view_ids in prebid in request ext, empty page view IDs",
inExt: json.RawMessage(`{"prebid":{"page_view_ids":{
"some-bidder":"page-view-id-for-some-bidder",
"second-bidder":"page-view-id-for-second-bidder"
}}}`),
wantExt: nil,
wantPageViewIds: []string{"", ""},
},
{
desc: "Matching page_view_ids in prebid in request ext, non-empty page view IDs",
inExt: json.RawMessage(`{"prebid": { "channel": { "name":"b", "version":"c" }, "page_view_ids": {
"some-bidder":"page-view-id-for-some-bidder",
"appnexus": "a147eab5-9b1f-4fdc-b1f8-900a58c87a39",
"pubmatic": "468dd24e-de62-4d2b-bdef-1daaea079e4c"
}}}`),
wantExt: []json.RawMessage{
json.RawMessage(`{"prebid":{"channel":{"name":"b","version":"c"}}}`),
json.RawMessage(`{"prebid":{"channel":{"name":"b","version":"c"}}}`),
},
wantPageViewIds: []string{pageViewId1, pageViewId2},
},
{
desc: "One matching page_view_id in prebid in request ext, one non-empty page view ID",
inExt: json.RawMessage(`{ "prebid": { "channel": { "name":"b", "version":"c" }, "page_view_ids": {
"some-bidder":"page-view-id-for-some-bidder",
"appnexus": "a147eab5-9b1f-4fdc-b1f8-900a58c87a39"
}}}`),
wantExt: []json.RawMessage{
json.RawMessage(`{"prebid":{"channel":{"name":"b","version":"c"}}}`),
json.RawMessage(`{"prebid":{"channel":{"name":"b","version":"c"}}}`),
},
wantPageViewIds: []string{pageViewId1, ""},
},
}

for _, test := range testCases {
req := newBidRequestWithBidderParams()
req.Ext = nil
var extRequest *openrtb_ext.ExtRequest
if test.inExt != nil {
req.Ext = test.inExt
extRequest = &openrtb_ext.ExtRequest{}
err := jsonutil.UnmarshalValid(req.Ext, extRequest)
assert.NoErrorf(t, err, test.desc+":Error unmarshaling inExt")
}

auctionReq := AuctionRequest{
BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
UserSyncs: &emptyUsersync{},
Account: config.Account{},
TCF2Config: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
}
gdprPermissionsBuilder := fakePermissionsBuilder{
permissions: &permissionsMock{
allowAllBidders: true,
},
}.Builder

reqSplitter := &requestSplitter{
bidderToSyncerKey: map[string]string{},
me: &metrics.MetricsEngineMock{},
privacyConfig: config.Privacy{},
gdprPermsBuilder: gdprPermissionsBuilder,
hostSChainNode: nil,
bidderInfo: config.BidderInfos{},
}

bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, map[string]float64{})
assert.Equal(t, 0, len(errs), test.desc)
sort.Slice(bidderRequests, func(i, j int) bool {
return bidderRequests[i].BidderCoreName < bidderRequests[j].BidderCoreName
})
for i, wantBidderRequest := range test.wantExt {
assert.Equal(t, wantBidderRequest, bidderRequests[i].BidRequest.Ext, test.desc+" : "+string(bidderRequests[i].BidderCoreName)+"\n\t\tGotRequestExt : "+string(bidderRequests[i].BidRequest.Ext))
}
for i, wantPageViewId := range test.wantPageViewIds {
assert.Equal(t, wantPageViewId, bidderRequests[i].PageViewId, test.desc+" : "+string(bidderRequests[i].BidderCoreName)+"\n\t\tGotPageViewId : "+bidderRequests[i].PageViewId)
}
}
}

func TestCleanOpenRTBRequestsFilterBidderRequestExtForAlternateBidderCodes(t *testing.T) {
testCases := []struct {
desc string
inExt json.RawMessage
Expand Down
1 change: 1 addition & 0 deletions openrtb_ext/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type ExtRequest struct {
type ExtRequestPrebid struct {
AdServerTargeting []AdServerTarget `json:"adservertargeting,omitempty"`
Aliases map[string]string `json:"aliases,omitempty"`
PageViewIds map[string]string `json:"page_view_ids,omitempty"`
AliasGVLIDs map[string]uint16 `json:"aliasgvlids,omitempty"`
Analytics map[string]json.RawMessage `json:"analytics,omitempty"`
BidAdjustmentFactors map[string]float64 `json:"bidadjustmentfactors,omitempty"`
Expand Down
Loading