flagsmith_flag_engine/
engine.rs1use 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
10pub 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
25pub 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
40pub 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
55pub 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 for fs in environment.feature_states.clone() {
83 feature_states.insert(fs.feature.clone(), fs);
84 }
85
86 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 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}