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