Skip to main content

bitwarden_vault/folder/
get_list.rs

1use bitwarden_crypto::CryptoError;
2use bitwarden_error::bitwarden_error;
3use bitwarden_state::repository::{RepositoryError, RepositoryOption};
4use thiserror::Error;
5
6use crate::{FolderId, FolderView, FoldersClient, ItemNotFoundError};
7
8#[allow(missing_docs)]
9#[bitwarden_error(flat)]
10#[derive(Debug, Error)]
11pub enum GetFolderError {
12    #[error(transparent)]
13    ItemNotFound(#[from] ItemNotFoundError),
14    #[error(transparent)]
15    Crypto(#[from] CryptoError),
16    #[error(transparent)]
17    Repository(#[from] RepositoryError),
18}
19
20#[bitwarden_ffi::wasm_export]
21impl FoldersClient {
22    /// Get a specific [crate::Folder] by its ID from state and decrypt it to a [FolderView].
23    pub async fn get(&self, folder_id: FolderId) -> Result<FolderView, GetFolderError> {
24        let folder = self
25            .repository
26            .require()?
27            .get(folder_id)
28            .await?
29            .ok_or(ItemNotFoundError)?;
30
31        Ok(self.key_store.decrypt(&folder)?)
32    }
33
34    /// Get all folders from state and decrypt them to a list of [FolderView].
35    pub async fn list(&self) -> Result<Vec<FolderView>, GetFolderError> {
36        let folders = self.repository.require()?.list().await?;
37        let views = self.key_store.decrypt_list(&folders)?;
38        Ok(views)
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use std::sync::Arc;
45
46    use bitwarden_api_api::apis::ApiClient;
47    use bitwarden_core::{
48        client::ApiConfigurations, key_management::create_test_crypto_with_user_key,
49    };
50    use bitwarden_crypto::{SymmetricCryptoKey, SymmetricKeyAlgorithm};
51    use bitwarden_test::MemoryRepository;
52    use uuid::uuid;
53
54    use super::*;
55    use crate::Folder;
56
57    fn create_client() -> FoldersClient {
58        let store = create_test_crypto_with_user_key(SymmetricCryptoKey::make(
59            SymmetricKeyAlgorithm::Aes256CbcHmac,
60        ));
61        let repository = Arc::new(MemoryRepository::<Folder>::default());
62
63        FoldersClient {
64            key_store: store,
65            api_configurations: Arc::new(ApiConfigurations::from_api_client(
66                ApiClient::new_mocked(|_| {}),
67            )),
68            repository: Some(repository),
69        }
70    }
71
72    fn make_folder(client: &FoldersClient, id: FolderId, name: &str) -> Folder {
73        client
74            .key_store
75            .encrypt(FolderView {
76                id: Some(id),
77                name: name.to_string(),
78                revision_date: "2025-01-01T00:00:00Z".parse().unwrap(),
79            })
80            .unwrap()
81    }
82
83    #[tokio::test]
84    async fn test_get_folder() {
85        let client = create_client();
86        let folder_id = FolderId::new(uuid!("25afb11c-9c95-4db5-8bac-c21cb204a3f1"));
87        let folder = make_folder(&client, folder_id, "Test Folder");
88
89        client
90            .repository
91            .as_ref()
92            .unwrap()
93            .set(folder_id, folder)
94            .await
95            .unwrap();
96
97        let result = client.get(folder_id).await.unwrap();
98
99        assert_eq!(
100            result,
101            FolderView {
102                id: Some(folder_id),
103                name: "Test Folder".to_string(),
104                revision_date: "2025-01-01T00:00:00Z".parse().unwrap(),
105            }
106        );
107    }
108
109    #[tokio::test]
110    async fn test_get_folder_not_found() {
111        let client = create_client();
112        let folder_id = FolderId::new(uuid!("25afb11c-9c95-4db5-8bac-c21cb204a3f1"));
113
114        let result = client.get(folder_id).await;
115
116        assert!(result.is_err());
117        assert!(matches!(
118            result.unwrap_err(),
119            GetFolderError::ItemNotFound(_)
120        ));
121    }
122
123    #[tokio::test]
124    async fn test_list_folders() {
125        let client = create_client();
126        let id_a = FolderId::new(uuid!("25afb11c-9c95-4db5-8bac-c21cb204a3f1"));
127        let id_b = FolderId::new(uuid!("35afb11c-9c95-4db5-8bac-c21cb204a3f2"));
128
129        let repository = client.repository.as_ref().unwrap();
130        repository
131            .set(id_a, make_folder(&client, id_a, "Folder A"))
132            .await
133            .unwrap();
134        repository
135            .set(id_b, make_folder(&client, id_b, "Folder B"))
136            .await
137            .unwrap();
138
139        let mut result = client.list().await.unwrap();
140        result.sort_by(|a, b| a.name.cmp(&b.name));
141
142        assert_eq!(result.len(), 2);
143        assert_eq!(result[0].name, "Folder A");
144        assert_eq!(result[1].name, "Folder B");
145    }
146
147    #[tokio::test]
148    async fn test_list_folders_empty() {
149        let client = create_client();
150
151        let result = client.list().await.unwrap();
152
153        assert!(result.is_empty());
154    }
155}