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

UOE-6719: Added scheduler for fetching gdpr vendor-list files #211

Merged
merged 34 commits into from
Sep 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1dcf910
OTT-105: VCR - Video Event Trackers (#152)
ShriprasadM May 5, 2021
c360511
OTT-172: Set default min ads to 1 from 2 (#153)
Pubmatic-Dhruv-Sonone May 11, 2021
d7f2b67
Revert "OTT-172: Set default min ads to 1 from 2 (#153)" (#154)
PubMatic-OpenWrap May 12, 2021
4105725
UOE-6319: OpenWrap S2S: Prebid Server Version Update to 0.157.0 (#146)
sachin-pubmatic May 14, 2021
fa6d819
UOE-6319: Upgraded prebid-server to 0.157.0 (#156)
sachin-pubmatic May 14, 2021
2037771
OTT-172: Set Default min Ads value to 1 from 2 (#161)
Pubmatic-Dhruv-Sonone May 25, 2021
80d3d40
UOE-6444:updating unruly URLs (#159)
PubMatic-OpenWrap May 25, 2021
a8aed1b
UOE-6240: Openwrap S2S: Send gpt slot name in extension field (#162)
sachin-pubmatic May 25, 2021
9860ee0
OTT-192: Ensure sURL (Event Tracker) and orig (OW Logger) parameter v…
Pubmatic-Dhruv-Sonone Jun 2, 2021
ca9b117
OTT-197: Log Partner bidder code in video event tracker (#168)
Pubmatic-Dhruv-Sonone Jun 15, 2021
7c2e2d2
OTT-223 Adding Client Configurations
pm-viral-vala Jul 6, 2021
a8fb2e7
UOE-6646: Added label adapter_name for tls_handshake_time stat
sachin-pubmatic Jul 9, 2021
b420a11
Fixed typo
sachin-pubmatic Jul 9, 2021
62934aa
OTT-227 Fixing Panic Issue for Prebid Adapter (#176)
pm-viral-vala Jul 9, 2021
c16692e
Merge pull request #177 from PubMatic-OpenWrap/UOE-6646
pm-isha-bharti Jul 14, 2021
6c8508d
OTT-216: add all SupportDeal features (#183)
Pubmatic-Dhruv-Sonone Jul 27, 2021
4ec85b0
UOE-6525: In-app OTT Add Support for dctr and pmzoneid (#170)
sachin-pubmatic Jul 27, 2021
721dfc1
UOE-6534: OW Prebid-server: Use fallback mechanism for get slot name …
sachin-pubmatic Jul 27, 2021
3c564c7
OTT-48 VAST Bidder Phase 1 (#186)
pm-viral-vala Jul 27, 2021
b594df4
UOE-6610: Upgrade prebid-server to 0.170.0 (#190)
sachin-pubmatic Aug 17, 2021
b14bfcd
UOE-6774: Fixed Spotx GDPR issue (#195)
sachin-pubmatic Aug 17, 2021
1ef324d
Handled NPE in interstitial.go (#196)
sachin-pubmatic Aug 18, 2021
2d39ba3
OTT-217 - Remove loggers for filtered VAST Tags
PubMatic-OpenWrap Aug 31, 2021
7053237
UOE-6744: Added code missed in previous prebid-server upgrade (#200)
sachin-pubmatic Aug 31, 2021
cf8805c
UOE-6853: Update ExtCTVBid to include skadn
pm-isha-bharti Sep 13, 2021
5111eb1
UOE-6853: Renaming ExtCTVBid to ExtOWBid
pm-isha-bharti Sep 13, 2021
a2763f2
Merge pull request #201 from PubMatic-OpenWrap/UOE-6853
pm-isha-bharti Sep 15, 2021
43b271a
Run validate.sh to format files & fixed unit test for bidderparams (#…
sachin-pubmatic Sep 15, 2021
23c6c51
Added vendor list file fetching scheduler
sachin-pubmatic Sep 28, 2021
aa876e2
Refactored code
sachin-pubmatic Sep 29, 2021
a1378f9
Added print statements
sachin-pubmatic Sep 29, 2021
a2feafd
Added unit test for vendorlist-scheduler
sachin-pubmatic Sep 29, 2021
682b57b
Merged UOE-6719
sachin-pubmatic Sep 30, 2021
a962ce4
Fixed formatting
sachin-pubmatic Sep 30, 2021
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
8 changes: 8 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ type Configuration struct {
//When true, new bid id will be generated in seatbid[].bid[].ext.prebid.bidid and used in event urls instead
GenerateBidID bool `mapstructure:"generate_bid_id"`
TrackerURL string `mapstructure:"tracker_url"`

VendorListScheduler VendorListScheduler `mapstructure:"vendor_list_scheduler"`
}

type VendorListScheduler struct {
Enabled bool `mapstructure:"enabled"`
Interval string `mapstructure:"interval"`
Timeout string `mapstructure:"timeout"`
}

const MIN_COOKIE_SIZE_BYTES = 500
Expand Down
5 changes: 4 additions & 1 deletion gdpr/vendorlist-fetching.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ import (

type saveVendors func(uint16, api.VendorList)

var cacheSave func(vendorListVersion uint16, list api.VendorList)
var cacheLoad func(vendorListVersion uint16) api.VendorList

// This file provides the vendorlist-fetching function for Prebid Server.
//
// For more info, see https://github.com/prebid/prebid-server/issues/504
//
// Nothing in this file is exported. Public APIs can be found in gdpr.go

func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http.Client, urlMaker func(uint16) string) func(ctx context.Context, id uint16) (vendorlist.VendorList, error) {
cacheSave, cacheLoad := newVendorListCache()
cacheSave, cacheLoad = newVendorListCache()

preloadContext, cancel := context.WithTimeout(initCtx, cfg.Timeouts.InitTimeout())
defer cancel()
Expand Down
119 changes: 119 additions & 0 deletions gdpr/vendorlist-scheduler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package gdpr

import (
"context"
"errors"
"github.com/golang/glog"
"net/http"
"sync"
"time"
)

type vendorListScheduler struct {
ticker *time.Ticker
interval time.Duration
done chan bool
isRunning bool
isStarted bool
lastRun time.Time

httpClient *http.Client
timeout time.Duration
}

//Only single instance must be created
var _instance *vendorListScheduler
var once sync.Once

func GetVendorListScheduler(interval, timeout string, httpClient *http.Client) (*vendorListScheduler, error) {
if _instance != nil {
return _instance, nil
}

intervalDuration, err := time.ParseDuration(interval)
if err != nil {
return nil, errors.New("error parsing vendor list scheduler interval: " + err.Error())
}

timeoutDuration, err := time.ParseDuration(timeout)
if err != nil {
return nil, errors.New("error parsing vendor list scheduler timeout: " + err.Error())
}

if httpClient == nil {
return nil, errors.New("http-client can not be nil")
}

once.Do(func() {
_instance = &vendorListScheduler{
ticker: nil,
interval: intervalDuration,
done: make(chan bool),
httpClient: httpClient,
timeout: timeoutDuration,
}
})

return _instance, nil
}

func (scheduler *vendorListScheduler) Start() {
if scheduler == nil || scheduler.isStarted {
return
}

scheduler.ticker = time.NewTicker(scheduler.interval)
scheduler.isStarted = true
go func() {
for {
select {
case <-scheduler.done:
scheduler.isRunning = false
scheduler.isStarted = false
scheduler.ticker = nil
return
case t := <-scheduler.ticker.C:
if !scheduler.isRunning {
scheduler.isRunning = true

glog.Info("Running vendor list scheduler at ", t)
scheduler.runLoadCache()

scheduler.lastRun = t
scheduler.isRunning = false
}
}
}
}()
}

func (scheduler *vendorListScheduler) Stop() {
if scheduler == nil || !scheduler.isStarted {
return
}
scheduler.ticker.Stop()
scheduler.done <- true
}

func (scheduler *vendorListScheduler) runLoadCache() {
if scheduler == nil {
return
}

preloadContext, cancel := context.WithTimeout(context.Background(), scheduler.timeout)
defer cancel()

latestVersion := saveOne(preloadContext, scheduler.httpClient, vendorListURLMaker(0), cacheSave)

// The GVL for TCF2 has no vendors defined in its first version. It's very unlikely to be used, so don't preload it.
firstVersionToLoad := uint16(2)

for i := latestVersion; i >= firstVersionToLoad; i-- {
// Check if version is present in the cache
if list := cacheLoad(i); list != nil {
continue
}
glog.Infof("Downloading: " + vendorListURLMaker(i))
saveOne(preloadContext, scheduler.httpClient, vendorListURLMaker(i), cacheSave)
}
}
175 changes: 175 additions & 0 deletions gdpr/vendorlist-scheduler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package gdpr

import (
"context"
"github.com/prebid/go-gdpr/api"
"github.com/stretchr/testify/assert"
"net/http"
"testing"
"time"
)

func TestGetVendorListScheduler(t *testing.T) {
type args struct {
interval string
timeout string
httpClient *http.Client
}
tests := []struct {
name string
args args
want *vendorListScheduler
wantErr bool
}{
{
name: "Test singleton",
args: args{
interval: "1m",
timeout: "1s",
httpClient: http.DefaultClient,
},
want: GetExpectedVendorListScheduler("1m", "1s", http.DefaultClient),
wantErr: false,
},
{
name: "Test singleton again",
args: args{
interval: "2m",
timeout: "2s",
httpClient: http.DefaultClient,
},
want: GetExpectedVendorListScheduler("2m", "2s", http.DefaultClient),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
//Mark instance as nil for recreating new instance
if tt.want == nil {
//_instance = nil
}

got, err := GetVendorListScheduler(tt.args.interval, tt.args.timeout, tt.args.httpClient)
if got != tt.want {
t.Errorf("GetVendorListScheduler() got = %v, want %v", got, tt.want)
}
if (err != nil) != tt.wantErr {
t.Errorf("GetVendorListScheduler() error = %v, wantErr %v", err, tt.wantErr)
return
}
})
}
}

func GetExpectedVendorListScheduler(interval string, timeout string, httpClient *http.Client) *vendorListScheduler {
s, _ := GetVendorListScheduler(interval, timeout, httpClient)
return s
}

func Test_vendorListScheduler_Start(t *testing.T) {
type fields struct {
scheduler *vendorListScheduler
}
tests := []struct {
name string
fields fields
}{
{
name: "Start test",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
scheduler, err := GetVendorListScheduler("1m", "30s", http.DefaultClient)
assert.Nil(t, err, "error should be nil")
assert.NotNil(t, scheduler, "scheduler instance should not be nil")

scheduler.Start()

assert.NotNil(t, scheduler.ticker, "ticker should not be nil")
assert.True(t, scheduler.isStarted, "isStarted should be true")

scheduler.Stop()
})
}
}

func Test_vendorListScheduler_Stop(t *testing.T) {
type fields struct {
scheduler *vendorListScheduler
}
tests := []struct {
name string
fields fields
}{
{
name: "Stop test",
},
{
name: "Calling stop again",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
scheduler, err := GetVendorListScheduler("1m", "30s", http.DefaultClient)
assert.Nil(t, err, "error should be nil")
assert.NotNil(t, scheduler, "scheduler instance should not be nil")

scheduler.Start()
scheduler.Stop()

assert.Nil(t, scheduler.ticker, "ticker should not be nil")
assert.False(t, scheduler.isStarted, "isStarted should be true")
})
}
}

func Test_vendorListScheduler_runLoadCache(t *testing.T) {
type fields struct {
scheduler *vendorListScheduler
}
tests := []struct {
name string
fields fields
}{
{
name: "runLoadCache caches all files",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var err error
tt.fields.scheduler, err = GetVendorListScheduler("5m", "5m", http.DefaultClient)
assert.Nil(t, err, "error should be nil")
assert.False(t, tt.fields.scheduler.isStarted, "VendorListScheduler should not be already running")

tt.fields.scheduler.timeout = 2 * time.Minute

mockCacheSave := func(uint16, api.VendorList) {}
latestVersion := saveOne(context.Background(), http.DefaultClient, vendorListURLMaker(0), mockCacheSave)

cacheSave, cacheLoad = newVendorListCache()
tt.fields.scheduler.runLoadCache()

firstVersionToLoad := uint16(2)
for i := latestVersion; i >= firstVersionToLoad; i-- {
list := cacheLoad(i)
assert.NotNil(t, list, "vendor-list file should be present in cache")
}
})
}
}

func Benchmark_vendorListScheduler_runLoadCache(b *testing.B) {
scheduler, err := GetVendorListScheduler("1m", "30m", http.DefaultClient)
assert.Nil(b, err, "")
assert.NotNil(b, scheduler, "")

scheduler.timeout = 2 * time.Minute

for n := 0; n < b.N; n++ {
cacheSave, cacheLoad = newVendorListCache()
scheduler.runLoadCache()
}

}
8 changes: 8 additions & 0 deletions router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,14 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R
gvlVendorIDs := bidderInfos.ToGVLVendorIDMap()
g_gdprPerms = gdpr.NewPermissions(context.Background(), cfg.GDPR, gvlVendorIDs, generalHttpClient)

if cfg.VendorListScheduler.Enabled {
vendorListScheduler, err := gdpr.GetVendorListScheduler(cfg.VendorListScheduler.Interval, cfg.VendorListScheduler.Timeout, generalHttpClient)
if err != nil {
glog.Fatal(err)
}
vendorListScheduler.Start()
}

exchanges = newExchangeMap(cfg)
g_cacheClient = pbc.NewClient(cacheHttpClient, &cfg.CacheURL, &cfg.ExtCacheURL, g_metrics)

Expand Down