Skip to content

Commit 7759ce7

Browse files
committed
Fix/segment priority: Add feature segment (#2)
* bump engine-test-data * fix: feature segment priority
1 parent 9307b45 commit 7759ce7

File tree

3 files changed

+92
-1
lines changed

3 files changed

+92
-1
lines changed

src/engine.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ fn get_identity_feature_states_map(
8888
evaluator::get_identity_segments(environment, identity, override_traits);
8989
for matching_segments in identity_segments {
9090
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+
}
9197
feature_states.insert(feature_state.feature.clone(), feature_state);
9298
}
9399
}

src/features/mod.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,20 @@ pub struct MultivariateFeatureStateValue {
2828
pub mv_fs_value_uuid: String,
2929
}
3030

31+
#[derive(Serialize, Deserialize, Clone, Debug)]
32+
pub struct FeatureSegment {
33+
pub priority: u32,
34+
}
35+
3136
#[derive(Serialize, Deserialize, Clone, Debug)]
3237
pub struct FeatureState {
3338
pub feature: Feature,
3439
pub enabled: bool,
3540
pub django_id: Option<u32>,
3641

42+
#[serde(skip_serializing_if = "Option::is_none")]
43+
pub feature_segment: Option<FeatureSegment>,
44+
3745
#[serde(default = "utils::get_uuid")]
3846
pub featurestate_uuid: String,
3947
pub multivariate_feature_state_values: Vec<MultivariateFeatureStateValue>,
@@ -51,6 +59,25 @@ impl FeatureState {
5159
};
5260
return value;
5361
}
62+
63+
// Returns `true` if `self` is higher segment priority than `other`
64+
// (i.e. has lower value for feature_segment.priority)
65+
// NOTE:
66+
// A segment will be considered higher priority only if:
67+
// 1. `other` does not have a feature segment(i.e: it is an environment feature state or it's a
68+
// feature state with feature segment but from an old document that does not have `feature_segment.priority`)
69+
// but `self` does.
70+
71+
// 2. `other` have a feature segment but with lower priority
72+
pub fn is_higher_segment_priority(&self, other: &FeatureState) -> bool {
73+
match &other.feature_segment {
74+
None if self.feature_segment.is_some() => true,
75+
Some(feature_segment) if self.feature_segment.is_some() => {
76+
self.feature_segment.as_ref().unwrap().priority < feature_segment.priority
77+
}
78+
_ => false,
79+
}
80+
}
5481
fn get_multivariate_value(&self, identity_id: &str) -> FlagsmithValue {
5582
let object_id = match self.django_id {
5683
Some(django_id) => django_id.to_string(),
@@ -144,6 +171,64 @@ mod tests {
144171
assert_eq!(given_json, feature_state_json)
145172
}
146173

174+
#[test]
175+
fn feature_state_is_higher_segment_priority() {
176+
// Given
177+
let feature_state_json = serde_json::json!(
178+
{
179+
"multivariate_feature_state_values": [],
180+
"feature_state_value": 1,
181+
"featurestate_uuid":"a6ff815f-63ed-4e72-99dc-9124c442ce4d",
182+
"django_id": 1,
183+
"feature": {
184+
"name": "feature1",
185+
"type": null,
186+
"id": 1
187+
},
188+
"enabled": false
189+
}
190+
);
191+
let mut feature_state_1: FeatureState =
192+
serde_json::from_value(feature_state_json.clone()).unwrap();
193+
let mut feature_state_2: FeatureState =
194+
serde_json::from_value(feature_state_json.clone()).unwrap();
195+
196+
// Firstly, since both fs do not have feature segment this should be false
197+
assert_eq!(
198+
feature_state_1.is_higher_segment_priority(&feature_state_2),
199+
false
200+
);
201+
assert_eq!(
202+
feature_state_2.is_higher_segment_priority(&feature_state_1),
203+
false
204+
);
205+
206+
// Now add feature_segment to feature_state_2
207+
feature_state_2.feature_segment = Some(FeatureSegment { priority: 1 });
208+
209+
// Since `feature_state_2` have a feature segment this should be false as well
210+
assert_eq!(
211+
feature_state_1.is_higher_segment_priority(&feature_state_2),
212+
false
213+
);
214+
// And, this true
215+
assert_eq!(
216+
feature_state_2.is_higher_segment_priority(&feature_state_1),
217+
true
218+
);
219+
220+
// Next, let's add a feature segment with higher priority to `feature_state_1`
221+
feature_state_1.feature_segment = Some(FeatureSegment { priority: 0 });
222+
assert_eq!(
223+
feature_state_1.is_higher_segment_priority(&feature_state_2),
224+
true
225+
);
226+
assert_eq!(
227+
feature_state_2.is_higher_segment_priority(&feature_state_1),
228+
false
229+
);
230+
}
231+
147232
#[rstest]
148233
#[case("2", "foo".to_string())] // Generated hash percentage 26
149234
#[case("8", "bar".to_string())] // Generated hash percentage 38

0 commit comments

Comments
 (0)