Skip to content

Commit 6f69a3a

Browse files
yasirfolio3Michael Ng
authored andcommitted
feat(gherkin-onTrack): Implemented onTrack support for gherkin. (#214)
1 parent 5477d16 commit 6f69a3a

File tree

7 files changed

+151
-95
lines changed

7 files changed

+151
-95
lines changed

tests/integration/models/api_response.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ import (
2424
type APIResponse struct {
2525
Result interface{}
2626
Type entities.VariableType
27-
ListenerCalled []DecisionListener
27+
ListenerCalled []interface{}
2828
}

tests/integration/models/constants.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,9 @@ const (
6969

7070
// KeyListenerCalled - Key for listener called
7171
const KeyListenerCalled = "listener_called"
72+
73+
// KeyDecision - Key for Decision listener
74+
const KeyDecision = "Decision"
75+
76+
// KeyTrack - Key for Track listener
77+
const KeyTrack = "Track"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/****************************************************************************
2+
* Copyright 2019, Optimizely, Inc. and contributors *
3+
* *
4+
* Licensed under the Apache License, Version 2.0 (the "License"); *
5+
* you may not use this file except in compliance with the License. *
6+
* You may obtain a copy of the License at *
7+
* *
8+
* http://www.apache.org/licenses/LICENSE-2.0 *
9+
* *
10+
* Unless required by applicable law or agreed to in writing, software *
11+
* distributed under the License is distributed on an "AS IS" BASIS, *
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
* See the License for the specific language governing permissions and *
14+
* limitations under the License. *
15+
***************************************************************************/
16+
17+
package models
18+
19+
// TrackListener represents a track notification
20+
type TrackListener struct {
21+
EventKey string `yaml:"event_key"`
22+
Attributes map[string]interface{} `yaml:"attributes"`
23+
UserID string `yaml:"user_id"`
24+
EventTags map[string]interface{} `yaml:"event_tags"`
25+
}

tests/integration/optlyplugins/test_composite_service.go renamed to tests/integration/optlyplugins/notification_manager.go

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,48 +17,41 @@
1717
package optlyplugins
1818

1919
import (
20+
"github.com/optimizely/go-sdk/pkg/client"
2021
"github.com/optimizely/go-sdk/pkg/decision"
2122
"github.com/optimizely/go-sdk/pkg/entities"
23+
"github.com/optimizely/go-sdk/pkg/event"
2224
"github.com/optimizely/go-sdk/pkg/notification"
2325
"github.com/optimizely/go-sdk/tests/integration/models"
2426
)
2527

26-
// TestCompositeService represents a CompositeService with custom implementations
27-
type TestCompositeService struct {
28-
decision.CompositeService
29-
listenersCalled []models.DecisionListener
28+
// NotificationManager manager class for notification listeners
29+
type NotificationManager struct {
30+
listenersCalled []interface{}
3031
}
3132

32-
// AddListeners - Adds Notification Listeners
33-
func (c *TestCompositeService) AddListeners(listeners map[string]int) {
33+
// SubscribeNotifications subscribes to the provided notification listeners
34+
func (n *NotificationManager) SubscribeNotifications(listeners map[string]int, client *client.OptimizelyClient) {
3435

35-
if len(listeners) < 1 {
36-
return
37-
}
38-
for listenerType, count := range listeners {
39-
for i := 1; i <= count; i++ {
40-
switch listenerType {
41-
case "Decision":
42-
c.OnDecision(c.decisionNotificationCallback)
43-
break
44-
default:
45-
break
46-
}
36+
addNotificationCallback := func(notificationType string) {
37+
switch notificationType {
38+
case models.KeyDecision:
39+
client.DecisionService.OnDecision(n.decisionCallback)
40+
break
41+
case models.KeyTrack:
42+
client.OnTrack(n.trackCallback)
43+
break
4744
}
4845
}
49-
}
5046

51-
// GetListenersCalled - Returns listeners called
52-
func (c *TestCompositeService) GetListenersCalled() []models.DecisionListener {
53-
listenerCalled := c.listenersCalled
54-
// Since for every scenario, a new sdk instance is created, emptying listenersCalled is required for scenario's
55-
// where multiple requests are executed but no session is to be maintained among them.
56-
// @TODO: Make it optional once event-batching(sessioned) tests are implemented.
57-
c.listenersCalled = nil
58-
return listenerCalled
47+
for key, count := range listeners {
48+
for i := 0; i < count; i++ {
49+
addNotificationCallback(key)
50+
}
51+
}
5952
}
6053

61-
func (c *TestCompositeService) decisionNotificationCallback(notification notification.DecisionNotification) {
54+
func (n *NotificationManager) decisionCallback(notification notification.DecisionNotification) {
6255

6356
model := models.DecisionListener{}
6457
model.Type = notification.Type
@@ -71,7 +64,17 @@ func (c *TestCompositeService) decisionNotificationCallback(notification notific
7164

7265
decisionInfoDict := getDecisionInfoForNotification(notification)
7366
model.DecisionInfo = decisionInfoDict
74-
c.listenersCalled = append(c.listenersCalled, model)
67+
n.listenersCalled = append(n.listenersCalled, model)
68+
}
69+
70+
func (n *NotificationManager) trackCallback(eventKey string, userContext entities.UserContext, eventTags map[string]interface{}, conversionEvent event.ConversionEvent) {
71+
listener := models.TrackListener{
72+
EventKey: eventKey,
73+
UserID: userContext.ID,
74+
Attributes: userContext.Attributes,
75+
EventTags: eventTags,
76+
}
77+
n.listenersCalled = append(n.listenersCalled, listener)
7578
}
7679

7780
func getDecisionInfoForNotification(decisionNotification notification.DecisionNotification) map[string]interface{} {
@@ -135,3 +138,13 @@ func getDecisionInfoForNotification(decisionNotification notification.DecisionNo
135138
}
136139
return decisionInfoDict
137140
}
141+
142+
// GetListenersCalled - Returns listeners called
143+
func (n *NotificationManager) GetListenersCalled() []interface{} {
144+
listenerCalled := n.listenersCalled
145+
// Since for every scenario, a new sdk instance is created, emptying listenersCalled is required for scenario's
146+
// where multiple requests are executed but no session is to be maintained among them.
147+
// @TODO: Make it optional once event-batching(sessioned) tests are implemented.
148+
n.listenersCalled = nil
149+
return listenerCalled
150+
}

tests/integration/support/client_wrapper.go

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ var sdkKey int
4343

4444
// ClientWrapper - wrapper around the optimizely client that keeps track of various custom components used with the client
4545
type ClientWrapper struct {
46-
Client *client.OptimizelyClient
47-
DecisionService decision.Service
48-
EventDispatcher event.Dispatcher
49-
UserProfileService decision.UserProfileService
50-
OverrideStore decision.ExperimentOverrideStore
46+
client *client.OptimizelyClient
47+
eventDispatcher event.Dispatcher
48+
userProfileService decision.UserProfileService
49+
overrideStore decision.ExperimentOverrideStore
50+
notificationManager *optlyplugins.NotificationManager
5151
}
5252

5353
// DeleteInstance deletes cached instance of optly wrapper
@@ -98,28 +98,25 @@ func GetInstance(apiOptions models.APIOptions) *ClientWrapper {
9898
)
9999

100100
// @TODO: Add sdkKey dynamically once event-batching support is implemented
101-
102-
compositeService := *decision.NewCompositeService(strconv.Itoa(sdkKey), decision.WithCompositeExperimentService(compositeExperimentService))
103-
decisionService := &optlyplugins.TestCompositeService{CompositeService: compositeService}
104-
101+
compositeService := decision.NewCompositeService(strconv.Itoa(sdkKey), decision.WithCompositeExperimentService(compositeExperimentService))
105102
client, err := optimizelyFactory.Client(
106103
client.WithConfigManager(configManager),
107-
client.WithDecisionService(decisionService),
104+
client.WithDecisionService(compositeService),
108105
client.WithEventProcessor(eventProcessor),
109106
)
110107
if err != nil {
111108
log.Fatal(err)
112109
}
113110

111+
notificationManager := optlyplugins.NotificationManager{}
112+
notificationManager.SubscribeNotifications(apiOptions.Listeners, client)
114113
clientInstance = &ClientWrapper{
115-
Client: client,
116-
DecisionService: decisionService,
117-
EventDispatcher: eventProcessor.EventDispatcher,
118-
UserProfileService: userProfileService,
119-
OverrideStore: overrideStore,
114+
client: client,
115+
eventDispatcher: eventProcessor.EventDispatcher,
116+
userProfileService: userProfileService,
117+
overrideStore: overrideStore,
118+
notificationManager: &notificationManager,
120119
}
121-
clientInstance.DecisionService.(*optlyplugins.TestCompositeService).AddListeners(apiOptions.Listeners)
122-
123120
return clientInstance
124121
}
125122

@@ -170,8 +167,8 @@ func (c *ClientWrapper) InvokeAPI(request models.APIOptions) (models.APIResponse
170167
break
171168
}
172169
// TODO: For event batching, it should be conditional.
173-
c.Client.Close()
174-
response.ListenerCalled = c.DecisionService.(*optlyplugins.TestCompositeService).GetListenersCalled()
170+
c.client.Close()
171+
response.ListenerCalled = c.notificationManager.GetListenersCalled()
175172
return response, err
176173
}
177174

@@ -185,7 +182,7 @@ func (c *ClientWrapper) isFeatureEnabled(request models.APIOptions) (models.APIR
185182
Attributes: params.Attributes,
186183
}
187184

188-
isEnabled, err := c.Client.IsFeatureEnabled(params.FeatureKey, user)
185+
isEnabled, err := c.client.IsFeatureEnabled(params.FeatureKey, user)
189186
result := "false"
190187
if err == nil && isEnabled {
191188
result = "true"
@@ -204,7 +201,7 @@ func (c *ClientWrapper) getFeatureVariable(request models.APIOptions) (models.AP
204201
ID: params.UserID,
205202
Attributes: params.Attributes,
206203
}
207-
value, valueType, err := c.Client.GetFeatureVariable(params.FeatureKey, params.VariableKey, user)
204+
value, valueType, err := c.client.GetFeatureVariable(params.FeatureKey, params.VariableKey, user)
208205
if err == nil {
209206
response.Result = value
210207
response.Type = valueType
@@ -222,7 +219,7 @@ func (c *ClientWrapper) getFeatureVariableInteger(request models.APIOptions) (mo
222219
ID: params.UserID,
223220
Attributes: params.Attributes,
224221
}
225-
value, err := c.Client.GetFeatureVariableInteger(params.FeatureKey, params.VariableKey, user)
222+
value, err := c.client.GetFeatureVariableInteger(params.FeatureKey, params.VariableKey, user)
226223
if err == nil {
227224
response.Result = value
228225
}
@@ -239,7 +236,7 @@ func (c *ClientWrapper) getFeatureVariableDouble(request models.APIOptions) (mod
239236
ID: params.UserID,
240237
Attributes: params.Attributes,
241238
}
242-
value, err := c.Client.GetFeatureVariableDouble(params.FeatureKey, params.VariableKey, user)
239+
value, err := c.client.GetFeatureVariableDouble(params.FeatureKey, params.VariableKey, user)
243240
if err == nil {
244241
response.Result = value
245242
}
@@ -256,7 +253,7 @@ func (c *ClientWrapper) getFeatureVariableBoolean(request models.APIOptions) (mo
256253
ID: params.UserID,
257254
Attributes: params.Attributes,
258255
}
259-
value, err := c.Client.GetFeatureVariableBoolean(params.FeatureKey, params.VariableKey, user)
256+
value, err := c.client.GetFeatureVariableBoolean(params.FeatureKey, params.VariableKey, user)
260257
if err == nil {
261258
response.Result = value
262259
}
@@ -273,7 +270,7 @@ func (c *ClientWrapper) getFeatureVariableString(request models.APIOptions) (mod
273270
ID: params.UserID,
274271
Attributes: params.Attributes,
275272
}
276-
value, err := c.Client.GetFeatureVariableString(params.FeatureKey, params.VariableKey, user)
273+
value, err := c.client.GetFeatureVariableString(params.FeatureKey, params.VariableKey, user)
277274
if err == nil {
278275
response.Result = value
279276
}
@@ -291,7 +288,7 @@ func (c *ClientWrapper) getEnabledFeatures(request models.APIOptions) (models.AP
291288
ID: params.UserID,
292289
Attributes: params.Attributes,
293290
}
294-
if values, err := c.Client.GetEnabledFeatures(user); err == nil {
291+
if values, err := c.client.GetEnabledFeatures(user); err == nil {
295292
enabledFeatures = values
296293
}
297294
response.Result = enabledFeatures
@@ -308,7 +305,7 @@ func (c *ClientWrapper) getVariation(request models.APIOptions) (models.APIRespo
308305
ID: params.UserID,
309306
Attributes: params.Attributes,
310307
}
311-
response.Result, _ = c.Client.GetVariation(params.ExperimentKey, user)
308+
response.Result, _ = c.client.GetVariation(params.ExperimentKey, user)
312309
if response.Result == "" {
313310
response.Result = "NULL"
314311
}
@@ -325,7 +322,7 @@ func (c *ClientWrapper) activate(request models.APIOptions) (models.APIResponse,
325322
ID: params.UserID,
326323
Attributes: params.Attributes,
327324
}
328-
response.Result, _ = c.Client.Activate(params.ExperimentKey, user)
325+
response.Result, _ = c.client.Activate(params.ExperimentKey, user)
329326
if response.Result == "" {
330327
response.Result = "NULL"
331328
}
@@ -342,7 +339,7 @@ func (c *ClientWrapper) track(request models.APIOptions) (models.APIResponse, er
342339
ID: params.UserID,
343340
Attributes: params.Attributes,
344341
}
345-
err = c.Client.Track(params.EventKey, user, params.EventTags)
342+
err = c.client.Track(params.EventKey, user, params.EventTags)
346343
}
347344
response.Result = "NULL"
348345
return response, err
@@ -356,9 +353,9 @@ func (c *ClientWrapper) setForcedVariation(request models.APIOptions) (models.AP
356353
if err == nil {
357354
// For removeForcedVariation cases
358355
if params.VariationKey == "" {
359-
c.OverrideStore.(*decision.MapExperimentOverridesStore).RemoveVariation(decision.ExperimentOverrideKey{ExperimentKey: params.ExperimentKey, UserID: params.UserID})
356+
c.overrideStore.(*decision.MapExperimentOverridesStore).RemoveVariation(decision.ExperimentOverrideKey{ExperimentKey: params.ExperimentKey, UserID: params.UserID})
360357
} else {
361-
c.OverrideStore.(*decision.MapExperimentOverridesStore).SetVariation(decision.ExperimentOverrideKey{ExperimentKey: params.ExperimentKey, UserID: params.UserID}, params.VariationKey)
358+
c.overrideStore.(*decision.MapExperimentOverridesStore).SetVariation(decision.ExperimentOverrideKey{ExperimentKey: params.ExperimentKey, UserID: params.UserID}, params.VariationKey)
362359
response.Result = params.VariationKey
363360
}
364361
}
@@ -371,7 +368,7 @@ func (c *ClientWrapper) getForcedVariation(request models.APIOptions) (models.AP
371368
err := yaml.Unmarshal([]byte(request.Arguments), &params)
372369
response.Result = "NULL"
373370
if err == nil {
374-
variation, success := c.OverrideStore.(*decision.MapExperimentOverridesStore).GetVariation(decision.ExperimentOverrideKey{ExperimentKey: params.ExperimentKey, UserID: params.UserID})
371+
variation, success := c.overrideStore.(*decision.MapExperimentOverridesStore).GetVariation(decision.ExperimentOverrideKey{ExperimentKey: params.ExperimentKey, UserID: params.UserID})
375372
if success {
376373
response.Result = variation
377374
}

0 commit comments

Comments
 (0)