reductstore/api/entry/
remove_entry.rs

1// Copyright 2023-2024 ReductSoftware UG
2// Licensed under the Business Source License 1.1
3
4use crate::api::middleware::check_permissions;
5use crate::api::{Components, HttpError};
6use crate::auth::policy::WriteAccessPolicy;
7use std::collections::HashMap;
8
9use axum::extract::{Path, State};
10use axum_extra::headers::HeaderMap;
11
12use std::sync::Arc;
13
14// DELETE /b/:bucket_name/:entry_name
15pub(crate) async fn remove_entry(
16    State(components): State<Arc<Components>>,
17    Path(path): Path<HashMap<String, String>>,
18    headers: HeaderMap,
19) -> Result<(), HttpError> {
20    let bucket_name = path.get("bucket_name").unwrap();
21    let entry_name = path.get("entry_name").unwrap();
22
23    check_permissions(
24        &components,
25        &headers,
26        WriteAccessPolicy {
27            bucket: bucket_name,
28        },
29    )
30    .await?;
31
32    components
33        .storage
34        .get_bucket(bucket_name)?
35        .upgrade()?
36        .remove_entry(entry_name)
37        .await?;
38    Ok(())
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use crate::api::tests::{components, headers};
45    use reduct_base::error::ErrorCode;
46    use rstest::rstest;
47
48    #[rstest]
49    #[tokio::test]
50    async fn test_remove_entry(#[future] components: Arc<Components>, headers: HeaderMap) {
51        let components = components.await;
52        let path = HashMap::from_iter(vec![
53            ("bucket_name".to_string(), "bucket-1".to_string()),
54            ("entry_name".to_string(), "entry-1".to_string()),
55        ]);
56        remove_entry(State(Arc::clone(&components)), Path(path), headers)
57            .await
58            .unwrap();
59
60        assert_eq!(
61            components
62                .storage
63                .get_bucket("bucket-1")
64                .unwrap()
65                .upgrade_and_unwrap()
66                .get_entry("entry-1")
67                .err()
68                .unwrap()
69                .status(),
70            ErrorCode::NotFound
71        );
72    }
73
74    #[rstest]
75    #[tokio::test]
76    async fn test_remove_bucket_not_found(
77        #[future] components: Arc<Components>,
78        headers: HeaderMap,
79    ) {
80        let components = components.await;
81        let path = HashMap::from_iter(vec![
82            ("bucket_name".to_string(), "XXX".to_string()),
83            ("entry_name".to_string(), "entry-1".to_string()),
84        ]);
85        let err = remove_entry(State(Arc::clone(&components)), Path(path), headers)
86            .await
87            .err()
88            .unwrap();
89        assert_eq!(
90            err,
91            HttpError::new(ErrorCode::NotFound, "Bucket 'XXX' is not found",)
92        )
93    }
94
95    #[rstest]
96    #[tokio::test]
97    async fn test_remove_entry_not_found(
98        #[future] components: Arc<Components>,
99        headers: HeaderMap,
100    ) {
101        let components = components.await;
102        let path = HashMap::from_iter(vec![
103            ("bucket_name".to_string(), "bucket-1".to_string()),
104            ("entry_name".to_string(), "XXX".to_string()),
105        ]);
106        let err = remove_entry(State(Arc::clone(&components)), Path(path), headers)
107            .await
108            .err()
109            .unwrap();
110        assert_eq!(
111            err,
112            HttpError::new(
113                ErrorCode::NotFound,
114                "Entry 'XXX' not found in bucket 'bucket-1'",
115            )
116        )
117    }
118}