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

Analytics activities #3024

Merged
merged 20 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
47 changes: 30 additions & 17 deletions analytics/config/config.go → analytics/build/build.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package config
package build

import (
"github.com/benbjohnson/clock"
Expand All @@ -8,14 +8,15 @@ import (
"github.com/prebid/prebid-server/analytics/filesystem"
"github.com/prebid/prebid-server/analytics/pubstack"
"github.com/prebid/prebid-server/config"
"github.com/prebid/prebid-server/privacy"
)

// Modules that need to be logged to need to be initialized here
func NewPBSAnalytics(analytics *config.Analytics) analytics.PBSAnalyticsModule {
func New(analytics *config.Analytics) analytics.Runner {
modules := make(enabledAnalytics, 0)
if len(analytics.File.Filename) > 0 {
if mod, err := filesystem.NewFileLogger(analytics.File.Filename); err == nil {
modules = append(modules, mod)
modules["filelogger"] = mod
} else {
glog.Fatalf("Could not initialize FileLogger for file %v :%v", analytics.File.Filename, err)
}
Expand All @@ -32,7 +33,7 @@ func NewPBSAnalytics(analytics *config.Analytics) analytics.PBSAnalyticsModule {
analytics.Pubstack.Buffers.Timeout,
clock.New())
if err == nil {
modules = append(modules, pubstackModule)
modules["pubstack"] = pubstackModule
} else {
glog.Errorf("Could not initialize PubstackModule: %v", err)
}
Expand All @@ -41,17 +42,23 @@ func NewPBSAnalytics(analytics *config.Analytics) analytics.PBSAnalyticsModule {
}

// Collection of all the correctly configured analytics modules - implements the PBSAnalyticsModule interface
type enabledAnalytics []analytics.PBSAnalyticsModule
type enabledAnalytics map[string]analytics.Module

func (ea enabledAnalytics) LogAuctionObject(ao *analytics.AuctionObject) {
for _, module := range ea {
module.LogAuctionObject(ao)
func (ea enabledAnalytics) LogAuctionObject(ao *analytics.AuctionObject, ac privacy.ActivityControl) {
for name, module := range ea {
component := privacy.Component{Type: privacy.ComponentTypeAnalytics, Name: name}
if ac.Allow(privacy.ActivityReportAnalytics, component, privacy.ActivityRequest{}) {
module.LogAuctionObject(ao)
}
}
}

func (ea enabledAnalytics) LogVideoObject(vo *analytics.VideoObject) {
for _, module := range ea {
module.LogVideoObject(vo)
func (ea enabledAnalytics) LogVideoObject(vo *analytics.VideoObject, ac privacy.ActivityControl) {
for name, module := range ea {
component := privacy.Component{Type: privacy.ComponentTypeAnalytics, Name: name}
if ac.Allow(privacy.ActivityReportAnalytics, component, privacy.ActivityRequest{}) {
module.LogVideoObject(vo)
}
}
}

Expand All @@ -67,14 +74,20 @@ func (ea enabledAnalytics) LogSetUIDObject(so *analytics.SetUIDObject) {
}
}

func (ea enabledAnalytics) LogAmpObject(ao *analytics.AmpObject) {
for _, module := range ea {
module.LogAmpObject(ao)
func (ea enabledAnalytics) LogAmpObject(ao *analytics.AmpObject, ac privacy.ActivityControl) {
for name, module := range ea {
component := privacy.Component{Type: privacy.ComponentTypeAnalytics, Name: name}
if ac.Allow(privacy.ActivityReportAnalytics, component, privacy.ActivityRequest{}) {
module.LogAmpObject(ao)
}
}
}

func (ea enabledAnalytics) LogNotificationEventObject(ne *analytics.NotificationEvent) {
for _, module := range ea {
module.LogNotificationEventObject(ne)
func (ea enabledAnalytics) LogNotificationEventObject(ne *analytics.NotificationEvent, ac privacy.ActivityControl) {
for name, module := range ea {
component := privacy.Component{Type: privacy.ComponentTypeAnalytics, Name: name}
if ac.Allow(privacy.ActivityReportAnalytics, component, privacy.ActivityRequest{}) {
module.LogNotificationEventObject(ne)
}
}
}
116 changes: 101 additions & 15 deletions analytics/config/config_test.go → analytics/build/build_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package config
package build

import (
"github.com/prebid/prebid-server/analytics"
"net/http"
"os"
"testing"

"github.com/prebid/openrtb/v19/openrtb2"
"github.com/stretchr/testify/assert"

"github.com/prebid/prebid-server/analytics"
"github.com/prebid/prebid-server/config"
"github.com/prebid/prebid-server/privacy"
"github.com/prebid/prebid-server/util/ptrutil"
"github.com/stretchr/testify/assert"
)

const TEST_DIR string = "testFiles"
Expand All @@ -21,7 +22,7 @@ func TestSampleModule(t *testing.T) {
Status: http.StatusOK,
Errors: nil,
Response: &openrtb2.BidResponse{},
})
}, privacy.ActivityControl{})
if count != 1 {
t.Errorf("PBSAnalyticsModule failed at LogAuctionObject")
}
Expand All @@ -42,17 +43,17 @@ func TestSampleModule(t *testing.T) {
t.Errorf("PBSAnalyticsModule failed at LogCookieSyncObject")
}

am.LogAmpObject(&analytics.AmpObject{})
am.LogAmpObject(&analytics.AmpObject{}, privacy.ActivityControl{})
if count != 4 {
t.Errorf("PBSAnalyticsModule failed at LogAmpObject")
}

am.LogVideoObject(&analytics.VideoObject{})
am.LogVideoObject(&analytics.VideoObject{}, privacy.ActivityControl{})
if count != 5 {
t.Errorf("PBSAnalyticsModule failed at LogVideoObject")
}

am.LogNotificationEventObject(&analytics.NotificationEvent{})
am.LogNotificationEventObject(&analytics.NotificationEvent{}, privacy.ActivityControl{})
if count != 6 {
t.Errorf("PBSAnalyticsModule failed at LogNotificationEventObject")
}
Expand All @@ -74,14 +75,14 @@ func (m *sampleModule) LogAmpObject(ao *analytics.AmpObject) { *m.count++ }

func (m *sampleModule) LogNotificationEventObject(ne *analytics.NotificationEvent) { *m.count++ }

func initAnalytics(count *int) analytics.PBSAnalyticsModule {
func initAnalytics(count *int) analytics.Runner {
modules := make(enabledAnalytics, 0)
modules = append(modules, &sampleModule{count})
modules["sampleModule"] = &sampleModule{count}
return &modules
}

func TestNewPBSAnalytics(t *testing.T) {
pbsAnalytics := NewPBSAnalytics(&config.Analytics{})
pbsAnalytics := New(&config.Analytics{})
instance := pbsAnalytics.(enabledAnalytics)

assert.Equal(t, len(instance), 0)
Expand All @@ -94,7 +95,7 @@ func TestNewPBSAnalytics_FileLogger(t *testing.T) {
}
}
defer os.RemoveAll(TEST_DIR)
mod := NewPBSAnalytics(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}})
mod := New(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}})
switch modType := mod.(type) {
case enabledAnalytics:
if len(enabledAnalytics(modType)) != 1 {
Expand All @@ -104,15 +105,15 @@ func TestNewPBSAnalytics_FileLogger(t *testing.T) {
t.Fatalf("Failed to initialize analytics module")
}

pbsAnalytics := NewPBSAnalytics(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}})
pbsAnalytics := New(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}})
instance := pbsAnalytics.(enabledAnalytics)

assert.Equal(t, len(instance), 1)
}

func TestNewPBSAnalytics_Pubstack(t *testing.T) {

pbsAnalyticsWithoutError := NewPBSAnalytics(&config.Analytics{
pbsAnalyticsWithoutError := New(&config.Analytics{
Pubstack: config.Pubstack{
Enabled: true,
ScopeId: "scopeId",
Expand All @@ -129,11 +130,96 @@ func TestNewPBSAnalytics_Pubstack(t *testing.T) {

assert.Equal(t, len(instanceWithoutError), 1)

pbsAnalyticsWithError := NewPBSAnalytics(&config.Analytics{
pbsAnalyticsWithError := New(&config.Analytics{
Pubstack: config.Pubstack{
Enabled: true,
},
})
instanceWithError := pbsAnalyticsWithError.(enabledAnalytics)
assert.Equal(t, len(instanceWithError), 0)
}

func TestSampleModuleActivitiesAllowed(t *testing.T) {
var count int
am := initAnalytics(&count)

acAllowed := privacy.NewActivityControl(getDefaultActivityConfig("sampleModule", true))

ao := &analytics.AuctionObject{
Status: http.StatusOK,
Errors: nil,
Response: &openrtb2.BidResponse{},
}

am.LogAuctionObject(ao, acAllowed)
if count != 1 {
t.Errorf("PBSAnalyticsModule failed at LogAuctionObject")
}

am.LogAmpObject(&analytics.AmpObject{}, acAllowed)
if count != 2 {
t.Errorf("PBSAnalyticsModule failed at LogAmpObject")
}

am.LogVideoObject(&analytics.VideoObject{}, acAllowed)
if count != 3 {
t.Errorf("PBSAnalyticsModule failed at LogVideoObject")
}

am.LogNotificationEventObject(&analytics.NotificationEvent{}, acAllowed)
if count != 4 {
t.Errorf("PBSAnalyticsModule failed at LogNotificationEventObject")
}
}

func TestSampleModuleActivitiesDenied(t *testing.T) {
var count int
am := initAnalytics(&count)

acDenied := privacy.NewActivityControl(getDefaultActivityConfig("sampleModule", false))

ao := &analytics.AuctionObject{
Status: http.StatusOK,
Errors: nil,
Response: &openrtb2.BidResponse{},
}

am.LogAuctionObject(ao, acDenied)
if count != 0 {
t.Errorf("PBSAnalyticsModule failed at LogAuctionObject")
}

am.LogAmpObject(&analytics.AmpObject{}, acDenied)
if count != 0 {
t.Errorf("PBSAnalyticsModule failed at LogAmpObject")
}

am.LogVideoObject(&analytics.VideoObject{}, acDenied)
if count != 0 {
t.Errorf("PBSAnalyticsModule failed at LogVideoObject")
}

am.LogNotificationEventObject(&analytics.NotificationEvent{}, acDenied)
if count != 0 {
t.Errorf("PBSAnalyticsModule failed at LogNotificationEventObject")
}
}

func getDefaultActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
return &config.AccountPrivacy{
AllowActivities: &config.AllowActivities{
ReportAnalytics: config.Activity{
Default: ptrutil.ToPtr(true),
Rules: []config.ActivityRule{
{
Allow: allow,
Condition: config.ActivityCondition{
ComponentName: []string{componentName},
ComponentType: []string{"analytics"},
},
},
},
},
},
}
}
4 changes: 2 additions & 2 deletions analytics/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import (
"github.com/prebid/prebid-server/openrtb_ext"
)

// PBSAnalyticsModule must be implemented by analytics modules to extract the required information and logging
// Module must be implemented by analytics modules to extract the required information and logging
// activities. Do not use marshal the parameter objects directly as they can change over time. Use a separate
// model for each analytics module and transform as appropriate.
type PBSAnalyticsModule interface {
type Module interface {
LogAuctionObject(*AuctionObject)
LogVideoObject(*VideoObject)
LogCookieSyncObject(*CookieSyncObject)
Expand Down
2 changes: 1 addition & 1 deletion analytics/filesystem/file_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (f *FileLogger) LogNotificationEventObject(ne *analytics.NotificationEvent)
}

// Method to initialize the analytic module
func NewFileLogger(filename string) (analytics.PBSAnalyticsModule, error) {
func NewFileLogger(filename string) (analytics.Module, error) {
options := glog.LogOptions{
File: filename,
Flag: glog.LstdFlags,
Expand Down
4 changes: 2 additions & 2 deletions analytics/pubstack/pubstack_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type PubstackModule struct {
clock clock.Clock
}

func NewModule(client *http.Client, scope, endpoint, configRefreshDelay string, maxEventCount int, maxByteSize, maxTime string, clock clock.Clock) (analytics.PBSAnalyticsModule, error) {
func NewModule(client *http.Client, scope, endpoint, configRefreshDelay string, maxEventCount int, maxByteSize, maxTime string, clock clock.Clock) (analytics.Module, error) {
configUpdateTask, err := NewConfigUpdateHttpTask(
client,
scope,
Expand All @@ -63,7 +63,7 @@ func NewModule(client *http.Client, scope, endpoint, configRefreshDelay string,
return NewModuleWithConfigTask(client, scope, endpoint, maxEventCount, maxByteSize, maxTime, configUpdateTask, clock)
}

func NewModuleWithConfigTask(client *http.Client, scope, endpoint string, maxEventCount int, maxByteSize, maxTime string, configTask ConfigUpdateTask, clock clock.Clock) (analytics.PBSAnalyticsModule, error) {
func NewModuleWithConfigTask(client *http.Client, scope, endpoint string, maxEventCount int, maxByteSize, maxTime string, configTask ConfigUpdateTask, clock clock.Clock) (analytics.Module, error) {
glog.Infof("[pubstack] Initializing module scope=%s endpoint=%s\n", scope, endpoint)

// parse args
Expand Down
14 changes: 7 additions & 7 deletions analytics/pubstack/pubstack_module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,47 +50,47 @@ func TestNewModuleSuccess(t *testing.T) {
tests := []struct {
description string
feature string
logObject func(analytics.PBSAnalyticsModule)
logObject func(analytics.Module)
}{
{
description: "auction events are only published when logging an auction object with auction feature on",
feature: auction,
logObject: func(module analytics.PBSAnalyticsModule) {
logObject: func(module analytics.Module) {
module.LogAuctionObject(&analytics.AuctionObject{Status: http.StatusOK})
},
},
{
description: "AMP events are only published when logging an AMP object with AMP feature on",
feature: amp,
logObject: func(module analytics.PBSAnalyticsModule) {
logObject: func(module analytics.Module) {
module.LogAmpObject(&analytics.AmpObject{Status: http.StatusOK})
},
},
{
description: "video events are only published when logging a video object with video feature on",
feature: video,
logObject: func(module analytics.PBSAnalyticsModule) {
logObject: func(module analytics.Module) {
module.LogVideoObject(&analytics.VideoObject{Status: http.StatusOK})
},
},
{
description: "cookie events are only published when logging a cookie object with cookie feature on",
feature: cookieSync,
logObject: func(module analytics.PBSAnalyticsModule) {
logObject: func(module analytics.Module) {
module.LogCookieSyncObject(&analytics.CookieSyncObject{Status: http.StatusOK})
},
},
{
description: "setUID events are only published when logging a setUID object with setUID feature on",
feature: setUID,
logObject: func(module analytics.PBSAnalyticsModule) {
logObject: func(module analytics.Module) {
module.LogSetUIDObject(&analytics.SetUIDObject{Status: http.StatusOK})
},
},
{
description: "Ignore excluded fields from marshal",
feature: auction,
logObject: func(module analytics.PBSAnalyticsModule) {
logObject: func(module analytics.Module) {
module.LogAuctionObject(&analytics.AuctionObject{
RequestWrapper: &openrtb_ext.RequestWrapper{},
SeatNonBid: []openrtb_ext.SeatNonBid{
Expand Down
14 changes: 14 additions & 0 deletions analytics/runner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package analytics

import (
"github.com/prebid/prebid-server/privacy"
)

type Runner interface {
LogAuctionObject(*AuctionObject, privacy.ActivityControl)
LogVideoObject(*VideoObject, privacy.ActivityControl)
LogCookieSyncObject(*CookieSyncObject)
LogSetUIDObject(*SetUIDObject)
LogAmpObject(*AmpObject, privacy.ActivityControl)
LogNotificationEventObject(*NotificationEvent, privacy.ActivityControl)
}
Loading