Skip to main content

bitwarden_vault/cipher/cipher_client/admin/
delete_attachment.rs

1use bitwarden_core::{ApiError, MissingFieldError};
2use bitwarden_error::bitwarden_error;
3use thiserror::Error;
4#[cfg(feature = "wasm")]
5use wasm_bindgen::prelude::wasm_bindgen;
6
7use crate::{
8    Cipher, CipherId, VaultParseError, cipher::cipher::PartialCipher,
9    cipher_client::admin::CipherAdminClient,
10};
11
12#[allow(missing_docs)]
13#[bitwarden_error(flat)]
14#[derive(Debug, Error)]
15pub enum DeleteAttachmentAdminError {
16    #[error(transparent)]
17    Api(#[from] ApiError),
18    #[error(transparent)]
19    MissingField(#[from] MissingFieldError),
20    #[error(transparent)]
21    VaultParse(#[from] VaultParseError),
22}
23
24impl<T> From<bitwarden_api_api::apis::Error<T>> for DeleteAttachmentAdminError {
25    fn from(value: bitwarden_api_api::apis::Error<T>) -> Self {
26        Self::Api(value.into())
27    }
28}
29
30async fn delete_attachment(
31    cipher_id: CipherId,
32    attachment_id: &str,
33    api_client: &bitwarden_api_api::apis::ApiClient,
34) -> Result<Cipher, DeleteAttachmentAdminError> {
35    let api = api_client.ciphers_api();
36    let response = api
37        .delete_attachment_admin(cipher_id.into(), attachment_id)
38        .await?;
39
40    let cipher_response = response
41        .cipher
42        .map(|c| *c)
43        .ok_or(MissingFieldError("cipher"))?;
44    Ok(cipher_response.merge_with_cipher(None)?)
45}
46
47#[cfg_attr(feature = "wasm", wasm_bindgen)]
48impl CipherAdminClient {
49    /// Deletes an attachment from a cipher using the admin endpoint.
50    /// Affects server data only, does not modify local state.
51    pub async fn delete_attachment(
52        &self,
53        cipher_id: CipherId,
54        attachment_id: String,
55    ) -> Result<Cipher, DeleteAttachmentAdminError> {
56        delete_attachment(
57            cipher_id,
58            &attachment_id,
59            &self.api_configurations.api_client,
60        )
61        .await
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use bitwarden_api_api::models::{CipherMiniResponseModel, DeleteAttachmentResponseModel};
68
69    use super::*;
70
71    const TEST_CIPHER_ID: &str = "5faa9684-c793-4a2d-8a12-b33900187097";
72    const TEST_ATTACHMENT_ID: &str = "uf7bkexzag04d3cw04jsbqqkbpbwhxs0";
73
74    #[tokio::test]
75    async fn test_delete_attachment_as_admin() {
76        let result = delete_attachment(
77            TEST_CIPHER_ID.parse().unwrap(),
78            TEST_ATTACHMENT_ID,
79            &bitwarden_api_api::apis::ApiClient::new_mocked(|mock| {
80                mock.ciphers_api.expect_delete_attachment_admin().returning(
81                    move |id, attachment_id| {
82                        assert_eq!(&id.to_string(), TEST_CIPHER_ID);
83                        assert_eq!(attachment_id, TEST_ATTACHMENT_ID);
84                        Ok(DeleteAttachmentResponseModel {
85                            object: None,
86                            cipher: Some(Box::new(CipherMiniResponseModel {
87                                id: Some(TEST_CIPHER_ID.try_into().unwrap()),
88                                name: Some("2.pMS6/icTQABtulw52pq2lg==|XXbxKxDTh+mWiN1HjH2N1w==|Q6PkuT+KX/axrgN9ubD5Ajk2YNwxQkgs3WJM0S0wtG8=".to_string()),
89                                r#type: Some(bitwarden_api_api::models::CipherType::Login),
90                                creation_date: Some("2024-05-31T11:20:58.4566667Z".to_string()),
91                                revision_date: Some("2024-05-31T11:20:58.4566667Z".to_string()),
92                                attachments: None,
93                                ..Default::default()
94                            })),
95                        })
96                    },
97                );
98            }),
99        )
100        .await
101        .unwrap();
102
103        assert!(result.attachments.is_none());
104    }
105}