1+ use std:: cell:: RefCell ;
12use super :: stores:: store:: DataStore ;
23use serde:: Serialize ;
34
4- use launchdarkly_server_sdk_evaluation:: { evaluate, Context , FlagValue , Reason } ;
5+ use launchdarkly_server_sdk_evaluation:: { evaluate, Context , FlagValue , PrerequisiteEvent , PrerequisiteEventRecorder , Reason } ;
56use std:: collections:: HashMap ;
67use std:: time:: SystemTime ;
78
@@ -78,6 +79,9 @@ pub struct FlagState {
7879
7980 #[ serde( skip_serializing_if = "Option::is_none" ) ]
8081 debug_events_until_date : Option < u64 > ,
82+
83+ #[ serde( skip_serializing_if = "Vec::is_empty" ) ]
84+ prerequisites : Vec < String > ,
8185}
8286
8387/// FlagDetail is a snapshot of the state of multiple feature flags with regard to a specific user.
@@ -97,6 +101,27 @@ pub struct FlagDetail {
97101 valid : bool ,
98102}
99103
104+ struct DirectPrerequisiteRecorder {
105+ target_flag_key : String ,
106+ prerequisites : RefCell < Vec < String > > ,
107+ }
108+
109+ impl DirectPrerequisiteRecorder {
110+ pub fn new ( target_flag_key : impl Into < String > ) -> Self {
111+ Self {
112+ target_flag_key : target_flag_key. into ( ) ,
113+ prerequisites : RefCell :: new ( Vec :: new ( ) ) ,
114+ }
115+ }
116+ }
117+ impl PrerequisiteEventRecorder for DirectPrerequisiteRecorder {
118+ fn record ( & self , event : PrerequisiteEvent ) {
119+ if event. target_flag_key == self . target_flag_key {
120+ self . prerequisites . borrow_mut ( ) . push ( event. prerequisite_flag . key )
121+ }
122+ }
123+ }
124+
100125impl FlagDetail {
101126 /// Create a new empty instance of FlagDetail.
102127 pub fn new ( valid : bool ) -> Self {
@@ -118,7 +143,10 @@ impl FlagDetail {
118143 continue ;
119144 }
120145
121- let detail = evaluate ( store. to_store ( ) , & flag, context, None ) ;
146+ let event_recorder = DirectPrerequisiteRecorder :: new ( key. clone ( ) ) ;
147+
148+ let detail = evaluate ( store. to_store ( ) , & flag, context, Some ( & event_recorder) ) ;
149+
122150
123151 // Here we are applying the same logic used in EventFactory.new_feature_request_event
124152 // to determine whether the evaluation involved an experiment, in which case both
@@ -171,6 +199,7 @@ impl FlagDetail {
171199 track_events,
172200 track_reason,
173201 debug_events_until_date : flag. debug_events_until_date ,
202+ prerequisites : event_recorder. prerequisites . take ( ) ,
174203 } ,
175204 ) ;
176205 }
@@ -182,11 +211,12 @@ impl FlagDetail {
182211
183212#[ cfg( test) ]
184213mod tests {
214+ use assert_json_diff:: assert_json_eq;
185215 use crate :: evaluation:: FlagDetail ;
186216 use crate :: stores:: store:: DataStore ;
187217 use crate :: stores:: store:: InMemoryDataStore ;
188218 use crate :: stores:: store_types:: { PatchTarget , StorageItem } ;
189- use crate :: test_common:: basic_flag;
219+ use crate :: test_common:: { basic_flag, basic_flag_with_prereqs } ;
190220 use crate :: FlagDetailConfig ;
191221 use launchdarkly_server_sdk_evaluation:: ContextBuilder ;
192222
@@ -410,4 +440,57 @@ mod tests {
410440 serde_json:: to_string_pretty( & expected) . unwrap( ) ,
411441 ) ;
412442 }
443+
444+ #[ test]
445+ fn flag_prerequisites_should_be_exposed ( ) {
446+ let context = ContextBuilder :: new ( "bob" )
447+ . build ( )
448+ . expect ( "Failed to create context" ) ;
449+ let mut store = InMemoryDataStore :: new ( ) ;
450+
451+ let prereq1 = basic_flag ( "prereq1" ) ;
452+ let prereq2 = basic_flag ( "prereq2" ) ;
453+ let toplevel = basic_flag_with_prereqs ( "toplevel" , & [ "prereq1" , "prereq2" ] ) ;
454+
455+
456+ store
457+ . upsert ( "prereq1" , PatchTarget :: Flag ( StorageItem :: Item ( prereq1) ) )
458+ . expect ( "patch should apply" ) ;
459+
460+ store
461+ . upsert ( "prereq2" , PatchTarget :: Flag ( StorageItem :: Item ( prereq2) ) )
462+ . expect ( "patch should apply" ) ;
463+
464+ store
465+ . upsert ( "toplevel" , PatchTarget :: Flag ( StorageItem :: Item ( toplevel) ) )
466+ . expect ( "patch should apply" ) ;
467+
468+ let mut flag_detail = FlagDetail :: new ( true ) ;
469+ flag_detail. populate ( & store, & context, FlagDetailConfig :: new ( ) ) ;
470+
471+ let expected = json ! ( {
472+ "prereq1" : true ,
473+ "prereq2" : true ,
474+ "toplevel" : true ,
475+ "$flagsState" : {
476+ "toplevel" : {
477+ "version" : 42 ,
478+ "variation" : 1 ,
479+ "prerequisites" : [ "prereq1" , "prereq2" ]
480+ } ,
481+ "prereq2" : {
482+ "version" : 42 ,
483+ "variation" : 1
484+ } ,
485+ "prereq1" : {
486+ "version" : 42 ,
487+ "variation" : 1 ,
488+ } ,
489+ } ,
490+ "$valid" : true
491+ } ) ;
492+
493+ assert_json_eq ! ( expected, flag_detail) ;
494+
495+ }
413496}
0 commit comments