1use bitwarden_core::ApiError;
2use bitwarden_error::bitwarden_error;
3use bitwarden_state::repository::{Repository, RepositoryError};
4use thiserror::Error;
5#[cfg(feature = "wasm")]
6use wasm_bindgen::prelude::*;
7
8use crate::{Send, SendId, send_client::SendClient};
9
10#[allow(missing_docs)]
11#[bitwarden_error(flat)]
12#[derive(Debug, Error)]
13pub enum DeleteSendError {
14 #[error(transparent)]
15 Api(#[from] ApiError),
16 #[error(transparent)]
17 Repository(#[from] RepositoryError),
18}
19
20async fn delete_send<R: Repository<Send> + ?Sized>(
21 api_client: &bitwarden_api_api::apis::ApiClient,
22 repository: &R,
23 send_id: SendId,
24) -> Result<(), DeleteSendError> {
25 api_client.sends_api().delete(&send_id.to_string()).await?;
26
27 repository.remove(send_id).await?;
28
29 Ok(())
30}
31
32#[cfg_attr(feature = "wasm", wasm_bindgen)]
33impl SendClient {
34 pub async fn delete(&self, send_id: SendId) -> Result<(), DeleteSendError> {
36 let config = self.client.internal.get_api_configurations();
37 let repository = self.get_repository()?;
38
39 delete_send(&config.api_client, repository.as_ref(), send_id).await
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use bitwarden_api_api::apis::ApiClient;
46 use bitwarden_core::key_management::{KeySlotIds, SymmetricKeySlotId};
47 use bitwarden_crypto::{KeyStore, SymmetricKeyAlgorithm};
48 use bitwarden_test::MemoryRepository;
49 use uuid::uuid;
50
51 use super::*;
52 use crate::{AuthType, Send, SendId, SendTextView, SendType, SendView};
53
54 async fn make_store_with_send(
55 send_id: uuid::Uuid,
56 ) -> (KeyStore<KeySlotIds>, MemoryRepository<Send>) {
57 let store: KeyStore<KeySlotIds> = KeyStore::default();
58 {
59 let mut ctx = store.context_mut();
60 let local_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
61 ctx.persist_symmetric_key(local_key_id, SymmetricKeySlotId::User)
62 .unwrap();
63 }
64
65 let repository = MemoryRepository::<Send>::default();
66 let send_view = SendView {
67 id: None,
68 access_id: None,
69 name: "Test Send".to_string(),
70 notes: None,
71 key: None,
72 new_password: None,
73 has_password: false,
74 r#type: SendType::Text,
75 file: None,
76 text: Some(SendTextView {
77 text: Some("Secret text".to_string()),
78 hidden: false,
79 }),
80 max_access_count: None,
81 access_count: 0,
82 disabled: false,
83 hide_email: false,
84 revision_date: "2025-01-01T00:00:00Z".parse().unwrap(),
85 deletion_date: "2025-01-10T00:00:00Z".parse().unwrap(),
86 expiration_date: None,
87 emails: Vec::new(),
88 auth_type: AuthType::None,
89 };
90 let mut send = store.encrypt(send_view).unwrap();
91 send.id = Some(SendId::new(send_id));
92 repository.set(SendId::new(send_id), send).await.unwrap();
93
94 (store, repository)
95 }
96
97 #[tokio::test]
98 async fn test_delete_send() {
99 let send_id = uuid!("25afb11c-9c95-4db5-8bac-c21cb204a3f1");
100 let (_store, repository) = make_store_with_send(send_id).await;
101
102 let api_client = ApiClient::new_mocked(move |mock| {
103 mock.sends_api
104 .expect_delete()
105 .returning(move |_id| Ok(()))
106 .once();
107 });
108
109 let result = delete_send(&api_client, &repository, SendId::new(send_id)).await;
110
111 assert!(result.is_ok());
112 assert!(
113 repository
114 .get(SendId::new(send_id))
115 .await
116 .unwrap()
117 .is_none()
118 );
119 }
120
121 #[tokio::test]
122 async fn test_delete_send_http_error() {
123 let send_id = uuid!("25afb11c-9c95-4db5-8bac-c21cb204a3f1");
124 let (_store, repository) = make_store_with_send(send_id).await;
125
126 let api_client = ApiClient::new_mocked(move |mock| {
127 mock.sends_api
128 .expect_delete()
129 .returning(move |_id| {
130 Err(bitwarden_api_api::ApiError::Io(std::io::Error::other(
131 "Simulated error",
132 )))
133 })
134 .once();
135 });
136
137 let result = delete_send(&api_client, &repository, SendId::new(send_id)).await;
138
139 assert!(result.is_err());
140 assert!(matches!(result.unwrap_err(), DeleteSendError::Api(_)));
141 assert!(
143 repository
144 .get(SendId::new(send_id))
145 .await
146 .unwrap()
147 .is_some()
148 );
149 }
150}