Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ORTB 2.6: Push wrapper into Eidpermissions and ApplyFPD #3998

Merged
merged 7 commits into from
Oct 24, 2024

Conversation

VeronikaSolovei9
Copy link
Contributor

No description provided.

@VeronikaSolovei9 VeronikaSolovei9 changed the base branch from master to ortb26 October 19, 2024 05:43
Comment on lines +170 to +175
// eid scrubbing
if err := removeUnpermissionedEids(reqWrapperCopy, bidder); err != nil {
errs = append(errs, fmt.Errorf("unable to enforce request.ext.prebid.data.eidpermissions because %v", err))
continue
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removeUnpermissionedEids function should be executed before buildRequestExtForBidder because it needs access to req.ext.prebid.data.eidpermissions.

buildRequestExtForBidder function rebuilds req.ext and doesn't add req.ext.prebid.data.eidpermissions.

Copy link
Collaborator

@bsardo bsardo Oct 19, 2024

Choose a reason for hiding this comment

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

I think you're right. It looks like we just need to read from req.ext.prebid.data and copy any applicable EIDs to req.user and then we can discard req.ext.prebid.data. If that's the case then we should move removeUnpermissionedEids just before buildRequestExtForBidder since buildRequestExtForBidder will delete req.ext.prebid.data.

@bsardo bsardo changed the title Eidpermissions reqwrapper ORTB 2.6: Push wrapper into Eidpermissions and ApplyFPD Oct 20, 2024
Copy link
Collaborator

@hhhjort hhhjort left a comment

Choose a reason for hiding this comment

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

LGTM


// exit early if there are no eids (empty array)
if len(eids) == 0 {
if reqExt.GetPrebid() == nil || reqExt.GetPrebid().Data == nil || len(reqExt.GetPrebid().Data.EidPermissions) == 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

You might want to consider setting GetPrebid() to a variable to avoid the unnecessary struct copy operation that will take place each time it is called:

reqExtPrebid := reqExt.GetPrebid()
if reqExtPrebid == nil || reqExtPrebid.Data == nil || len(reqExtPrebid.Data.EidPermissions) == 0 {
	return nil
}

You can also use this variable in the range loop on line 783.

Comment on lines 975 to 982
if fpdToApply.User != nil {
//BuyerUID is a value obtained between fpd extraction and fpd application.
//BuyerUID needs to be set back to fpd before applying this fpd to final bidder request
if bidRequest.User != nil && len(bidRequest.User.BuyerUID) > 0 {
fpdToApply.User.BuyerUID = bidRequest.User.BuyerUID
if reqWrapper.User != nil && len(reqWrapper.User.BuyerUID) > 0 {
fpdToApply.User.BuyerUID = reqWrapper.User.BuyerUID
}
bidRequest.User = fpdToApply.User
reqWrapper.User = fpdToApply.User
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we might have always had a bug here. Notice how we first update fpdToApply.User.BuyerUID to reqWrapper.User.BuyerUID before assigning fpdToApply.User to reqWrapper.User in case the buyer UID was updated after fpdToApply.User was populated upstream. Similarly, I think we should copy reqWrapper.User.EIDs to fpdToApply.User.EIDs because EIDs might have been removed by removeUnpermissionedEids.

}
bidRequest.User = fpdToApply.User
reqWrapper.User = fpdToApply.User
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just checking, this statement will change the user object pointer on reqWrapper to point to the user object on fpdToApply. I believe the fpdToApply user object may contain changes to the user ext. Will the request wrapper behave correctly when RebuildRequest is called or will the ext changes be lost? I think the changes may be lost if a change is made to the UserExt object on the request wrapper that result in it being marked as dirty so that a user ext rebuild is triggered when RebuildRequest is called.
If you all agree that this is a problem, I think it could be fixed in a future PR after we have released ORTB 2.6 support since I think this issue has always existed.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, if fpdToApply.User.ext has been modified, then the wrapper will overwrite those changes when the request is rebuilt.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, we will need to fix it.

//BuyerUID needs to be set back to fpd before applying this fpd to final bidder request
fpdToApply.User.BuyerUID = reqWrapper.User.BuyerUID
}
if len(reqWrapper.User.EIDs) > 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think we want the conditional. Maybe this is simply just the statement fpdToApply.User.EIDs = reqWrapper.User.EIDs so it always executes. It's possible that there were EIDs but they were all removed bringing the count to 0. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed offline. More changes are in progress

if len(userExt) == 0 {
setUserExtWithCopy(request, nil)
return nil
reqWrapper.User.EIDs = eidsAllowed
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need both cases anymore? If eidsAllowed is null when there are no eids allowed, then we can get rid of the if statement and just set reqWrapper.User.EIDs = eidsAllowed.

Copy link
Collaborator

@bsardo bsardo Oct 22, 2024

Choose a reason for hiding this comment

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

I think we can remove the conditional here if we change
eidsAllowed := make([]openrtb2.EID, 0, len(eids)) to var eidsAllowed []openrtb2.EID
If we do that eidsAllowed will start off as nil, however there will be the performance penalty when building up that slice via append.
@SyntaxNode thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Discussed offline. We'll keep it the way it is using make to initialize eidsAllowed and the conditional check here.

@@ -157,6 +157,8 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
bidderRequests = make([]BidderRequest, 0, len(impsByBidder))

for bidder, imps := range impsByBidder {
userEIDsOverride := fpdUserEIDExists(req, auctionReq.FirstPartyData, bidder)
fpdUserEIDExists(req, auctionReq.FirstPartyData, bidder)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you can delete the second occurrence of fpdUserEIDExists here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, good catch, I used it for the debugging and forgot to remove.

@@ -157,6 +157,8 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
bidderRequests = make([]BidderRequest, 0, len(impsByBidder))

for bidder, imps := range impsByBidder {
userEIDsOverride := fpdUserEIDExists(req, auctionReq.FirstPartyData, bidder)
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about changing this variable name to fpdUserEIDsExist or fpdUserEIDsPresent? I think this will be much easier to understand downstream if you make this same change to the parameter passed to applyFPD as your conditional there will then read:

if !fpdUserEIDsExist {
	fpdToApply.User.EIDs = reqWrapper.User.EIDs
}

The override language confuses me a bit.

Comment on lines +278 to +280
if len(fpdUserEIDs) == 0 {
return false
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we remove this statement? Technically it is possible that EID is specified as first party data as an empty array.
We can just lean on your length check and for loop below to make the determination.

pReqUserEID := &reqUserEIDs[i]
pFpdUserEID := &fpdUserEIDs[i]
if pReqUserEID != pFpdUserEID {
return false
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't this supposed to be return true?

}

}
return true
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't this supposed to be return false?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored

fpdToApply.User.EIDs = reqWrapper.User.EIDs

// if FPD config didn't have user.eids - use reqWrapper.User.EIDs after removeUnpermissionedEids
if userEIDsOverride {
Copy link
Collaborator

Choose a reason for hiding this comment

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

As mentioned in an earlier comment, I suggest changing this variable name to fpdUserEIDsExist so the meaning is clearer.

Also, isn't this conditional supposed to be negated: if !fpdUserEIDsExist?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed

@@ -819,37 +844,15 @@ func removeUnpermissionedEids(request *openrtb2.BidRequest, bidder string, reque
return nil
}

// marshal eidsAllowed back to userExt
// set eidsAllowed back to userExt
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you either change the userExt part of this comment or delete the comment?

if len(userExt) == 0 {
setUserExtWithCopy(request, nil)
return nil
reqWrapper.User.EIDs = eidsAllowed
}
Copy link
Collaborator

@bsardo bsardo Oct 22, 2024

Choose a reason for hiding this comment

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

I think we can remove the conditional here if we change
eidsAllowed := make([]openrtb2.EID, 0, len(eids)) to var eidsAllowed []openrtb2.EID
If we do that eidsAllowed will start off as nil, however there will be the performance penalty when building up that slice via append.
@SyntaxNode thoughts?

return false
}
fpdUserEIDs := fpdToApply.User.EIDs
reqUserEIDs := req.User.EIDs
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could User be nil?

Copy link
Collaborator

Choose a reason for hiding this comment

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

if len(fpdUserEIDs) == 0 {
    return false
}
if req.User == nil  {
    return true
}

Comment on lines 3555 to 3577
description: "req.User is defined and had bidder fpd user eids (userEIDsOverride); bidderFPD.User defined and has EIDs. Expect to see user.EIDs in result request taken from fpd",
inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
"bidderNormalized": {Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId", EIDs: []openrtb2.EID{{Source: "source1"}, {Source: "source2"}}}},
},
inputBidderName: "bidderFromRequest",
inputBidderCoreName: "bidderNormalized",
inputBidderIsRequestAlias: false,
inputRequest: openrtb2.BidRequest{User: &openrtb2.User{ID: "UserId", EIDs: []openrtb2.EID{{Source: "source3"}, {Source: "source4"}}}},
expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId", EIDs: []openrtb2.EID{{Source: "source1"}, {Source: "source2"}}}},
fpdUserEIDsExisted: true,
},
{
description: "req.User is defined and doesn't have fpr user eids (userEIDsOverride); bidderFPD.User defined and has EIDs. Expect to see user.EIDs in result request taken from original req",
inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
"bidderNormalized": {Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId", EIDs: []openrtb2.EID{{Source: "source1"}, {Source: "source2"}}}},
},
inputBidderName: "bidderFromRequest",
inputBidderCoreName: "bidderNormalized",
inputBidderIsRequestAlias: false,
inputRequest: openrtb2.BidRequest{User: &openrtb2.User{ID: "UserId", EIDs: []openrtb2.EID{{Source: "source3"}, {Source: "source4"}}}},
expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId", EIDs: []openrtb2.EID{{Source: "source3"}, {Source: "source4"}}}},
fpdUserEIDsExisted: false,
},
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpick: remove references to variable userEIDsOverride in the descriptions.

reqWrapper,
testCase.fpdUserEIDsExisted,
)
assert.Equal(t, &testCase.expectedRequest, reqWrapper.BidRequest, fmt.Sprintf("incorrect request after applying fpd, testcase %s", testCase.description))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpick: you can delete the last parameter now that you're using t.Run.

Comment on lines 3059 to 3060
assert.NoError(t, resultErr, test.description)
assert.Equal(t, expectedRequest, reqWrapper.BidRequest, test.description)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpick: you can delete the last parameter now that you're using t.Run.

Copy link
Collaborator

@hhhjort hhhjort left a comment

Choose a reason for hiding this comment

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

LGTM

@hhhjort hhhjort merged commit b2d6db8 into ortb26 Oct 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants