Skip to content

Commit

Permalink
add support for batch ups
Browse files Browse the repository at this point in the history
  • Loading branch information
pulak-opti committed Oct 16, 2024
1 parent 6a21959 commit 53c853b
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 1 deletion.
42 changes: 42 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ type OptimizelyClient struct {
ctx context.Context
ConfigManager config.ProjectConfigManager
DecisionService decision.Service
UserProfileService decision.UserProfileService
EventProcessor event.Processor
OdpManager odp.Manager
notificationCenter notification.Center
Expand Down Expand Up @@ -178,12 +179,37 @@ func (o *OptimizelyClient) decide(userContext OptimizelyUserContext, key string,
decisionContext.Variable = entities.Variable{}
var featureDecision decision.FeatureDecision
var reasons decide.DecisionReasons
foundSavedVariation := false

// To avoid cyclo-complexity warning
findRegularDecision := func() {
// regular decision
featureDecision, reasons, err = o.DecisionService.GetFeatureDecision(decisionContext, usrContext, &allOptions)
decisionReasons.Append(reasons)

if o.UserProfileService != nil && featureDecision.Variation != nil {
decisionKey := decision.NewUserDecisionKey(featureDecision.Experiment.ID)
savedUserProfile := o.UserProfileService.Lookup(userContext.GetUserID())

if savedVariationID, ok := savedUserProfile.ExperimentBucketMap[decisionKey]; ok {
infoMessage := reasons.AddInfo(`User "%s" was previously bucketed into variation "%s" of experiment "%s".`, userContext.GetUserID(), savedVariationID, featureDecision.Experiment.ID)
o.logger.Debug(infoMessage)
variationList := projectConfig.GetFlagVariationsMap()[key]
var variation *entities.Variation
for _, v := range variationList {
if v.ID == savedVariationID {
variation = &v
foundSavedVariation = true
break
}
}
if !foundSavedVariation {
warningMessage := reasons.AddInfo(`User "%s" was previously bucketed into variation with ID "%s" for experiment "%s", but no matching variation was found.`, userContext.GetUserID(), savedVariationID, featureDecision.Experiment.ID)
o.logger.Warning(warningMessage)
}
featureDecision = decision.FeatureDecision{Decision: decision.Decision{Reason: pkgReasons.BucketedIntoVariation}, Variation: variation, Source: featureDecision.Source}
}
}
}

// check forced-decisions first
Expand Down Expand Up @@ -235,6 +261,12 @@ func (o *OptimizelyClient) decide(userContext OptimizelyUserContext, key string,
}
}

if !foundSavedVariation && o.UserProfileService != nil && featureDecision.Variation != nil {
decisionKey := decision.NewUserDecisionKey(featureDecision.Experiment.ID)
userContext.userProfile.ExperimentBucketMap[decisionKey] = featureDecision.Variation.ID
o.logger.Debug(fmt.Sprintf(`Decision saved for user %q experiment %s variation %s.`, userContext.GetUserID(), featureDecision.Experiment.ID, featureDecision.Variation.ID))
}

return NewOptimizelyDecision(variationKey, ruleKey, key, flagEnabled, optimizelyJSON, userContext, reasonsToReport)
}

Expand Down Expand Up @@ -269,6 +301,12 @@ func (o *OptimizelyClient) decideForKeys(userContext OptimizelyUserContext, keys
return decisionMap
}

userProfile := decision.UserProfile{
ID: userContext.GetUserID(),
ExperimentBucketMap: make(map[decision.UserDecisionKey]string),
}
userContext.SetUserProfile(&userProfile)

enabledFlagsOnly := o.getAllOptions(options).EnabledFlagsOnly
for _, key := range keys {
optimizelyDecision := o.decide(userContext, key, options)
Expand All @@ -277,6 +315,10 @@ func (o *OptimizelyClient) decideForKeys(userContext OptimizelyUserContext, keys
}
}

if o.UserProfileService != nil && len(userContext.userProfile.ExperimentBucketMap) > 0 {
o.UserProfileService.Save(userProfile)
}

return decisionMap
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/client/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func (f *OptimizelyFactory) Client(clientOptions ...OptionFunc) (*OptimizelyClie
} else {
var experimentServiceOptions []decision.CESOptionFunc
if f.userProfileService != nil {
experimentServiceOptions = append(experimentServiceOptions, decision.WithUserProfileService(f.userProfileService))
appClient.UserProfileService = f.userProfileService
}
if f.overrideStore != nil {
experimentServiceOptions = append(experimentServiceOptions, decision.WithOverrideStore(f.overrideStore))
Expand Down
6 changes: 6 additions & 0 deletions pkg/client/optimizely_user_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"sync"

"github.com/optimizely/go-sdk/v2/pkg/decide"
"github.com/optimizely/go-sdk/v2/pkg/decision"
pkgDecision "github.com/optimizely/go-sdk/v2/pkg/decision"
"github.com/optimizely/go-sdk/v2/pkg/entities"
pkgOdpSegment "github.com/optimizely/go-sdk/v2/pkg/odp/segment"
Expand All @@ -36,6 +37,7 @@ type OptimizelyUserContext struct {
optimizely *OptimizelyClient
forcedDecisionService *pkgDecision.ForcedDecisionService
mutex *sync.RWMutex
userProfile *decision.UserProfile
}

// returns an instance of the optimizely user context.
Expand Down Expand Up @@ -190,6 +192,10 @@ func (o *OptimizelyUserContext) RemoveAllForcedDecisions() bool {
return o.forcedDecisionService.RemoveAllForcedDecisions()
}

func (o *OptimizelyUserContext) SetUserProfile(userProfile *decision.UserProfile) {
o.userProfile = userProfile
}

func copyUserAttributes(attributes map[string]interface{}) (attributesCopy map[string]interface{}) {
if attributes != nil {
attributesCopy = make(map[string]interface{})
Expand Down

0 comments on commit 53c853b

Please sign in to comment.