11use self :: analytics:: AnalyticsProcessor ;
22use self :: models:: { Flag , Flags } ;
33use super :: error;
4- use flagsmith_flag_engine:: engine;
4+ use flagsmith_flag_engine:: engine:: get_evaluation_result;
5+ use flagsmith_flag_engine:: engine_eval:: {
6+ add_identity_to_context, environment_to_context, EngineEvaluationContext , SegmentSource ,
7+ } ;
58use flagsmith_flag_engine:: environments:: builders:: build_environment_struct;
69use flagsmith_flag_engine:: environments:: Environment ;
7- use flagsmith_flag_engine:: identities:: { Identity , Trait } ;
8- use flagsmith_flag_engine:: segments:: evaluator:: get_identity_segments;
10+ use flagsmith_flag_engine:: identities:: Trait ;
911use flagsmith_flag_engine:: segments:: Segment ;
1012use log:: debug;
1113use models:: SDKTrait ;
@@ -70,7 +72,7 @@ pub struct Flagsmith {
7072
7173struct DataStore {
7274 environment : Option < Environment > ,
73- identities_with_overrides_by_identifier : HashMap < String , Identity > ,
75+ evaluation_context : Option < EngineEvaluationContext > ,
7476}
7577
7678impl Flagsmith {
@@ -108,6 +110,9 @@ impl Flagsmith {
108110 {
109111 panic ! ( "offline_handler cannot be used with local evaluation" )
110112 }
113+ if flagsmith_options. enable_local_evaluation && !environment_key. starts_with ( "ser." ) {
114+ panic ! ( "In order to use local evaluation, please use a server-side environment key (starts with 'ser.')" )
115+ }
111116
112117 // Initialize analytics processor
113118 let analytics_processor = match flagsmith_options. enable_analytics {
@@ -122,7 +127,10 @@ impl Flagsmith {
122127
123128 // Put the environment model behind mutex to
124129 // to share it safely between threads
125- let ds = Arc :: new ( Mutex :: new ( DataStore { environment : None , identities_with_overrides_by_identifier : HashMap :: new ( ) } ) ) ;
130+ let ds = Arc :: new ( Mutex :: new ( DataStore {
131+ environment : None ,
132+ evaluation_context : None ,
133+ } ) ) ;
126134 let ( tx, rx) = mpsc:: sync_channel :: < u32 > ( 1 ) ;
127135
128136 let flagsmith = Flagsmith {
@@ -138,14 +146,18 @@ impl Flagsmith {
138146
139147 if flagsmith. options . offline_handler . is_some ( ) {
140148 let mut data = flagsmith. datastore . lock ( ) . unwrap ( ) ;
141- data. environment = Some (
142- flagsmith
143- . options
144- . offline_handler
145- . as_ref ( )
146- . unwrap ( )
147- . get_environment ( ) ,
148- )
149+ let environment = flagsmith
150+ . options
151+ . offline_handler
152+ . as_ref ( )
153+ . unwrap ( )
154+ . get_environment ( ) ;
155+
156+ // Create evaluation context from offline environment
157+ let eval_context = environment_to_context ( environment. clone ( ) ) ;
158+ data. evaluation_context = Some ( eval_context) ;
159+
160+ data. environment = Some ( environment) ;
149161 }
150162
151163 // Create a thread to update environment document
@@ -155,8 +167,10 @@ impl Flagsmith {
155167
156168 if flagsmith. options . enable_local_evaluation {
157169 // Update environment once...
158- update_environment ( & client, & ds, & environment_url) . unwrap ( ) ;
159-
170+ if let Err ( e) = update_environment ( & client, & ds, & environment_url) {
171+ log:: warn!( "Failed to fetch environment on initialization: {}. Will retry in background." , e) ;
172+ }
173+
160174 // ...and continue updating in the background
161175 let ds = Arc :: clone ( & ds) ;
162176 thread:: spawn ( move || loop {
@@ -168,17 +182,19 @@ impl Flagsmith {
168182 Err ( TryRecvError :: Empty ) => { }
169183 }
170184 thread:: sleep ( Duration :: from_millis ( environment_refresh_interval_mills) ) ;
171- update_environment ( & client, & ds, & environment_url) . unwrap ( ) ;
185+ if let Err ( e) = update_environment ( & client, & ds, & environment_url) {
186+ log:: warn!( "Failed to update environment: {}. Will retry on next interval." , e) ;
187+ }
172188 } ) ;
173189 }
174190 return flagsmith;
175191 }
176192 //Returns `Flags` struct holding all the flags for the current environment.
177193 pub fn get_environment_flags ( & self ) -> Result < models:: Flags , error:: Error > {
178194 let data = self . datastore . lock ( ) . unwrap ( ) ;
179- if data. environment . is_some ( ) {
180- let environment = data. environment . as_ref ( ) . unwrap ( ) ;
181- return Ok ( self . get_environment_flags_from_document ( environment ) ) ;
195+ if data. evaluation_context . is_some ( ) {
196+ let eval_context = data. evaluation_context . as_ref ( ) . unwrap ( ) ;
197+ return Ok ( self . get_environment_flags_from_document ( eval_context ) ) ;
182198 }
183199 return self . default_handler_if_err ( self . get_environment_flags_from_api ( ) ) ;
184200 }
@@ -211,12 +227,11 @@ impl Flagsmith {
211227 ) -> Result < Flags , error:: Error > {
212228 let data = self . datastore . lock ( ) . unwrap ( ) ;
213229 let traits = traits. unwrap_or ( vec ! [ ] ) ;
214- if data. environment . is_some ( ) {
215- let environment = data. environment . as_ref ( ) . unwrap ( ) ;
230+ if data. evaluation_context . is_some ( ) {
231+ let eval_context = data. evaluation_context . as_ref ( ) . unwrap ( ) ;
216232 let engine_traits: Vec < Trait > = traits. into_iter ( ) . map ( |t| t. into ( ) ) . collect ( ) ;
217233 return self . get_identity_flags_from_document (
218- environment,
219- & data. identities_with_overrides_by_identifier ,
234+ eval_context,
220235 identifier,
221236 engine_traits,
222237 ) ;
@@ -234,17 +249,33 @@ impl Flagsmith {
234249 traits : Option < Vec < Trait > > ,
235250 ) -> Result < Vec < Segment > , error:: Error > {
236251 let data = self . datastore . lock ( ) . unwrap ( ) ;
237- if data. environment . is_none ( ) {
252+ if data. evaluation_context . is_none ( ) {
238253 return Err ( error:: Error :: new (
239254 error:: ErrorKind :: FlagsmithClientError ,
240255 "Local evaluation required to obtain identity segments." . to_string ( ) ,
241256 ) ) ;
242257 }
243- let environment = data. environment . as_ref ( ) . unwrap ( ) ;
244- let identities_with_overrides_by_identifier = & data. identities_with_overrides_by_identifier ;
245- let identity_model =
246- self . get_identity_model ( & environment, & identities_with_overrides_by_identifier, identifier, traits. clone ( ) . unwrap_or ( vec ! [ ] ) ) ?;
247- let segments = get_identity_segments ( environment, & identity_model, traits. as_ref ( ) ) ;
258+ let eval_context = data. evaluation_context . as_ref ( ) . unwrap ( ) ;
259+ let traits = traits. unwrap_or ( vec ! [ ] ) ;
260+
261+ let context_with_identity = add_identity_to_context ( eval_context, identifier, & traits) ;
262+
263+ let result = get_evaluation_result ( & context_with_identity) ;
264+
265+ let segments: Vec < Segment > = result
266+ . segments
267+ . iter ( )
268+ . filter ( |seg_result| {
269+ seg_result. metadata . source == SegmentSource :: Api
270+ } )
271+ . map ( |seg_result| Segment {
272+ id : seg_result. metadata . segment_id . unwrap_or ( 0 ) as u32 ,
273+ name : seg_result. name . clone ( ) ,
274+ rules : vec ! [ ] ,
275+ feature_states : vec ! [ ] ,
276+ } )
277+ . collect ( ) ;
278+
248279 return Ok ( segments) ;
249280 }
250281
@@ -268,12 +299,19 @@ impl Flagsmith {
268299 }
269300 }
270301 }
271- fn get_environment_flags_from_document ( & self , environment : & Environment ) -> models:: Flags {
272- return models:: Flags :: from_feature_states (
273- & environment. feature_states ,
302+ fn get_environment_flags_from_document ( & self , eval_context : & EngineEvaluationContext ) -> models:: Flags {
303+ // Clear segments and identity for environment evaluation
304+ let environment_eval_ctx = EngineEvaluationContext {
305+ environment : eval_context. environment . clone ( ) ,
306+ features : eval_context. features . clone ( ) ,
307+ segments : HashMap :: new ( ) ,
308+ identity : None ,
309+ } ;
310+ let result = get_evaluation_result ( & environment_eval_ctx) ;
311+ return models:: Flags :: from_evaluation_result (
312+ & result,
274313 self . analytics_processor . clone ( ) ,
275314 self . options . default_flag_handler ,
276- None ,
277315 ) ;
278316 }
279317 pub fn update_environment ( & mut self ) -> Result < ( ) , error:: Error > {
@@ -282,41 +320,22 @@ impl Flagsmith {
282320
283321 fn get_identity_flags_from_document (
284322 & self ,
285- environment : & Environment ,
286- identities_with_overrides_by_identifier : & HashMap < String , Identity > ,
323+ eval_context : & EngineEvaluationContext ,
287324 identifier : & str ,
288325 traits : Vec < Trait > ,
289326 ) -> Result < Flags , error:: Error > {
290- let identity = self . get_identity_model ( environment, identities_with_overrides_by_identifier, identifier, traits. clone ( ) ) ?;
291- let feature_states =
292- engine:: get_identity_feature_states ( environment, & identity, Some ( traits. as_ref ( ) ) ) ;
293- let flags = Flags :: from_feature_states (
294- & feature_states,
327+ let context_with_identity = add_identity_to_context ( eval_context, identifier, & traits) ;
328+
329+ let result = get_evaluation_result ( & context_with_identity) ;
330+
331+ let flags = Flags :: from_evaluation_result (
332+ & result,
295333 self . analytics_processor . clone ( ) ,
296334 self . options . default_flag_handler ,
297- Some ( & identity. composite_key ( ) ) ,
298335 ) ;
299336 return Ok ( flags) ;
300337 }
301338
302- fn get_identity_model (
303- & self ,
304- environment : & Environment ,
305- identities_with_overrides_by_identifier : & HashMap < String , Identity > ,
306- identifier : & str ,
307- traits : Vec < Trait > ,
308- ) -> Result < Identity , error:: Error > {
309- let mut identity: Identity ;
310-
311- if identities_with_overrides_by_identifier. contains_key ( identifier) {
312- identity = identities_with_overrides_by_identifier. get ( identifier) . unwrap ( ) . clone ( ) ;
313- } else {
314- identity = Identity :: new ( identifier. to_string ( ) , environment. api_key . clone ( ) ) ;
315- }
316-
317- identity. identity_traits = traits;
318- return Ok ( identity. to_owned ( ) )
319- }
320339 fn get_identity_flags_from_api (
321340 & self ,
322341 identifier : & str ,
@@ -396,9 +415,10 @@ fn update_environment(
396415 & client,
397416 environment_url. clone ( ) ,
398417 ) ?) ;
399- for identity in & environment. as_ref ( ) . unwrap ( ) . identity_overrides {
400- data. identities_with_overrides_by_identifier . insert ( identity. identifier . clone ( ) , identity. clone ( ) ) ;
401- }
418+
419+ let eval_context = environment_to_context ( environment. as_ref ( ) . unwrap ( ) . clone ( ) ) ;
420+ data. evaluation_context = Some ( eval_context) ;
421+
402422 data. environment = environment;
403423 return Ok ( ( ) ) ;
404424}
0 commit comments