Skip to content

Commit d0ad003

Browse files
authored
feat: support prerequisite relation data in all_flags_detail (#99)
This PR updates the `FlagDetail` struct returned by `all_flags_detail` to contain prerequisite relations for flags.
1 parent de3ee23 commit d0ad003

File tree

4 files changed

+443
-12
lines changed

4 files changed

+443
-12
lines changed

contract-tests/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ async fn status() -> impl Responder {
109109
"omit-anonymous-contexts".to_string(),
110110
"migrations".to_string(),
111111
"event-sampling".to_string(),
112+
"client-prereq-events".to_string(),
112113
],
113114
})
114115
}

launchdarkly-server-sdk/src/client.rs

Lines changed: 209 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -839,13 +839,13 @@ impl Client {
839839

840840
#[cfg(test)]
841841
mod tests {
842+
use assert_json_diff::assert_json_eq;
842843
use crossbeam_channel::Receiver;
843844
use eval::{ContextBuilder, MultiContextBuilder};
844845
use futures::FutureExt;
845846
use hyper::client::HttpConnector;
846847
use launchdarkly_server_sdk_evaluation::Reason;
847848
use std::collections::HashMap;
848-
849849
use tokio::time::Instant;
850850

851851
use crate::data_source::MockDataSource;
@@ -855,8 +855,8 @@ mod tests {
855855
use crate::events::processor_builders::EventProcessorBuilder;
856856
use crate::stores::store_types::{PatchTarget, StorageItem};
857857
use crate::test_common::{
858-
self, basic_flag, basic_flag_with_prereq, basic_int_flag, basic_migration_flag,
859-
basic_off_flag,
858+
self, basic_flag, basic_flag_with_prereq, basic_flag_with_prereqs_and_visibility,
859+
basic_flag_with_visibility, basic_int_flag, basic_migration_flag, basic_off_flag,
860860
};
861861
use crate::{ConfigBuilder, MigratorBuilder, Operation, Origin};
862862
use test_case::test_case;
@@ -965,6 +965,212 @@ mod tests {
965965
));
966966
}
967967

968+
#[test]
969+
fn all_flags_detail_is_invalid_when_offline() {
970+
let (client, _event_rx) = make_mocked_offline_client();
971+
client.start_with_default_executor();
972+
973+
let context = ContextBuilder::new("bob")
974+
.build()
975+
.expect("Failed to create context");
976+
977+
let all_flags = client.all_flags_detail(&context, FlagDetailConfig::new());
978+
assert_json_eq!(all_flags, json!({"$valid": false, "$flagsState" : {}}));
979+
}
980+
981+
#[test]
982+
fn all_flags_detail_is_invalid_when_not_initialized() {
983+
let (client, _event_rx) = make_mocked_client();
984+
985+
let context = ContextBuilder::new("bob")
986+
.build()
987+
.expect("Failed to create context");
988+
989+
let all_flags = client.all_flags_detail(&context, FlagDetailConfig::new());
990+
assert_json_eq!(all_flags, json!({"$valid": false, "$flagsState" : {}}));
991+
}
992+
993+
#[test]
994+
fn all_flags_detail_returns_flag_states() {
995+
let (client, _event_rx) = make_mocked_client();
996+
client.start_with_default_executor();
997+
client
998+
.data_store
999+
.write()
1000+
.upsert(
1001+
"myFlag1",
1002+
PatchTarget::Flag(StorageItem::Item(basic_flag("myFlag1"))),
1003+
)
1004+
.expect("patch should apply");
1005+
client
1006+
.data_store
1007+
.write()
1008+
.upsert(
1009+
"myFlag2",
1010+
PatchTarget::Flag(StorageItem::Item(basic_flag("myFlag2"))),
1011+
)
1012+
.expect("patch should apply");
1013+
let context = ContextBuilder::new("bob")
1014+
.build()
1015+
.expect("Failed to create context");
1016+
1017+
let all_flags = client.all_flags_detail(&context, FlagDetailConfig::new());
1018+
1019+
client.close();
1020+
1021+
assert_json_eq!(
1022+
all_flags,
1023+
json!({
1024+
"myFlag1": true,
1025+
"myFlag2": true,
1026+
"$flagsState": {
1027+
"myFlag1": {
1028+
"version": 42,
1029+
"variation": 1
1030+
},
1031+
"myFlag2": {
1032+
"version": 42,
1033+
"variation": 1
1034+
},
1035+
},
1036+
"$valid": true
1037+
})
1038+
);
1039+
}
1040+
1041+
#[test]
1042+
fn all_flags_detail_returns_prerequisite_relations() {
1043+
let (client, _event_rx) = make_mocked_client();
1044+
client.start_with_default_executor();
1045+
client
1046+
.data_store
1047+
.write()
1048+
.upsert(
1049+
"prereq1",
1050+
PatchTarget::Flag(StorageItem::Item(basic_flag("prereq1"))),
1051+
)
1052+
.expect("patch should apply");
1053+
client
1054+
.data_store
1055+
.write()
1056+
.upsert(
1057+
"prereq2",
1058+
PatchTarget::Flag(StorageItem::Item(basic_flag("prereq2"))),
1059+
)
1060+
.expect("patch should apply");
1061+
1062+
client
1063+
.data_store
1064+
.write()
1065+
.upsert(
1066+
"toplevel",
1067+
PatchTarget::Flag(StorageItem::Item(basic_flag_with_prereqs_and_visibility(
1068+
"toplevel",
1069+
&["prereq1", "prereq2"],
1070+
false,
1071+
))),
1072+
)
1073+
.expect("patch should apply");
1074+
1075+
let context = ContextBuilder::new("bob")
1076+
.build()
1077+
.expect("Failed to create context");
1078+
1079+
let all_flags = client.all_flags_detail(&context, FlagDetailConfig::new());
1080+
1081+
client.close();
1082+
1083+
assert_json_eq!(
1084+
all_flags,
1085+
json!({
1086+
"prereq1": true,
1087+
"prereq2": true,
1088+
"toplevel": true,
1089+
"$flagsState": {
1090+
"toplevel": {
1091+
"version": 42,
1092+
"variation": 1,
1093+
"prerequisites": ["prereq1", "prereq2"]
1094+
},
1095+
"prereq1": {
1096+
"version": 42,
1097+
"variation": 1
1098+
},
1099+
"prereq2": {
1100+
"version": 42,
1101+
"variation": 1
1102+
},
1103+
},
1104+
"$valid": true
1105+
})
1106+
);
1107+
}
1108+
1109+
#[test]
1110+
fn all_flags_detail_returns_prerequisite_relations_when_not_visible_to_clients() {
1111+
let (client, _event_rx) = make_mocked_client();
1112+
client.start_with_default_executor();
1113+
client
1114+
.data_store
1115+
.write()
1116+
.upsert(
1117+
"prereq1",
1118+
PatchTarget::Flag(StorageItem::Item(basic_flag_with_visibility(
1119+
"prereq1", false,
1120+
))),
1121+
)
1122+
.expect("patch should apply");
1123+
client
1124+
.data_store
1125+
.write()
1126+
.upsert(
1127+
"prereq2",
1128+
PatchTarget::Flag(StorageItem::Item(basic_flag_with_visibility(
1129+
"prereq2", false,
1130+
))),
1131+
)
1132+
.expect("patch should apply");
1133+
1134+
client
1135+
.data_store
1136+
.write()
1137+
.upsert(
1138+
"toplevel",
1139+
PatchTarget::Flag(StorageItem::Item(basic_flag_with_prereqs_and_visibility(
1140+
"toplevel",
1141+
&["prereq1", "prereq2"],
1142+
true,
1143+
))),
1144+
)
1145+
.expect("patch should apply");
1146+
1147+
let context = ContextBuilder::new("bob")
1148+
.build()
1149+
.expect("Failed to create context");
1150+
1151+
let mut config = FlagDetailConfig::new();
1152+
config.client_side_only();
1153+
1154+
let all_flags = client.all_flags_detail(&context, config);
1155+
1156+
client.close();
1157+
1158+
assert_json_eq!(
1159+
all_flags,
1160+
json!({
1161+
"toplevel": true,
1162+
"$flagsState": {
1163+
"toplevel": {
1164+
"version": 42,
1165+
"variation": 1,
1166+
"prerequisites": ["prereq1", "prereq2"]
1167+
},
1168+
},
1169+
"$valid": true
1170+
})
1171+
);
1172+
}
1173+
9681174
#[test]
9691175
fn variation_tracks_events_correctly() {
9701176
let (client, event_rx) = make_mocked_client();

0 commit comments

Comments
 (0)