Skip to content

Commit 23f6de1

Browse files
yasirfolio3Michael Ng
authored andcommitted
feat(factory-ep): Added EventProcessor to factory and made fixes to run FSC feature-management tests. (#85)
1 parent c2f5a9b commit 23f6de1

File tree

12 files changed

+146
-28
lines changed

12 files changed

+146
-28
lines changed

optimizely/client/factory.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ package client
2020
import (
2121
"context"
2222
"errors"
23-
"time"
2423

2524
"github.com/optimizely/go-sdk/optimizely/event"
2625

@@ -36,6 +35,7 @@ type Options struct {
3635
Context context.Context
3736
ProjectConfigManager optimizely.ProjectConfigManager
3837
DecisionService decision.Service
38+
EventProcessor event.Processor
3939
}
4040

4141
// OptimizelyFactory is used to construct an instance of the OptimizelyClient
@@ -44,9 +44,6 @@ type OptimizelyFactory struct {
4444
Datafile []byte
4545
}
4646

47-
const defaultEventQueueSize = 10
48-
const defaultEventFlushInterval = 30 * time.Second
49-
5047
// StaticClient returns a client initialized with a static project config
5148
func (f OptimizelyFactory) StaticClient() (*OptimizelyClient, error) {
5249
var configManager optimizely.ProjectConfigManager
@@ -117,8 +114,12 @@ func (f OptimizelyFactory) ClientWithOptions(clientOptions Options) (*Optimizely
117114
client.decisionService = decision.NewCompositeService(notificationCenter)
118115
}
119116

120-
// @TODO: allow event processor to be passed in
121-
client.eventProcessor = event.NewEventProcessor(ctx, defaultEventQueueSize, defaultEventFlushInterval)
117+
if clientOptions.EventProcessor != nil {
118+
client.eventProcessor = clientOptions.EventProcessor
119+
} else {
120+
client.eventProcessor = event.NewEventProcessor(ctx, event.DefaultBatchSize, event.DefaultEventQueueSize, event.DefaultEventFlushInterval)
121+
}
122+
122123
client.isValid = true
123124
return client, nil
124125
}

optimizely/client/factory_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,100 @@ import (
2020
"errors"
2121
"testing"
2222

23+
"github.com/optimizely/go-sdk/optimizely/config"
24+
"github.com/optimizely/go-sdk/optimizely/config/datafileprojectconfig"
25+
"github.com/optimizely/go-sdk/optimizely/event"
2326
"github.com/stretchr/testify/assert"
2427
)
2528

29+
type MockDispatcher struct {
30+
Events []event.LogEvent
31+
}
32+
33+
func (f *MockDispatcher) DispatchEvent(event event.LogEvent, callback func(success bool)) {
34+
f.Events = append(f.Events, event)
35+
callback(true)
36+
}
37+
38+
func TestFactoryClientReturnsDefaultClient(t *testing.T) {
39+
factory := OptimizelyFactory{}
40+
41+
client, err := factory.Client()
42+
expectedErr := errors.New("unable to instantiate client: no project config manager, SDK key, or a Datafile provided")
43+
assert.False(t, client.isValid)
44+
if assert.Error(t, err) {
45+
assert.Equal(t, err, expectedErr)
46+
}
47+
}
48+
49+
func TestClientWithSDKKey(t *testing.T) {
50+
factory := OptimizelyFactory{SDKKey: "1212"}
51+
52+
clientOptions := Options{}
53+
54+
client, err := factory.ClientWithOptions(clientOptions)
55+
assert.True(t, client.isValid)
56+
assert.NoError(t, err)
57+
assert.NotNil(t, client.configManager)
58+
assert.NotNil(t, client.decisionService)
59+
assert.NotNil(t, client.eventProcessor)
60+
}
61+
62+
func TestClientWithProjectConfigManagerInOptions(t *testing.T) {
63+
factory := OptimizelyFactory{}
64+
projectConfig := datafileprojectconfig.DatafileProjectConfig{}
65+
configManager := config.NewStaticProjectConfigManager(projectConfig)
66+
67+
clientOptions := Options{ProjectConfigManager: configManager}
68+
69+
client, err := factory.ClientWithOptions(clientOptions)
70+
assert.True(t, client.isValid)
71+
assert.NoError(t, err)
72+
assert.NotNil(t, client.configManager)
73+
assert.NotNil(t, client.decisionService)
74+
assert.NotNil(t, client.eventProcessor)
75+
}
76+
77+
func TestClientWithNoDecisionServiceAndEventProcessorInOptions(t *testing.T) {
78+
factory := OptimizelyFactory{}
79+
projectConfig := datafileprojectconfig.DatafileProjectConfig{}
80+
configManager := config.NewStaticProjectConfigManager(projectConfig)
81+
82+
clientOptions := Options{ProjectConfigManager: configManager}
83+
84+
client, err := factory.ClientWithOptions(clientOptions)
85+
assert.True(t, client.isValid)
86+
assert.NoError(t, err)
87+
assert.NotNil(t, client.configManager)
88+
assert.NotNil(t, client.decisionService)
89+
assert.NotNil(t, client.eventProcessor)
90+
}
91+
92+
func TestClientWithDecisionServiceAndEventProcessorInOptions(t *testing.T) {
93+
factory := OptimizelyFactory{}
94+
projectConfig := datafileprojectconfig.DatafileProjectConfig{}
95+
configManager := config.NewStaticProjectConfigManager(projectConfig)
96+
decisionService := new(MockDecisionService)
97+
processor := &event.QueueingEventProcessor{
98+
MaxQueueSize: 100,
99+
FlushInterval: 100,
100+
Q: event.NewInMemoryQueue(100),
101+
EventDispatcher: &MockDispatcher{},
102+
}
103+
104+
clientOptions := Options{
105+
ProjectConfigManager: configManager,
106+
DecisionService: decisionService,
107+
EventProcessor: processor,
108+
}
109+
110+
client, err := factory.ClientWithOptions(clientOptions)
111+
assert.True(t, client.isValid)
112+
assert.NoError(t, err)
113+
assert.Equal(t, decisionService, client.decisionService)
114+
assert.Equal(t, processor, client.eventProcessor)
115+
}
116+
26117
func TestClientWithOptionsErrorCase(t *testing.T) {
27118
// Error when no config manager, sdk key, or datafile is provided
28119
factory := OptimizelyFactory{}

optimizely/config/datafileprojectconfig/config.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,18 @@ func NewDatafileProjectConfig(jsonDatafile []byte) (*DatafileProjectConfig, erro
8989
rolloutMap := mappers.MapRollouts(datafile.Rollouts)
9090
mergedAudiences := append(datafile.TypedAudiences, datafile.Audiences...)
9191
config := &DatafileProjectConfig{
92+
accountID: datafile.AccountID,
93+
anonymizeIP: datafile.AnonymizeIP,
94+
attributeKeyToIDMap: attributeKeyToIDMap,
9295
audienceMap: mappers.MapAudiences(mergedAudiences),
9396
attributeMap: attributeMap,
94-
attributeKeyToIDMap: attributeKeyToIDMap,
95-
experimentMap: experimentMap,
97+
botFiltering: datafile.BotFiltering,
9698
experimentKeyToIDMap: experimentKeyMap,
97-
rolloutMap: rolloutMap,
99+
experimentMap: experimentMap,
98100
featureMap: mappers.MapFeatures(datafile.FeatureFlags, rolloutMap, experimentMap),
101+
projectID: datafile.ProjectID,
102+
revision: datafile.Revision,
103+
rolloutMap: rolloutMap,
99104
}
100105

101106
logger.Info("Datafile is valid.")

optimizely/config/datafileprojectconfig/mappers/experiment.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ func mapExperiment(rawExperiment datafileEntities.Experiment) entities.Experimen
6666
AudienceIds: rawExperiment.AudienceIds,
6767
ID: rawExperiment.ID,
6868
Key: rawExperiment.Key,
69-
TrafficAllocation: make([]entities.Range, len(rawExperiment.TrafficAllocation)),
7069
Variations: make(map[string]entities.Variation),
70+
TrafficAllocation: make([]entities.Range, len(rawExperiment.TrafficAllocation)),
7171
AudienceConditionTree: audienceConditionTree,
7272
}
7373

optimizely/decision/composite_experiment_service.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func NewCompositeExperimentService() *CompositeExperimentService {
4444

4545
// GetDecision returns a decision for the given experiment and user context
4646
func (s CompositeExperimentService) GetDecision(decisionContext ExperimentDecisionContext, userContext entities.UserContext) (ExperimentDecision, error) {
47+
4748
experimentDecision := ExperimentDecision{}
4849
experiment := decisionContext.Experiment
4950

optimizely/decision/composite_service.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,19 @@ func (s CompositeService) GetFeatureDecision(featureDecisionContext FeatureDecis
6262

6363
// @TODO: add errors
6464
if s.notificationCenter != nil {
65+
featureInfo := map[string]interface{}{
66+
"feature_key": featureDecisionContext.Feature.Key,
67+
"feature_enabled": false,
68+
"source": featureDecision.Source,
69+
}
70+
if featureDecision.Variation != nil {
71+
featureInfo["feature_enabled"] = featureDecision.Variation.FeatureEnabled
72+
}
73+
6574
decisionInfo := map[string]interface{}{
66-
"feature": map[string]interface{}{
67-
"feature_key": featureDecisionContext.Feature.Key,
68-
"feature_enabled": featureDecision.Variation.FeatureEnabled,
69-
"source": featureDecision.Source,
70-
},
75+
"feature": featureInfo,
7176
}
77+
7278
decisionNotification := notification.DecisionNotification{
7379
DecisionInfo: decisionInfo,
7480
Type: notification.Feature,

optimizely/event/dispatcher.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ var dispatcherLogger = logging.GetLogger("EventDispatcher")
4242
// DispatchEvent dispatches event with callback
4343
func (*HTTPEventDispatcher) DispatchEvent(event LogEvent, callback func(bool)) {
4444

45-
jsonValue, _ := json.Marshal(event.event)
46-
resp, err := http.Post(event.endPoint, jsonContentType, bytes.NewBuffer(jsonValue))
45+
jsonValue, _ := json.Marshal(event.Event)
46+
resp, err := http.Post(event.EndPoint, jsonContentType, bytes.NewBuffer(jsonValue))
4747
// also check response codes
4848
// resp.StatusCode == 400 is an error
4949
var success bool

optimizely/event/events.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ type ConversionEvent struct {
6262

6363
// LogEvent represents a log event
6464
type LogEvent struct {
65-
endPoint string
66-
event Batch
65+
EndPoint string
66+
Event Batch
6767
}
6868

6969
// Batch - Context about the event to send in batch

optimizely/event/factory.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const revenueKey = "revenue"
4444
const valueKey = "value"
4545

4646
func createLogEvent(event Batch) LogEvent {
47-
return LogEvent{endPoint: eventEndPoint, event: event}
47+
return LogEvent{EndPoint: eventEndPoint, Event: event}
4848
}
4949

5050
func makeTimestamp() int64 {
@@ -240,6 +240,7 @@ func getEventAttributes(projectConfig optimizely.ProjectConfig, attributes map[s
240240
efLogger.Debug(fmt.Sprintf("Unrecognized attribute %s provided. Pruning before sending event to Optimizely.", key))
241241
continue
242242
}
243+
visitorAttribute.Key = attribute.Key
243244
visitorAttribute.Value = value
244245
visitorAttribute.AttributeType = attributeType
245246

optimizely/event/factory_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func TestCreateAndSendImpressionEvent(t *testing.T) {
9393

9494
impressionUserEvent := BuildTestImpressionEvent()
9595

96-
processor := NewEventProcessor(ctx, 100, 100)
96+
processor := NewEventProcessor(ctx, 10, 100, 100)
9797

9898
processor.ProcessEvent(impressionUserEvent)
9999

@@ -109,7 +109,7 @@ func TestCreateAndSendConversionEvent(t *testing.T) {
109109

110110
conversionUserEvent := BuildTestConversionEvent()
111111

112-
processor := NewEventProcessor(ctx, 100, 100)
112+
processor := NewEventProcessor(ctx, 10, 100, 100)
113113

114114
processor.ProcessEvent(conversionUserEvent)
115115

0 commit comments

Comments
 (0)