objectstore_server/
killswitches.rs1use std::collections::BTreeMap;
2
3use objectstore_service::id::ObjectContext;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Default, Deserialize, Serialize)]
8pub struct Killswitches(pub Vec<Killswitch>);
9
10impl Killswitches {
11 pub fn matches(&self, context: &ObjectContext) -> bool {
13 self.0.iter().any(|s| s.matches(context))
14 }
15}
16
17#[derive(Debug, PartialEq, Deserialize, Serialize)]
22pub struct Killswitch {
23 #[serde(default)]
27 pub usecase: Option<String>,
28
29 #[serde(default)]
34 pub scopes: BTreeMap<String, String>,
35}
36
37impl Killswitch {
38 pub fn matches(&self, context: &ObjectContext) -> bool {
40 if let Some(ref switch_usecase) = self.usecase
41 && switch_usecase != &context.usecase
42 {
43 return false;
44 }
45
46 for (scope_name, scope_value) in &self.scopes {
47 match context.scopes.get_value(scope_name) {
48 Some(value) if value == scope_value => (),
49 _ => return false,
50 }
51 }
52
53 true
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use objectstore_types::scope::{Scope, Scopes};
60
61 use super::*;
62
63 #[test]
64 fn test_matches_empty() {
65 let switch = Killswitch {
66 usecase: None,
67 scopes: BTreeMap::new(),
68 };
69
70 let context = ObjectContext {
71 usecase: "any".to_string(),
72 scopes: Scopes::from_iter([Scope::create("any", "value").unwrap()]),
73 };
74
75 assert!(switch.matches(&context));
76 }
77
78 #[test]
79 fn test_matches_usecase() {
80 let switch = Killswitch {
81 usecase: Some("test".to_string()),
82 scopes: BTreeMap::new(),
83 };
84
85 let context = ObjectContext {
86 usecase: "test".to_string(),
87 scopes: Scopes::from_iter([Scope::create("any", "value").unwrap()]),
88 };
89 assert!(switch.matches(&context));
90
91 let context = ObjectContext {
93 usecase: "other".to_string(),
94 scopes: Scopes::from_iter([Scope::create("any", "value").unwrap()]),
95 };
96 assert!(!switch.matches(&context));
97 }
98
99 #[test]
100 fn test_matches_scopes() {
101 let switch = Killswitch {
102 usecase: None,
103 scopes: BTreeMap::from([
104 ("org".to_string(), "123".to_string()),
105 ("project".to_string(), "456".to_string()),
106 ]),
107 };
108
109 let context = ObjectContext {
111 usecase: "any".to_string(),
112 scopes: Scopes::from_iter([
113 Scope::create("org", "123").unwrap(),
114 Scope::create("project", "456").unwrap(),
115 Scope::create("extra", "789").unwrap(),
116 ]),
117 };
118 assert!(switch.matches(&context));
119
120 let context = ObjectContext {
122 usecase: "any".to_string(),
123 scopes: Scopes::from_iter([
124 Scope::create("org", "123").unwrap(),
125 Scope::create("project", "999").unwrap(),
126 ]),
127 };
128 assert!(!switch.matches(&context));
129
130 let context = ObjectContext {
132 usecase: "any".to_string(),
133 scopes: Scopes::from_iter([Scope::create("org", "123").unwrap()]),
134 };
135 assert!(!switch.matches(&context));
136 }
137
138 #[test]
139 fn test_matches_full() {
140 let switch = Killswitch {
141 usecase: Some("test".to_string()),
142 scopes: BTreeMap::from([("org".to_string(), "123".to_string())]),
143 };
144
145 let context = ObjectContext {
147 usecase: "test".to_string(),
148 scopes: Scopes::from_iter([Scope::create("org", "123").unwrap()]),
149 };
150 assert!(switch.matches(&context));
151
152 let context = ObjectContext {
154 usecase: "other".to_string(),
155 scopes: Scopes::from_iter([Scope::create("org", "123").unwrap()]),
156 };
157 assert!(!switch.matches(&context));
158
159 let context = ObjectContext {
161 usecase: "test".to_string(),
162 scopes: Scopes::from_iter([Scope::create("org", "999").unwrap()]),
163 };
164 assert!(!switch.matches(&context));
165 }
166}