Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion pkg/client/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func (f OptimizelyFactory) Client(clientOptions ...OptionFunc) (*OptimizelyClien

// Initialize the default services with the execution context
if pollingConfigManager, ok := appClient.ConfigManager.(*config.PollingProjectConfigManager); ok {
pollingConfigManager.Start(appClient.executionCtx)
pollingConfigManager.Start(f.SDKKey, appClient.executionCtx)
}

if batchProcessor, ok := appClient.EventProcessor.(*event.BatchEventProcessor); ok {
Expand Down
13 changes: 1 addition & 12 deletions pkg/client/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ package client

import (
"errors"
"fmt"
"log"
"net/http"
"net/http/httptest"
"testing"
"time"

Expand Down Expand Up @@ -75,15 +71,8 @@ func TestClientWithPollingConfigManager(t *testing.T) {

func TestClientWithPollingConfigManagerRequester(t *testing.T) {

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Print(">request: ", r)
if r.URL.String() == "/good" {
fmt.Fprintln(w, "Hello, client")
}
}))

factory := OptimizelyFactory{}
requester := utils.NewHTTPRequester(ts.URL + "/good")
requester := utils.NewHTTPRequester()
optimizelyClient, err := factory.Client(WithPollingConfigManagerRequester(requester, time.Minute, nil))
assert.NoError(t, err)
assert.NotNil(t, optimizelyClient.ConfigManager)
Expand Down
28 changes: 11 additions & 17 deletions pkg/config/polling_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ import (
// DefaultPollingInterval sets default interval for polling manager
const DefaultPollingInterval = 5 * time.Minute // default to 5 minutes for polling

// DatafileURLTemplate is used to construct the endpoint for retrieving the datafile from the CDN
const DatafileURLTemplate = "https://cdn.optimizely.com/datafiles/%s.json"

// ModifiedSince header key for request
const ModifiedSince = "If-Modified-Since"

Expand All @@ -61,13 +58,11 @@ type PollingProjectConfigManager struct {
// OptionFunc is a type to a proper func
type OptionFunc func(*PollingProjectConfigManager)

// DefaultRequester is an optional function, sets default requester based on a key.
func DefaultRequester(sdkKey string) OptionFunc {
// DefaultRequester is an optional function, sets default requester
func DefaultRequester() OptionFunc {
return func(p *PollingProjectConfigManager) {

url := fmt.Sprintf(DatafileURLTemplate, sdkKey)
requester := utils.NewHTTPRequester(url)

requester := utils.NewHTTPRequester()
p.requester = requester
}
}
Expand All @@ -94,7 +89,7 @@ func InitialDatafile(datafile []byte) OptionFunc {
}

// SyncConfig gets current datafile and updates projectConfig
func (cm *PollingProjectConfigManager) SyncConfig(datafile []byte) {
func (cm *PollingProjectConfigManager) SyncConfig(sdkKey string, datafile []byte) {
var e error
var code int
var respHeaders http.Header
Expand All @@ -103,13 +98,13 @@ func (cm *PollingProjectConfigManager) SyncConfig(datafile []byte) {
cm.err = e
cm.configLock.Unlock()
}

uri := "/" + sdkKey + ".json"
if len(datafile) == 0 {
if cm.lastModified != "" {
lastModifiedHeader := utils.Header{Name: ModifiedSince, Value: cm.lastModified}
datafile, respHeaders, code, e = cm.requester.Get(lastModifiedHeader)
datafile, respHeaders, code, e = cm.requester.Get(uri, lastModifiedHeader)
} else {
datafile, respHeaders, code, e = cm.requester.Get()
datafile, respHeaders, code, e = cm.requester.Get(uri)
}

if e != nil {
Expand Down Expand Up @@ -168,14 +163,14 @@ func (cm *PollingProjectConfigManager) SyncConfig(datafile []byte) {
}

// Start starts the polling
func (cm *PollingProjectConfigManager) Start(exeCtx utils.ExecutionCtx) {
func (cm *PollingProjectConfigManager) Start(sdkKey string, exeCtx utils.ExecutionCtx) {
go func() {
cmLogger.Debug("Polling Config Manager Initiated")
t := time.NewTicker(cm.pollingInterval)
for {
select {
case <-t.C:
cm.SyncConfig([]byte{})
cm.SyncConfig(sdkKey, []byte{})
case <-exeCtx.GetContext().Done():
cmLogger.Debug("Polling Config Manager Stopped")
return
Expand All @@ -186,20 +181,19 @@ func (cm *PollingProjectConfigManager) Start(exeCtx utils.ExecutionCtx) {

// NewPollingProjectConfigManager returns an instance of the polling config manager with the customized configuration
func NewPollingProjectConfigManager(sdkKey string, pollingMangerOptions ...OptionFunc) *PollingProjectConfigManager {
url := fmt.Sprintf(DatafileURLTemplate, sdkKey)

pollingProjectConfigManager := PollingProjectConfigManager{
notificationCenter: registry.GetNotificationCenter(sdkKey),
pollingInterval: DefaultPollingInterval,
requester: utils.NewHTTPRequester(url),
requester: utils.NewHTTPRequester(),
}

for _, opt := range pollingMangerOptions {
opt(&pollingProjectConfigManager)
}

initDatafile := pollingProjectConfigManager.initDatafile
pollingProjectConfigManager.SyncConfig(initDatafile) // initial poll
pollingProjectConfigManager.SyncConfig(sdkKey, initDatafile) // initial poll
return &pollingProjectConfigManager
}

Expand Down
42 changes: 20 additions & 22 deletions pkg/config/polling_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type MockRequester struct {
mock.Mock
}

func (m *MockRequester) Get(headers ...utils.Header) (response []byte, responseHeaders http.Header, code int, err error) {
func (m *MockRequester) Get(uri string, headers ...utils.Header) (response []byte, responseHeaders http.Header, code int, err error) {
args := m.Called(headers)
return args.Get(0).([]byte), args.Get(1).(http.Header), args.Int(2), args.Error(3)
}
Expand All @@ -51,7 +51,7 @@ func TestNewPollingProjectConfigManagerWithOptions(t *testing.T) {

exeCtx := utils.NewCancelableExecutionCtx()
configManager := NewPollingProjectConfigManager(sdkKey, Requester(mockRequester))
configManager.Start(exeCtx)
configManager.Start(sdkKey, exeCtx)
mockRequester.AssertExpectations(t)

actual, err := configManager.GetConfig()
Expand All @@ -72,7 +72,7 @@ func TestNewPollingProjectConfigManagerWithNull(t *testing.T) {

exeCtx := utils.NewCancelableExecutionCtx()
configManager := NewPollingProjectConfigManager(sdkKey, Requester(mockRequester))
configManager.Start(exeCtx)
configManager.Start(sdkKey, exeCtx)
mockRequester.AssertExpectations(t)

_, err := configManager.GetConfig()
Expand All @@ -90,15 +90,15 @@ func TestNewPollingProjectConfigManagerWithSimilarDatafileRevisions(t *testing.T

exeCtx := utils.NewCancelableExecutionCtx()
configManager := NewPollingProjectConfigManager(sdkKey, Requester(mockRequester))
configManager.Start(exeCtx)
configManager.Start(sdkKey, exeCtx)
mockRequester.AssertExpectations(t)

actual, err := configManager.GetConfig()
assert.Nil(t, err)
assert.NotNil(t, actual)
assert.Equal(t, projectConfig1, actual)

configManager.SyncConfig(mockDatafile2)
configManager.SyncConfig(sdkKey, mockDatafile2)
actual, err = configManager.GetConfig()
assert.Equal(t, projectConfig1, actual)
}
Expand All @@ -118,7 +118,7 @@ func TestNewPollingProjectConfigManagerWithLastModifiedDates(t *testing.T) {

exeCtx := utils.NewCancelableExecutionCtx()
configManager := NewPollingProjectConfigManager(sdkKey, Requester(mockRequester))
configManager.Start(exeCtx)
configManager.Start(sdkKey, exeCtx)

// Fetch valid config
actual, err := configManager.GetConfig()
Expand All @@ -127,7 +127,7 @@ func TestNewPollingProjectConfigManagerWithLastModifiedDates(t *testing.T) {
assert.Equal(t, projectConfig1, actual)

// Sync and check no changes were made to the previous config because of 304 error code
configManager.SyncConfig([]byte{})
configManager.SyncConfig(sdkKey, []byte{})
actual, err = configManager.GetConfig()
assert.Nil(t, err)
assert.NotNil(t, actual)
Expand All @@ -148,15 +148,15 @@ func TestNewPollingProjectConfigManagerWithDifferentDatafileRevisions(t *testing

exeCtx := utils.NewCancelableExecutionCtx()
configManager := NewPollingProjectConfigManager(sdkKey, Requester(mockRequester))
configManager.Start(exeCtx)
configManager.Start(sdkKey, exeCtx)
mockRequester.AssertExpectations(t)

actual, err := configManager.GetConfig()
assert.Nil(t, err)
assert.NotNil(t, actual)
assert.Equal(t, projectConfig1, actual)

configManager.SyncConfig(mockDatafile2)
configManager.SyncConfig(sdkKey, mockDatafile2)
actual, err = configManager.GetConfig()
assert.Equal(t, projectConfig2, actual)
}
Expand All @@ -175,20 +175,20 @@ func TestNewPollingProjectConfigManagerWithErrorHandling(t *testing.T) {

exeCtx := utils.NewCancelableExecutionCtx()
configManager := NewPollingProjectConfigManager(sdkKey, Requester(mockRequester))
configManager.Start(exeCtx)
configManager.Start(sdkKey, exeCtx)
mockRequester.AssertExpectations(t)

actual, err := configManager.GetConfig() // polling for bad file
assert.NotNil(t, err)
assert.Nil(t, actual)
assert.Nil(t, projectConfig1)

configManager.SyncConfig(mockDatafile2) // polling for good file
configManager.SyncConfig(sdkKey, mockDatafile2) // polling for good file
actual, err = configManager.GetConfig()
assert.Nil(t, err)
assert.Equal(t, projectConfig2, actual)

configManager.SyncConfig(mockDatafile1) // polling for bad file, error not null but good project
configManager.SyncConfig(sdkKey, mockDatafile1) // polling for bad file, error not null but good project
actual, err = configManager.GetConfig()
assert.Nil(t, err)
assert.Equal(t, projectConfig2, actual)
Expand All @@ -206,7 +206,7 @@ func TestNewPollingProjectConfigManagerOnDecision(t *testing.T) {

exeCtx := utils.NewCancelableExecutionCtx()
configManager := NewPollingProjectConfigManager(sdkKey, Requester(mockRequester))
configManager.Start(exeCtx)
configManager.Start(sdkKey, exeCtx)

var numberOfCalls = 0
callback := func(notification notification.ProjectConfigUpdateNotification) {
Expand All @@ -219,7 +219,7 @@ func TestNewPollingProjectConfigManagerOnDecision(t *testing.T) {
assert.Nil(t, err)
assert.NotNil(t, actual)

configManager.SyncConfig(mockDatafile2)
configManager.SyncConfig(sdkKey, mockDatafile2)
actual, err = configManager.GetConfig()
assert.Nil(t, err)
assert.NotNil(t, actual)
Expand All @@ -237,34 +237,32 @@ func TestNewPollingProjectConfigManagerOnDecision(t *testing.T) {
func TestDefaultRequester(t *testing.T) {

sdkKey := "test_sdk_key"
DefaultRequester(sdkKey)
exeCtx := utils.NewCancelableExecutionCtx()
configManager := NewPollingProjectConfigManager(sdkKey, DefaultRequester(sdkKey))
configManager.Start(exeCtx)
configManager := NewPollingProjectConfigManager(sdkKey, DefaultRequester())
configManager.Start(sdkKey, exeCtx)

requester := configManager.requester
assert.NotNil(t, requester)
assert.Equal(t, requester.String(), "{url: https://cdn.optimizely.com/datafiles/test_sdk_key.json, timeout: 5s, retries: 1}")
assert.Equal(t, requester.String(), "{api: https://cdn.optimizely.com/datafiles, timeout: 5s, retries: 1}")
}

func TestPollingInterval(t *testing.T) {

sdkKey := "test_sdk_key"
DefaultRequester(sdkKey)

exeCtx := utils.NewCancelableExecutionCtx()
configManager := NewPollingProjectConfigManager(sdkKey, PollingInterval(5*time.Second))
configManager.Start(exeCtx)
configManager.Start(sdkKey, exeCtx)

assert.Equal(t, configManager.pollingInterval, 5*time.Second)
}

func TestInitialDatafile(t *testing.T) {

sdkKey := "test_sdk_key"
DefaultRequester(sdkKey)
exeCtx := utils.NewCancelableExecutionCtx()
configManager := NewPollingProjectConfigManager(sdkKey, InitialDatafile([]byte("test")))
configManager.Start(exeCtx)
configManager.Start(sdkKey, exeCtx)

assert.Equal(t, configManager.initDatafile, []byte("test"))
}
6 changes: 4 additions & 2 deletions pkg/config/static_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ type StaticProjectConfigManager struct {
// NewStaticProjectConfigManagerFromURL returns new instance of StaticProjectConfigManager for URL
func NewStaticProjectConfigManagerFromURL(sdkKey string) (*StaticProjectConfigManager, error) {

requester := utils.NewHTTPRequester(fmt.Sprintf(DatafileURLTemplate, sdkKey))
datafile, _, code, e := requester.Get()
requester := utils.NewHTTPRequester()

uri := "/" + sdkKey + ".json"
datafile, _, code, e := requester.Get(uri)
if e != nil {
cmLogger.Error(fmt.Sprintf("request returned with http code=%d", code), e)
return nil, e
Expand Down
4 changes: 2 additions & 2 deletions pkg/event/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ type HTTPEventDispatcher struct {
// DispatchEvent dispatches event with callback
func (*HTTPEventDispatcher) DispatchEvent(event LogEvent) (bool, error) {

requester := utils.NewHTTPRequester(event.EndPoint)
_, _, code, err := requester.Post(event.Event)
requester := utils.NewHTTPRequester(utils.API(eventAPI))
_, _, code, err := requester.Post(eventURI, event.Event)

// also check response codes
// resp.StatusCode == 400 is an error
Expand Down
5 changes: 3 additions & 2 deletions pkg/event/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ const clientVersion string = pkg.Version
const attributeType = "custom"
const specialPrefix = "$opt_"
const botFilteringKey = "$opt_bot_filtering"
const eventEndPoint = "https://logx.optimizely.com/v1/events"
const eventAPI = "https://logx.optimizely.com/v1"
const eventURI = "/events"
const revenueKey = "revenue"
const valueKey = "value"

func createLogEvent(event Batch) LogEvent {
return LogEvent{EndPoint: eventEndPoint, Event: event}
return LogEvent{EndPoint: eventAPI + eventURI, Event: event}
}

func makeTimestamp() int64 {
Expand Down
Loading