11/****************************************************************************
2- * Copyright 2020, Optimizely, Inc. and contributors *
2+ * Copyright 2020-2021 , Optimizely, Inc. and contributors *
33 * *
44 * Licensed under the Apache License, Version 2.0 (the "License"); *
55 * you may not use this file except in compliance with the License. *
@@ -21,6 +21,7 @@ import (
2121 "sync"
2222
2323 "github.com/optimizely/go-sdk/pkg/decide"
24+ "github.com/optimizely/go-sdk/pkg/decision"
2425 "github.com/optimizely/go-sdk/pkg/entities"
2526)
2627
@@ -29,23 +30,24 @@ type OptimizelyUserContext struct {
2930 UserID string `json:"userId"`
3031 Attributes map [string ]interface {} `json:"attributes"`
3132
32- optimizely * OptimizelyClient
33- mutex * sync.RWMutex
33+ optimizely * OptimizelyClient
34+ forcedDecisionService * decision.ForcedDecisionService
35+ mutex * sync.RWMutex
3436}
3537
3638// returns an instance of the optimizely user context.
37- func newOptimizelyUserContext (optimizely * OptimizelyClient , userID string , attributes map [string ]interface {}) OptimizelyUserContext {
39+ func newOptimizelyUserContext (optimizely * OptimizelyClient , userID string , attributes map [string ]interface {}, forcedDecisionService * decision. ForcedDecisionService ) OptimizelyUserContext {
3840 // store a copy of the provided attributes so it isn't affected by changes made afterwards.
3941 if attributes == nil {
4042 attributes = map [string ]interface {}{}
4143 }
4244 attributesCopy := copyUserAttributes (attributes )
43-
4445 return OptimizelyUserContext {
45- UserID : userID ,
46- Attributes : attributesCopy ,
47- optimizely : optimizely ,
48- mutex : new (sync.RWMutex ),
46+ UserID : userID ,
47+ Attributes : attributesCopy ,
48+ optimizely : optimizely ,
49+ forcedDecisionService : forcedDecisionService ,
50+ mutex : new (sync.RWMutex ),
4951 }
5052}
5153
@@ -66,6 +68,13 @@ func (o OptimizelyUserContext) GetUserAttributes() map[string]interface{} {
6668 return copyUserAttributes (o .Attributes )
6769}
6870
71+ func (o OptimizelyUserContext ) getForcedDecisionService () * decision.ForcedDecisionService {
72+ if o .forcedDecisionService != nil {
73+ return o .forcedDecisionService .CreateCopy ()
74+ }
75+ return nil
76+ }
77+
6978// SetAttribute sets an attribute for a given key.
7079func (o * OptimizelyUserContext ) SetAttribute (key string , value interface {}) {
7180 o .mutex .Lock ()
@@ -80,21 +89,21 @@ func (o *OptimizelyUserContext) SetAttribute(key string, value interface{}) {
8089// all data required to deliver the flag or experiment.
8190func (o * OptimizelyUserContext ) Decide (key string , options []decide.OptimizelyDecideOptions ) OptimizelyDecision {
8291 // use a copy of the user context so that any changes to the original context are not reflected inside the decision
83- userContextCopy := newOptimizelyUserContext (o .GetOptimizely (), o .GetUserID (), o .GetUserAttributes ())
92+ userContextCopy := newOptimizelyUserContext (o .GetOptimizely (), o .GetUserID (), o .GetUserAttributes (), o . getForcedDecisionService () )
8493 return o .optimizely .decide (userContextCopy , key , convertDecideOptions (options ))
8594}
8695
8796// DecideAll returns a key-map of decision results for all active flag keys with options.
8897func (o * OptimizelyUserContext ) DecideAll (options []decide.OptimizelyDecideOptions ) map [string ]OptimizelyDecision {
8998 // use a copy of the user context so that any changes to the original context are not reflected inside the decision
90- userContextCopy := newOptimizelyUserContext (o .GetOptimizely (), o .GetUserID (), o .GetUserAttributes ())
99+ userContextCopy := newOptimizelyUserContext (o .GetOptimizely (), o .GetUserID (), o .GetUserAttributes (), o . getForcedDecisionService () )
91100 return o .optimizely .decideAll (userContextCopy , convertDecideOptions (options ))
92101}
93102
94103// DecideForKeys returns a key-map of decision results for multiple flag keys and options.
95104func (o * OptimizelyUserContext ) DecideForKeys (keys []string , options []decide.OptimizelyDecideOptions ) map [string ]OptimizelyDecision {
96105 // use a copy of the user context so that any changes to the original context are not reflected inside the decision
97- userContextCopy := newOptimizelyUserContext (o .GetOptimizely (), o .GetUserID (), o .GetUserAttributes ())
106+ userContextCopy := newOptimizelyUserContext (o .GetOptimizely (), o .GetUserID (), o .GetUserAttributes (), o . getForcedDecisionService () )
98107 return o .optimizely .decideForKeys (userContextCopy , keys , convertDecideOptions (options ))
99108}
100109
@@ -108,6 +117,55 @@ func (o *OptimizelyUserContext) TrackEvent(eventKey string, eventTags map[string
108117 return o .optimizely .Track (eventKey , userContext , eventTags )
109118}
110119
120+ // SetForcedDecision sets the forced decision (variation key) for a given flag and an optional rule.
121+ // returns true if the forced decision has been set successfully.
122+ func (o * OptimizelyUserContext ) SetForcedDecision (flagKey , ruleKey , variationKey string ) bool {
123+ if _ , err := o .optimizely .getProjectConfig (); err != nil {
124+ o .optimizely .logger .Error ("Optimizely instance is not valid, failing setForcedDecision call." , err )
125+ return false
126+ }
127+ if o .forcedDecisionService == nil {
128+ o .forcedDecisionService = decision .NewForcedDecisionService (o .GetUserID ())
129+ }
130+ return o .forcedDecisionService .SetForcedDecision (flagKey , ruleKey , variationKey )
131+ }
132+
133+ // GetForcedDecision returns the forced decision for a given flag and an optional rule
134+ func (o * OptimizelyUserContext ) GetForcedDecision (flagKey , ruleKey string ) string {
135+ if _ , err := o .optimizely .getProjectConfig (); err != nil {
136+ o .optimizely .logger .Error ("Optimizely instance is not valid, failing getForcedDecision call." , err )
137+ return ""
138+ }
139+ if o .forcedDecisionService == nil {
140+ return ""
141+ }
142+ return o .forcedDecisionService .GetForcedDecision (flagKey , ruleKey )
143+ }
144+
145+ // RemoveForcedDecision removes the forced decision for a given flag and an optional rule.
146+ func (o * OptimizelyUserContext ) RemoveForcedDecision (flagKey , ruleKey string ) bool {
147+ if _ , err := o .optimizely .getProjectConfig (); err != nil {
148+ o .optimizely .logger .Error ("Optimizely instance is not valid, failing removeForcedDecision call." , err )
149+ return false
150+ }
151+ if o .forcedDecisionService == nil {
152+ return false
153+ }
154+ return o .forcedDecisionService .RemoveForcedDecision (flagKey , ruleKey )
155+ }
156+
157+ // RemoveAllForcedDecisions removes all forced decisions bound to this user context.
158+ func (o * OptimizelyUserContext ) RemoveAllForcedDecisions () bool {
159+ if _ , err := o .optimizely .getProjectConfig (); err != nil {
160+ o .optimizely .logger .Error ("Optimizely instance is not valid, failing removeForcedDecision call." , err )
161+ return false
162+ }
163+ if o .forcedDecisionService == nil {
164+ return true
165+ }
166+ return o .forcedDecisionService .RemoveAllForcedDecisions ()
167+ }
168+
111169func copyUserAttributes (attributes map [string ]interface {}) (attributesCopy map [string ]interface {}) {
112170 if attributes != nil {
113171 attributesCopy = make (map [string ]interface {})
0 commit comments