-
Notifications
You must be signed in to change notification settings - Fork 769
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
Modules activities #3057
Modules activities #3057
Changes from all commits
8bdbbae
89509aa
03340ba
a0c2acb
0171679
e7e03cb
97b2639
d0de9a9
c830408
2d9dfc9
0c4a4db
a026019
f2702ae
90c4f20
d019263
30ca359
e67b1d9
9b71cd2
bf08739
921a52d
fb6be8a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,13 +3,15 @@ package hookexecution | |
import ( | ||
"context" | ||
"fmt" | ||
"github.com/prebid/prebid-server/v2/ortb" | ||
"strings" | ||
"sync" | ||
"time" | ||
|
||
"github.com/prebid/prebid-server/v2/hooks" | ||
"github.com/prebid/prebid-server/v2/hooks/hookstage" | ||
"github.com/prebid/prebid-server/v2/metrics" | ||
"github.com/prebid/prebid-server/v2/privacy" | ||
) | ||
|
||
type hookResponse[T any] struct { | ||
|
@@ -66,10 +68,11 @@ func executeGroup[H any, P any]( | |
|
||
for _, hook := range group.Hooks { | ||
mCtx := executionCtx.getModuleContext(hook.Module) | ||
newPayload := handleModuleActivities(hook.Code, executionCtx.activityControl, payload) | ||
wg.Add(1) | ||
go func(hw hooks.HookWrapper[H], moduleCtx hookstage.ModuleInvocationContext) { | ||
defer wg.Done() | ||
executeHook(moduleCtx, hw, payload, hookHandler, group.Timeout, resp, rejected) | ||
executeHook(moduleCtx, hw, newPayload, hookHandler, group.Timeout, resp, rejected) | ||
}(hook, mCtx) | ||
} | ||
|
||
|
@@ -176,7 +179,7 @@ func handleHookResponse[P any]( | |
metricEngine metrics.MetricsEngine, | ||
) (P, HookOutcome, *RejectError) { | ||
var rejectErr *RejectError | ||
labels := metrics.ModuleLabels{Module: moduleReplacer.Replace(hr.HookID.ModuleCode), Stage: ctx.stage, AccountID: ctx.accountId} | ||
labels := metrics.ModuleLabels{Module: moduleReplacer.Replace(hr.HookID.ModuleCode), Stage: ctx.stage, AccountID: ctx.accountID} | ||
metricEngine.RecordModuleCalled(labels, hr.ExecutionTime) | ||
|
||
hookOutcome := HookOutcome{ | ||
|
@@ -311,3 +314,29 @@ func handleHookMutations[P any]( | |
|
||
return payload | ||
} | ||
|
||
func handleModuleActivities[P any](hookCode string, activityControl privacy.ActivityControl, payload P) P { | ||
payloadData, ok := any(&payload).(hookstage.RequestUpdater) | ||
if !ok { | ||
return payload | ||
} | ||
|
||
scopeGeneral := privacy.Component{Type: privacy.ComponentTypeGeneral, Name: hookCode} | ||
transmitUserFPDActivityAllowed := activityControl.Allow(privacy.ActivityTransmitUserFPD, scopeGeneral, privacy.ActivityRequest{}) | ||
|
||
if !transmitUserFPDActivityAllowed { | ||
// changes need to be applied to new payload and leave original payload unchanged | ||
bidderReq := payloadData.GetBidderRequestPayload() | ||
|
||
bidderReqCopy := ortb.CloneBidderReq(bidderReq.BidRequest) | ||
|
||
privacy.ScrubUserFPD(bidderReqCopy) | ||
|
||
var newPayload = payload | ||
var np = any(&newPayload).(hookstage.RequestUpdater) | ||
np.SetBidderRequestPayload(bidderReqCopy) | ||
return newPayload | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the point of newPayload? Does it make a shallow copy? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this point we know already that request will be changed for this module (for instance user.ID will be removed). if privacyEnforcement.AnyActivities() {
// changes need to be applied to new payload and leave original payload unchanged
bidderReq := payloadData.GetBidderRequestPayload()
bidderReqCopy := ptrutil.Clone(bidderReq)
privacyEnforcement.Apply(bidderReqCopy)
var newPayload = payload
var np = any(&newPayload).(hookstage.PayloadBidderRequest)
np.SetBidderRequestPayload(bidderReqCopy)
return newPayload
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So you have a shallow copy of the bidrequest. That shallow copy includes a pointer to the original There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What you are saying is correct. In this case User copy will be handled inside of Apply function. It will execute var userCopy *openrtb2.User
userCopy = ptrutil.Clone(bidRequest.User)
...
...
bidRequest.User = userCopy
return bidRequest |
||
} | ||
|
||
return payload | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package hookexecution | ||
|
||
import ( | ||
"github.com/prebid/openrtb/v19/openrtb2" | ||
"github.com/prebid/prebid-server/v2/config" | ||
"github.com/prebid/prebid-server/v2/hooks/hookstage" | ||
"github.com/prebid/prebid-server/v2/openrtb_ext" | ||
"github.com/prebid/prebid-server/v2/privacy" | ||
"github.com/stretchr/testify/assert" | ||
"testing" | ||
) | ||
|
||
func TestHandleModuleActivitiesBidderRequestPayload(t *testing.T) { | ||
|
||
testCases := []struct { | ||
description string | ||
hookCode string | ||
privacyConfig *config.AccountPrivacy | ||
inPayloadData hookstage.BidderRequestPayload | ||
expectedPayloadData hookstage.BidderRequestPayload | ||
}{ | ||
{ | ||
description: "payload should change when userFPD is blocked by activity", | ||
hookCode: "foo", | ||
inPayloadData: hookstage.BidderRequestPayload{ | ||
Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ | ||
User: &openrtb2.User{ID: "test_user_id"}, | ||
}}, | ||
}, | ||
privacyConfig: getTransmitUFPDActivityConfig("foo", false), | ||
expectedPayloadData: hookstage.BidderRequestPayload{ | ||
Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ | ||
User: &openrtb2.User{ID: ""}, | ||
}, | ||
}}, | ||
}, | ||
{ | ||
description: "payload should not change when userFPD is not blocked by activity", | ||
hookCode: "foo", | ||
inPayloadData: hookstage.BidderRequestPayload{ | ||
Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ | ||
User: &openrtb2.User{ID: "test_user_id"}, | ||
}}, | ||
}, | ||
privacyConfig: getTransmitUFPDActivityConfig("foo", true), | ||
expectedPayloadData: hookstage.BidderRequestPayload{ | ||
Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ | ||
User: &openrtb2.User{ID: "test_user_id"}, | ||
}}, | ||
}, | ||
}, | ||
} | ||
for _, test := range testCases { | ||
t.Run(test.description, func(t *testing.T) { | ||
//check input payload didn't change | ||
origInPayloadData := test.inPayloadData | ||
activityControl := privacy.NewActivityControl(test.privacyConfig) | ||
newPayload := handleModuleActivities(test.hookCode, activityControl, test.inPayloadData) | ||
assert.Equal(t, test.expectedPayloadData.Request.BidRequest, newPayload.Request.BidRequest) | ||
assert.Equal(t, origInPayloadData, test.inPayloadData) | ||
}) | ||
} | ||
} | ||
|
||
func TestHandleModuleActivitiesProcessedAuctionRequestPayload(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like processed auction request works because the payload type is able to be casted to the bidder request payload type, is that correct? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct, |
||
|
||
testCases := []struct { | ||
description string | ||
hookCode string | ||
privacyConfig *config.AccountPrivacy | ||
inPayloadData hookstage.ProcessedAuctionRequestPayload | ||
expectedPayloadData hookstage.ProcessedAuctionRequestPayload | ||
}{ | ||
{ | ||
description: "payload should change when userFPD is blocked by activity", | ||
hookCode: "foo", | ||
inPayloadData: hookstage.ProcessedAuctionRequestPayload{ | ||
Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ | ||
User: &openrtb2.User{ID: "test_user_id"}, | ||
}}, | ||
}, | ||
privacyConfig: getTransmitUFPDActivityConfig("foo", false), | ||
expectedPayloadData: hookstage.ProcessedAuctionRequestPayload{ | ||
Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ | ||
User: &openrtb2.User{ID: ""}, | ||
}}, | ||
}, | ||
}, | ||
{ | ||
description: "payload should not change when userFPD is not blocked by activity", | ||
hookCode: "foo", | ||
inPayloadData: hookstage.ProcessedAuctionRequestPayload{ | ||
Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ | ||
User: &openrtb2.User{ID: "test_user_id"}, | ||
}}, | ||
}, | ||
privacyConfig: getTransmitUFPDActivityConfig("foo", true), | ||
expectedPayloadData: hookstage.ProcessedAuctionRequestPayload{ | ||
Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ | ||
User: &openrtb2.User{ID: "test_user_id"}, | ||
}}, | ||
}, | ||
}, | ||
} | ||
for _, test := range testCases { | ||
t.Run(test.description, func(t *testing.T) { | ||
//check input payload didn't change | ||
origInPayloadData := test.inPayloadData | ||
activityControl := privacy.NewActivityControl(test.privacyConfig) | ||
newPayload := handleModuleActivities(test.hookCode, activityControl, test.inPayloadData) | ||
assert.Equal(t, test.expectedPayloadData.Request.BidRequest, newPayload.Request.BidRequest) | ||
assert.Equal(t, origInPayloadData, test.inPayloadData) | ||
}) | ||
} | ||
} | ||
|
||
func TestHandleModuleActivitiesNoBidderRequestPayload(t *testing.T) { | ||
|
||
testCases := []struct { | ||
description string | ||
hookCode string | ||
privacyConfig *config.AccountPrivacy | ||
inPayloadData hookstage.RawAuctionRequestPayload | ||
expectedPayloadData hookstage.RawAuctionRequestPayload | ||
}{ | ||
{ | ||
description: "payload should change when userFPD is blocked by activity", | ||
hookCode: "foo", | ||
inPayloadData: hookstage.RawAuctionRequestPayload{}, | ||
privacyConfig: getTransmitUFPDActivityConfig("foo", false), | ||
expectedPayloadData: hookstage.RawAuctionRequestPayload{}, | ||
}, | ||
{ | ||
description: "payload should not change when userFPD is not blocked by activity", | ||
hookCode: "foo", | ||
inPayloadData: hookstage.RawAuctionRequestPayload{}, | ||
privacyConfig: getTransmitUFPDActivityConfig("foo", true), | ||
expectedPayloadData: hookstage.RawAuctionRequestPayload{}, | ||
}, | ||
} | ||
for _, test := range testCases { | ||
t.Run(test.description, func(t *testing.T) { | ||
//check input payload didn't change | ||
origInPayloadData := test.inPayloadData | ||
activityControl := privacy.NewActivityControl(test.privacyConfig) | ||
newPayload := handleModuleActivities(test.hookCode, activityControl, test.inPayloadData) | ||
assert.Equal(t, test.expectedPayloadData, newPayload) | ||
assert.Equal(t, origInPayloadData, test.inPayloadData) | ||
}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to call this for other endpoints as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooof, yes, I will add this to amp and video.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added to AMP endpoint only.
In Video endpoint hooks are not configured:
HookExecutor: hookexecution.EmptyHookExecutor{},