Skip to content

Commit 370d5eb

Browse files
refactor initialization part of optimizely client and polling manager.
1 parent 2fba187 commit 370d5eb

File tree

6 files changed

+110
-184
lines changed

6 files changed

+110
-184
lines changed

examples/benchmark/main.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,7 @@ func stressTest() {
4444
notificationCenter := notification.NewNotificationCenter()
4545
decisionService := decision.NewCompositeService(notificationCenter)
4646

47-
clientOptions := client.Options{
48-
DecisionService: decisionService,
49-
}
50-
clientApp, err := optlyClient.ClientWithOptions(clientOptions)
47+
clientApp, err := optlyClient.Client(client.DecisionService(decisionService))
5148
if err != nil {
5249
log.Print(err)
5350
}

examples/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ func main() {
6464

6565
notificationCenter := notification.NewNotificationCenter()
6666

67-
app = optimizelyFactory.GetClient(
68-
client.PollingConfigManager("4SLpaJA1r1pgE6T2CoMs9q", time.Second, nil),
67+
app, _ = optimizelyFactory.Client(
68+
client.PollingConfigManager("4SLpaJA1r1pgE6T2CoMs9q", time.Second, nil, notificationCenter),
6969
client.CompositeDecisionService(notificationCenter),
7070
client.BatchEventProcessor(event.DefaultBatchSize, event.DefaultEventQueueSize, event.DefaultEventFlushInterval),
7171
)

optimizely/client/factory.go

Lines changed: 30 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,6 @@ import (
2929
"github.com/optimizely/go-sdk/optimizely/utils"
3030
)
3131

32-
// Options are used to create an instance of the OptimizelyClient with custom configuration
33-
type Options struct {
34-
ProjectConfigManager optimizely.ProjectConfigManager
35-
DecisionService decision.Service
36-
EventProcessor event.Processor
37-
}
38-
3932
// OptimizelyFactory is used to construct an instance of the OptimizelyClient
4033
type OptimizelyFactory struct {
4134
SDKKey string
@@ -45,26 +38,40 @@ type OptimizelyFactory struct {
4538
// OptionFunc is a type to a proper func
4639
type OptionFunc func(*OptimizelyClient, utils.ExecutionCtx)
4740

48-
// GetClient gets client and sets some parameters
49-
func (f OptimizelyFactory) GetClient(clientOptions ...OptionFunc) *OptimizelyClient {
41+
// Client gets client and sets some parameters
42+
func (f OptimizelyFactory) Client(clientOptions ...OptionFunc) (*OptimizelyClient, error) {
43+
5044
executionCtx := utils.NewCancelableExecutionCtx()
45+
notificationCenter := notification.NewNotificationCenter()
46+
5147
appClient := &OptimizelyClient{
52-
executionCtx: executionCtx,
48+
executionCtx: executionCtx,
49+
decisionService: decision.NewCompositeService(notificationCenter),
50+
eventProcessor: event.NewEventProcessor(executionCtx, event.DefaultBatchSize, event.DefaultEventQueueSize, event.DefaultEventFlushInterval),
5351
}
52+
5453
for _, opt := range clientOptions {
5554
opt(appClient, executionCtx)
5655
}
57-
return appClient
56+
57+
if f.SDKKey == "" && f.Datafile == nil && appClient.configManager == nil {
58+
return nil, errors.New("unable to instantiate client: no project config manager, SDK key, or a Datafile provided")
59+
}
60+
61+
if appClient.configManager == nil { // if it was not passed then assign here
62+
63+
appClient.configManager = config.NewPollingProjectConfigManager(executionCtx, f.SDKKey,
64+
config.SetInitialDatafile(f.Datafile), config.SetPollingInterval(config.DefaultPollingInterval), config.SetNotification(notificationCenter))
65+
}
66+
67+
return appClient, nil
5868
}
5969

6070
// PollingConfigManager sets polling config manager on a client
61-
func PollingConfigManager(sdkKey string, pollingInterval time.Duration, dataFile []byte) OptionFunc {
71+
func PollingConfigManager(sdkKey string, pollingInterval time.Duration, initDataFile []byte, notificationCenter notification.Center) OptionFunc {
6272
return func(f *OptimizelyClient, executionCtx utils.ExecutionCtx) {
63-
options := config.PollingProjectConfigManagerOptions{
64-
Datafile: dataFile,
65-
PollingInterval: pollingInterval,
66-
}
67-
f.configManager = config.NewPollingProjectConfigManagerWithOptions(f.executionCtx, sdkKey, options)
73+
f.configManager = config.NewPollingProjectConfigManager(f.executionCtx, sdkKey, config.SetInitialDatafile(initDataFile),
74+
config.SetPollingInterval(pollingInterval), config.SetNotification(notificationCenter))
6875
}
6976
}
7077

@@ -126,57 +133,12 @@ func (f OptimizelyFactory) StaticClient() (*OptimizelyClient, error) {
126133
configManager = staticConfigManager
127134
}
128135

129-
clientOptions := Options{
130-
ProjectConfigManager: configManager,
131-
}
132-
client, err := f.ClientWithOptions(clientOptions)
133-
return client, err
134-
}
135-
136-
// ClientWithOptions returns a client initialized with the given configuration options
137-
func (f OptimizelyFactory) ClientWithOptions(clientOptions Options) (*OptimizelyClient, error) {
138-
139-
executionCtx := utils.NewCancelableExecutionCtx()
140-
client := &OptimizelyClient{
141-
executionCtx: executionCtx,
142-
}
143-
144136
notificationCenter := notification.NewNotificationCenter()
145137

146-
switch {
147-
case clientOptions.ProjectConfigManager != nil:
148-
client.configManager = clientOptions.ProjectConfigManager
149-
case f.SDKKey != "":
150-
options := config.PollingProjectConfigManagerOptions{
151-
Datafile: f.Datafile,
152-
}
153-
client.configManager = config.NewPollingProjectConfigManagerWithOptions(executionCtx, f.SDKKey, options)
154-
case f.Datafile != nil:
155-
staticConfigManager, _ := config.NewStaticProjectConfigManagerFromPayload(f.Datafile)
156-
client.configManager = staticConfigManager
157-
default:
158-
return client, errors.New("unable to instantiate client: no project config manager, SDK key, or a Datafile provided")
159-
}
160-
161-
if clientOptions.DecisionService != nil {
162-
client.decisionService = clientOptions.DecisionService
163-
} else {
164-
client.decisionService = decision.NewCompositeService(notificationCenter)
165-
}
166-
167-
if clientOptions.EventProcessor != nil {
168-
client.eventProcessor = clientOptions.EventProcessor
169-
} else {
170-
client.eventProcessor = event.NewEventProcessor(executionCtx, event.DefaultBatchSize, event.DefaultEventQueueSize, event.DefaultEventFlushInterval)
171-
}
172-
173-
return client, nil
174-
}
175-
176-
// Client returns a client initialized with the defaults
177-
func (f OptimizelyFactory) Client() (*OptimizelyClient, error) {
178-
// Creates a default, canceleable context
179-
clientOptions := Options{}
180-
client, err := f.ClientWithOptions(clientOptions)
181-
return client, err
138+
optlyClient, e := f.Client(
139+
ConfigManager(configManager),
140+
CompositeDecisionService(notificationCenter),
141+
BatchEventProcessor(event.DefaultBatchSize, event.DefaultEventQueueSize, event.DefaultEventFlushInterval),
142+
)
143+
return optlyClient, e
182144
}

optimizely/client/factory_test.go

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/optimizely/go-sdk/optimizely/config"
2424
"github.com/optimizely/go-sdk/optimizely/config/datafileprojectconfig"
2525
"github.com/optimizely/go-sdk/optimizely/event"
26+
2627
"github.com/stretchr/testify/assert"
2728
)
2829

@@ -48,41 +49,23 @@ func TestFactoryClientReturnsDefaultClient(t *testing.T) {
4849
func TestClientWithSDKKey(t *testing.T) {
4950
factory := OptimizelyFactory{SDKKey: "1212"}
5051

51-
clientOptions := Options{}
52-
53-
client, err := factory.ClientWithOptions(clientOptions)
52+
app, err := factory.Client()
5453
assert.NoError(t, err)
55-
assert.NotNil(t, client.configManager)
56-
assert.NotNil(t, client.decisionService)
57-
assert.NotNil(t, client.eventProcessor)
54+
assert.NotNil(t, app.configManager)
55+
assert.NotNil(t, app.decisionService)
56+
assert.NotNil(t, app.eventProcessor)
5857
}
5958

6059
func TestClientWithProjectConfigManagerInOptions(t *testing.T) {
6160
factory := OptimizelyFactory{}
6261
projectConfig := datafileprojectconfig.DatafileProjectConfig{}
6362
configManager := config.NewStaticProjectConfigManager(projectConfig)
6463

65-
clientOptions := Options{ProjectConfigManager: configManager}
66-
67-
client, err := factory.ClientWithOptions(clientOptions)
68-
assert.NoError(t, err)
69-
assert.NotNil(t, client.configManager)
70-
assert.NotNil(t, client.decisionService)
71-
assert.NotNil(t, client.eventProcessor)
72-
}
73-
74-
func TestClientWithNoDecisionServiceAndEventProcessorInOptions(t *testing.T) {
75-
factory := OptimizelyFactory{}
76-
projectConfig := datafileprojectconfig.DatafileProjectConfig{}
77-
configManager := config.NewStaticProjectConfigManager(projectConfig)
78-
79-
clientOptions := Options{ProjectConfigManager: configManager}
80-
81-
client, err := factory.ClientWithOptions(clientOptions)
64+
app, err := factory.Client(ConfigManager(configManager))
8265
assert.NoError(t, err)
83-
assert.NotNil(t, client.configManager)
84-
assert.NotNil(t, client.decisionService)
85-
assert.NotNil(t, client.eventProcessor)
66+
assert.NotNil(t, app.configManager)
67+
assert.NotNil(t, app.decisionService)
68+
assert.NotNil(t, app.eventProcessor)
8669
}
8770

8871
func TestClientWithDecisionServiceAndEventProcessorInOptions(t *testing.T) {
@@ -97,26 +80,8 @@ func TestClientWithDecisionServiceAndEventProcessorInOptions(t *testing.T) {
9780
EventDispatcher: &MockDispatcher{},
9881
}
9982

100-
clientOptions := Options{
101-
ProjectConfigManager: configManager,
102-
DecisionService: decisionService,
103-
EventProcessor: processor,
104-
}
105-
106-
client, err := factory.ClientWithOptions(clientOptions)
83+
app, err := factory.Client(ConfigManager(configManager), DecisionService(decisionService), EventProcessor(processor))
10784
assert.NoError(t, err)
108-
assert.Equal(t, decisionService, client.decisionService)
109-
assert.Equal(t, processor, client.eventProcessor)
110-
}
111-
112-
func TestClientWithOptionsErrorCase(t *testing.T) {
113-
// Error when no config manager, sdk key, or datafile is provided
114-
factory := OptimizelyFactory{}
115-
clientOptions := Options{}
116-
117-
_, err := factory.ClientWithOptions(clientOptions)
118-
expectedErr := errors.New("unable to instantiate client: no project config manager, SDK key, or a Datafile provided")
119-
if assert.Error(t, err) {
120-
assert.Equal(t, err, expectedErr)
121-
}
85+
assert.Equal(t, decisionService, app.decisionService)
86+
assert.Equal(t, processor, app.eventProcessor)
12287
}

optimizely/config/polling_manager.go

Lines changed: 59 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,67 @@ import (
2929
"github.com/optimizely/go-sdk/optimizely/utils"
3030
)
3131

32-
const defaultPollingInterval = 5 * time.Minute // default to 5 minutes for polling
32+
// DefaultPollingInterval sets default interval for polling manager
33+
const DefaultPollingInterval = 5 * time.Minute // default to 5 minutes for polling
3334

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

3738
var cmLogger = logging.GetLogger("PollingConfigManager")
3839

39-
// PollingProjectConfigManagerOptions used to create an instance with custom configuration
40-
type PollingProjectConfigManagerOptions struct {
41-
Datafile []byte
42-
PollingInterval time.Duration
43-
Requester utils.Requester
44-
NotificationCenter notification.Center
45-
}
46-
4740
// PollingProjectConfigManager maintains a dynamic copy of the project config
4841
type PollingProjectConfigManager struct {
4942
requester utils.Requester
5043
pollingInterval time.Duration
51-
projectConfig optimizely.ProjectConfig
52-
configLock sync.RWMutex
53-
err error
5444
notificationCenter notification.Center
45+
initDatafile []byte
46+
47+
configLock sync.RWMutex
48+
err error
49+
projectConfig optimizely.ProjectConfig
50+
exeCtx utils.ExecutionCtx // context used for execution control
51+
}
52+
53+
// OptionFunc is a type to a proper func
54+
type OptionFunc func(*PollingProjectConfigManager)
55+
56+
// SetDefaultRequester is an optional function, sets default requester based on a key.
57+
func SetDefaultRequester(sdkKey string) OptionFunc {
58+
return func(p *PollingProjectConfigManager) {
59+
60+
url := fmt.Sprintf(DatafileURLTemplate, sdkKey)
61+
requester := utils.NewHTTPRequester(url)
5562

56-
exeCtx utils.ExecutionCtx // context used for execution control
63+
p.requester = requester
64+
}
65+
}
66+
67+
// SetRequester is an optional function, sets a passed requester
68+
func SetRequester(requester utils.Requester) OptionFunc {
69+
return func(p *PollingProjectConfigManager) {
70+
p.requester = requester
71+
}
72+
}
73+
74+
// SetPollingInterval is an optional function, sets a passed polling interval
75+
func SetPollingInterval(interval time.Duration) OptionFunc {
76+
return func(p *PollingProjectConfigManager) {
77+
p.pollingInterval = interval
78+
}
79+
}
80+
81+
// SetNotification is an optional function, sets a passed notification
82+
func SetNotification(notificationCenter notification.Center) OptionFunc {
83+
return func(p *PollingProjectConfigManager) {
84+
p.notificationCenter = notificationCenter
85+
}
86+
}
87+
88+
// SetInitialDatafile is an optional function, sets a passed datafile
89+
func SetInitialDatafile(datafile []byte) OptionFunc {
90+
return func(p *PollingProjectConfigManager) {
91+
p.initDatafile = datafile
92+
}
5793
}
5894

5995
// SyncConfig gets current datafile and updates projectConfig
@@ -98,12 +134,7 @@ func (cm *PollingProjectConfigManager) SyncConfig(datafile []byte) {
98134
cm.configLock.Unlock()
99135
}
100136

101-
func (cm *PollingProjectConfigManager) start(initialDatafile []byte, init bool) {
102-
103-
if init {
104-
cm.SyncConfig(initialDatafile)
105-
return
106-
}
137+
func (cm *PollingProjectConfigManager) start() {
107138

108139
t := time.NewTicker(cm.pollingInterval)
109140
for {
@@ -117,38 +148,24 @@ func (cm *PollingProjectConfigManager) start(initialDatafile []byte, init bool)
117148
}
118149
}
119150

120-
// NewPollingProjectConfigManagerWithOptions returns new instance of PollingProjectConfigManager with the given options
121-
func NewPollingProjectConfigManagerWithOptions(exeCtx utils.ExecutionCtx, sdkKey string, options PollingProjectConfigManagerOptions) *PollingProjectConfigManager {
151+
// NewPollingProjectConfigManager returns an instance of the polling config manager with the customized configuration
152+
func NewPollingProjectConfigManager(exeCtx utils.ExecutionCtx, sdkKey string, pollingMangerOptions ...OptionFunc) *PollingProjectConfigManager {
153+
url := fmt.Sprintf(DatafileURLTemplate, sdkKey)
122154

123-
var requester utils.Requester
124-
if options.Requester != nil {
125-
requester = options.Requester
126-
} else {
127-
url := fmt.Sprintf(DatafileURLTemplate, sdkKey)
128-
requester = utils.NewHTTPRequester(url)
129-
}
155+
pollingProjectConfigManager := PollingProjectConfigManager{exeCtx: exeCtx, pollingInterval: DefaultPollingInterval, requester: utils.NewHTTPRequester(url)}
130156

131-
pollingInterval := options.PollingInterval
132-
if pollingInterval == 0 {
133-
pollingInterval = defaultPollingInterval
157+
for _, opt := range pollingMangerOptions {
158+
opt(&pollingProjectConfigManager)
134159
}
135160

136-
pollingProjectConfigManager := PollingProjectConfigManager{requester: requester, pollingInterval: pollingInterval, notificationCenter: options.NotificationCenter, exeCtx: exeCtx}
137-
138-
pollingProjectConfigManager.SyncConfig(options.Datafile) // initial poll
161+
initDatafile := pollingProjectConfigManager.initDatafile
162+
pollingProjectConfigManager.SyncConfig(initDatafile) // initial poll
139163

140164
cmLogger.Debug("Polling Config Manager Initiated")
141-
go pollingProjectConfigManager.start([]byte{}, false)
165+
go pollingProjectConfigManager.start()
142166
return &pollingProjectConfigManager
143167
}
144168

145-
// NewPollingProjectConfigManager returns an instance of the polling config manager with the default configuration
146-
func NewPollingProjectConfigManager(exeCtx utils.ExecutionCtx, sdkKey string) *PollingProjectConfigManager {
147-
options := PollingProjectConfigManagerOptions{}
148-
configManager := NewPollingProjectConfigManagerWithOptions(exeCtx, sdkKey, options)
149-
return configManager
150-
}
151-
152169
// GetConfig returns the project config
153170
func (cm *PollingProjectConfigManager) GetConfig() (optimizely.ProjectConfig, error) {
154171
cm.configLock.RLock()

0 commit comments

Comments
 (0)