flagsmith_flag_engine/
engine.rs

1use super::environments;
2use super::error;
3use super::features;
4use super::identities;
5use super::segments::evaluator;
6use crate::features::Feature;
7use crate::features::FeatureState;
8use std::collections::HashMap;
9
10//Returns a vector of feature states for a given environment
11pub fn get_environment_feature_states(
12    environment: environments::Environment,
13) -> Vec<features::FeatureState> {
14    if environment.project.hide_disabled_flags {
15        return environment
16            .feature_states
17            .iter()
18            .filter(|fs| fs.enabled)
19            .map(|fs| fs.clone())
20            .collect();
21    }
22    return environment.feature_states;
23}
24
25// Returns a specific feature state for a given feature_name in a given environment
26// If exists else returns a FeatureStateNotFound error
27pub fn get_environment_feature_state(
28    environment: environments::Environment,
29    feature_name: &str,
30) -> Result<features::FeatureState, error::Error> {
31    let fs = environment
32        .feature_states
33        .iter()
34        .filter(|fs| fs.feature.name == feature_name)
35        .next()
36        .ok_or(error::Error::new(error::ErrorKind::FeatureStateNotFound));
37    return Ok(fs?.clone());
38}
39
40// Returns a vector of feature state models based on the environment, any matching
41// segments and any specific identity overrides
42pub fn get_identity_feature_states(
43    environment: &environments::Environment,
44    identity: &identities::Identity,
45    override_traits: Option<&Vec<identities::Trait>>,
46) -> Vec<features::FeatureState> {
47    let feature_states =
48        get_identity_feature_states_map(environment, identity, override_traits).into_values();
49    if environment.project.hide_disabled_flags {
50        return feature_states.filter(|fs| fs.enabled).collect();
51    }
52    return feature_states.collect();
53}
54
55// Returns a specific feature state based on the environment, any matching
56// segments and any specific identity overrides
57// If exists else returns a FeatureStateNotFound error
58pub fn get_identity_feature_state(
59    environment: &environments::Environment,
60    identity: &identities::Identity,
61    feature_name: &str,
62    override_traits: Option<&Vec<identities::Trait>>,
63) -> Result<features::FeatureState, error::Error> {
64    let feature_states =
65        get_identity_feature_states_map(environment, identity, override_traits).into_values();
66    let fs = feature_states
67        .filter(|fs| fs.feature.name == feature_name)
68        .next()
69        .ok_or(error::Error::new(error::ErrorKind::FeatureStateNotFound));
70
71    return Ok(fs?.clone());
72}
73
74fn get_identity_feature_states_map(
75    environment: &environments::Environment,
76    identity: &identities::Identity,
77    override_traits: Option<&Vec<identities::Trait>>,
78) -> HashMap<Feature, FeatureState> {
79    let mut feature_states: HashMap<Feature, FeatureState> = HashMap::new();
80
81    // Get feature states from the environment
82    for fs in environment.feature_states.clone() {
83        feature_states.insert(fs.feature.clone(), fs);
84    }
85
86    // Override with any feature states defined by matching segments
87    let identity_segments =
88        evaluator::get_identity_segments(environment, identity, override_traits);
89    for matching_segments in identity_segments {
90        for feature_state in matching_segments.feature_states {
91            let existing = feature_states.get(&feature_state.feature);
92            if existing.is_some() {
93                if existing.unwrap().is_higher_segment_priority(&feature_state) {
94                    continue;
95                }
96            }
97            feature_states.insert(feature_state.feature.clone(), feature_state);
98        }
99    }
100    // Override with any feature states defined directly the identity
101    for feature_state in identity.identity_features.clone() {
102        feature_states.insert(feature_state.feature.clone(), feature_state);
103    }
104    return feature_states;
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    static IDENTITY_JSON: &str = r#"{
111            "identifier": "test_user",
112            "environment_api_key": "test_api_key",
113            "created_date": "2022-03-02T12:31:05.309861",
114            "identity_features": [],
115            "identity_traits": [],
116            "identity_uuid":""
117        }"#;
118    static ENVIRONMENT_JSON: &str = r#"
119        {
120 "api_key": "test_key",
121 "project": {
122  "name": "Test project",
123  "organisation": {
124   "feature_analytics": false,
125   "name": "Test Org",
126   "id": 1,
127   "persist_trait_data": true,
128   "stop_serving_flags": false
129  },
130  "id": 1,
131  "hide_disabled_flags": true,
132  "segments": []
133 },
134 "segment_overrides": [],
135 "id": 1,
136 "feature_states": [
137  {
138   "multivariate_feature_state_values": [],
139   "feature_state_value": true,
140   "django_id": 1,
141   "feature": {
142    "name": "feature1",
143    "type": null,
144    "id": 1
145   },
146   "enabled": false
147  },
148  {
149   "multivariate_feature_state_values": [],
150   "feature_state_value": null,
151   "django_id": 2,
152   "feature": {
153    "name": "feature_2",
154    "type": null,
155    "id": 2
156   },
157   "enabled": true
158  }
159 ]
160}"#;
161
162    #[test]
163    fn get_environment_feature_states_only_return_enabled_fs_if_hide_disabled_flags_is_true() {
164        let environment: environments::Environment =
165            serde_json::from_str(ENVIRONMENT_JSON).unwrap();
166
167        let environment_feature_states = get_environment_feature_states(environment);
168        assert_eq!(environment_feature_states.len(), 1);
169        assert_eq!(environment_feature_states[0].django_id.unwrap(), 2);
170    }
171
172    #[test]
173    fn get_environment_feature_state_returns_correct_feature_state() {
174        let environment: environments::Environment =
175            serde_json::from_str(ENVIRONMENT_JSON).unwrap();
176        let feature_name = "feature_2";
177        let feature_state = get_environment_feature_state(environment, feature_name).unwrap();
178        assert_eq!(feature_state.feature.name, feature_name)
179    }
180
181    #[test]
182    fn get_environment_feature_state_returns_error_if_feature_state_does_not_exists() {
183        let environment: environments::Environment =
184            serde_json::from_str(ENVIRONMENT_JSON).unwrap();
185        let feature_name = "feature_that_does_not_exists";
186        let err = get_environment_feature_state(environment, feature_name)
187            .err()
188            .unwrap();
189        assert_eq!(err.kind, error::ErrorKind::FeatureStateNotFound)
190    }
191
192    #[test]
193    fn get_identity_feature_state_returns_correct_feature_state() {
194        let environment: environments::Environment =
195            serde_json::from_str(ENVIRONMENT_JSON).unwrap();
196        let feature_name = "feature_2";
197        let identity: identities::Identity = serde_json::from_str(IDENTITY_JSON).unwrap();
198        let feature_state =
199            get_identity_feature_state(&environment, &identity, feature_name, None).unwrap();
200        assert_eq!(feature_state.feature.name, feature_name)
201    }
202    #[test]
203    fn get_identity_feature_state_returns_error_if_feature_state_does_not_exists() {
204        let environment: environments::Environment =
205            serde_json::from_str(ENVIRONMENT_JSON).unwrap();
206        let feature_name = "feature_that_does_not_exists";
207        let identity: identities::Identity = serde_json::from_str(IDENTITY_JSON).unwrap();
208        let err = get_identity_feature_state(&environment, &identity, feature_name, None)
209            .err()
210            .unwrap();
211        assert_eq!(err.kind, error::ErrorKind::FeatureStateNotFound)
212    }
213}