]> git.proxmox.com Git - proxmox.git/blob - proxmox-notify/src/api/group.rs
notify: api: add API for filters
[proxmox.git] / proxmox-notify / src / api / group.rs
1 use crate::api::ApiError;
2 use crate::group::{DeleteableGroupProperty, GroupConfig, GroupConfigUpdater, GROUP_TYPENAME};
3 use crate::Config;
4
5 /// Get all notification groups
6 ///
7 /// The caller is responsible for any needed permission checks.
8 /// Returns a list of all groups or an `ApiError` if the config is erroneous.
9 pub fn get_groups(config: &Config) -> Result<Vec<GroupConfig>, ApiError> {
10 config
11 .config
12 .convert_to_typed_array(GROUP_TYPENAME)
13 .map_err(|e| ApiError::internal_server_error("Could not fetch groups", Some(e.into())))
14 }
15
16 /// Get group with given `name`
17 ///
18 /// The caller is responsible for any needed permission checks.
19 /// Returns the endpoint or an `ApiError` if the group was not found.
20 pub fn get_group(config: &Config, name: &str) -> Result<GroupConfig, ApiError> {
21 config
22 .config
23 .lookup(GROUP_TYPENAME, name)
24 .map_err(|_| ApiError::not_found(format!("group '{name}' not found"), None))
25 }
26
27 /// Add a new group.
28 ///
29 /// The caller is responsible for any needed permission checks.
30 /// The caller also responsible for locking the configuration files.
31 /// Returns an `ApiError` if a group with the same name already exists, or
32 /// if the group could not be saved
33 pub fn add_group(config: &mut Config, group_config: &GroupConfig) -> Result<(), ApiError> {
34 if get_group(config, &group_config.name).is_ok() {
35 return Err(ApiError::bad_request(
36 format!("group '{}' already exists", group_config.name),
37 None,
38 ));
39 }
40
41 if group_config.endpoint.is_empty() {
42 return Err(ApiError::bad_request(
43 "group must contain at least one endpoint",
44 None,
45 ));
46 }
47
48 if let Some(filter) = &group_config.filter {
49 // Check if filter exists
50 super::filter::get_filter(config, filter)?;
51 }
52
53 check_if_endpoints_exist(config, &group_config.endpoint)?;
54
55 config
56 .config
57 .set_data(&group_config.name, GROUP_TYPENAME, group_config)
58 .map_err(|e| {
59 ApiError::internal_server_error(
60 format!("could not save group '{}'", group_config.name),
61 Some(e.into()),
62 )
63 })?;
64
65 Ok(())
66 }
67
68 /// Update existing group
69 ///
70 /// The caller is responsible for any needed permission checks.
71 /// The caller also responsible for locking the configuration files.
72 /// Returns an `ApiError` if the config could not be saved.
73 pub fn update_group(
74 config: &mut Config,
75 name: &str,
76 updater: &GroupConfigUpdater,
77 delete: Option<&[DeleteableGroupProperty]>,
78 digest: Option<&[u8]>,
79 ) -> Result<(), ApiError> {
80 super::verify_digest(config, digest)?;
81
82 let mut group = get_group(config, name)?;
83
84 if let Some(delete) = delete {
85 for deleteable_property in delete {
86 match deleteable_property {
87 DeleteableGroupProperty::Comment => group.comment = None,
88 DeleteableGroupProperty::Filter => group.filter = None,
89 }
90 }
91 }
92
93 if let Some(endpoints) = &updater.endpoint {
94 check_if_endpoints_exist(config, endpoints)?;
95 if endpoints.is_empty() {
96 return Err(ApiError::bad_request(
97 "group must contain at least one endpoint",
98 None,
99 ));
100 }
101 group.endpoint = endpoints.iter().map(Into::into).collect()
102 }
103
104 if let Some(comment) = &updater.comment {
105 group.comment = Some(comment.into());
106 }
107
108 if let Some(filter) = &updater.filter {
109 // Check if filter exists
110 let _ = super::filter::get_filter(config, filter)?;
111 group.filter = Some(filter.into());
112 }
113
114 config
115 .config
116 .set_data(name, GROUP_TYPENAME, &group)
117 .map_err(|e| {
118 ApiError::internal_server_error(
119 format!("could not save group '{name}'"),
120 Some(e.into()),
121 )
122 })?;
123
124 Ok(())
125 }
126
127 /// Delete existing group
128 ///
129 /// The caller is responsible for any needed permission checks.
130 /// The caller also responsible for locking the configuration files.
131 /// Returns an `ApiError` if the group does not exist.
132 pub fn delete_group(config: &mut Config, name: &str) -> Result<(), ApiError> {
133 // Check if the group exists
134 let _ = get_group(config, name)?;
135
136 config.config.sections.remove(name);
137
138 Ok(())
139 }
140
141 fn check_if_endpoints_exist(config: &Config, endpoints: &[String]) -> Result<(), ApiError> {
142 for endpoint in endpoints {
143 if !super::endpoint_exists(config, endpoint) {
144 return Err(ApiError::not_found(
145 format!("endoint '{endpoint}' does not exist"),
146 None,
147 ));
148 }
149 }
150
151 Ok(())
152 }
153
154 // groups cannot be empty, so only build the tests if we have the
155 // sendmail endpoint available
156 #[cfg(all(test, feature = "sendmail"))]
157 mod tests {
158 use super::*;
159 use crate::api::sendmail::tests::add_sendmail_endpoint_for_test;
160 use crate::api::test_helpers::*;
161
162 fn add_default_group(config: &mut Config) -> Result<(), ApiError> {
163 add_sendmail_endpoint_for_test(config, "test")?;
164
165 add_group(
166 config,
167 &GroupConfig {
168 name: "group1".into(),
169 endpoint: vec!["test".to_string()],
170 comment: None,
171 filter: None,
172 },
173 )?;
174
175 Ok(())
176 }
177
178 #[test]
179 fn test_add_group_fails_if_endpoint_does_not_exist() {
180 let mut config = empty_config();
181 assert!(add_group(
182 &mut config,
183 &GroupConfig {
184 name: "group1".into(),
185 endpoint: vec!["foo".into()],
186 comment: None,
187 filter: None,
188 },
189 )
190 .is_err());
191 }
192
193 #[test]
194 fn test_add_group() -> Result<(), ApiError> {
195 let mut config = empty_config();
196 assert!(add_default_group(&mut config).is_ok());
197 Ok(())
198 }
199
200 #[test]
201 fn test_update_group_fails_if_endpoint_does_not_exist() -> Result<(), ApiError> {
202 let mut config = empty_config();
203 add_default_group(&mut config)?;
204
205 assert!(update_group(
206 &mut config,
207 "group1",
208 &GroupConfigUpdater {
209 endpoint: Some(vec!["foo".into()]),
210 ..Default::default()
211 },
212 None,
213 None
214 )
215 .is_err());
216 Ok(())
217 }
218
219 #[test]
220 fn test_update_group_fails_if_digest_invalid() -> Result<(), ApiError> {
221 let mut config = empty_config();
222 add_default_group(&mut config)?;
223
224 assert!(update_group(
225 &mut config,
226 "group1",
227 &Default::default(),
228 None,
229 Some(&[0u8; 32])
230 )
231 .is_err());
232 Ok(())
233 }
234
235 #[test]
236 fn test_update_group() -> Result<(), ApiError> {
237 let mut config = empty_config();
238 add_default_group(&mut config)?;
239
240 assert!(update_group(
241 &mut config,
242 "group1",
243 &GroupConfigUpdater {
244 endpoint: None,
245 comment: Some("newcomment".into()),
246 filter: None
247 },
248 None,
249 None,
250 )
251 .is_ok());
252 let group = get_group(&config, "group1")?;
253 assert_eq!(group.comment, Some("newcomment".into()));
254
255 assert!(update_group(
256 &mut config,
257 "group1",
258 &Default::default(),
259 Some(&[DeleteableGroupProperty::Comment]),
260 None
261 )
262 .is_ok());
263 let group = get_group(&config, "group1")?;
264 assert_eq!(group.comment, None);
265
266 Ok(())
267 }
268
269 #[test]
270 fn test_group_delete() -> Result<(), ApiError> {
271 let mut config = empty_config();
272 add_default_group(&mut config)?;
273
274 assert!(delete_group(&mut config, "group1").is_ok());
275 assert!(delete_group(&mut config, "group1").is_err());
276
277 Ok(())
278 }
279 }