bitwarden_vault/cipher/attachment_client/
renew.rs1use bitwarden_core::{ApiError, MissingFieldError};
2use bitwarden_error::bitwarden_error;
3use thiserror::Error;
4
5use crate::{AttachmentsClient, CipherId};
6
7#[allow(missing_docs)]
8#[bitwarden_error(flat)]
9#[derive(Debug, Error)]
10pub enum CipherRenewFileUploadUrlError {
11 #[error(transparent)]
12 Api(#[from] ApiError),
13 #[error(transparent)]
14 MissingField(#[from] MissingFieldError),
15}
16
17#[bitwarden_ffi::wasm_export]
18impl AttachmentsClient {
19 pub async fn renew_file_upload_url(
22 &self,
23 cipher_id: CipherId,
24 attachment_id: String,
25 ) -> Result<String, CipherRenewFileUploadUrlError> {
26 let response = self
27 .api_configurations
28 .api_client
29 .ciphers_api()
30 .renew_file_upload_url(cipher_id.into(), &attachment_id)
31 .await?;
32
33 response.url.ok_or_else(|| MissingFieldError("url").into())
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use std::sync::Arc;
40
41 use bitwarden_api_api::{apis::ApiClient, models::AttachmentUploadDataResponseModel};
42 use bitwarden_core::{client::ApiConfigurations, key_management::KeySlotIds};
43 use bitwarden_crypto::KeyStore;
44 use reqwest::StatusCode;
45
46 use super::*;
47
48 const TEST_CIPHER_ID: &str = "5faa9684-c793-4a2d-8a12-b33900187097";
49 const TEST_ATTACHMENT_ID: &str = "uf7bkexzag04d3cw04jsbqqkbpbwhxs0";
50 const TEST_RENEW_URL: &str = "http://localhost:4000/attachments/test/renewed";
51
52 fn client_with_api(api_client: ApiClient) -> AttachmentsClient {
53 AttachmentsClient {
54 key_store: KeyStore::<KeySlotIds>::default(),
55 api_configurations: Arc::new(ApiConfigurations::from_api_client(api_client)),
56 repository: None,
57 http_client: reqwest::Client::new(),
58 }
59 }
60
61 #[tokio::test]
62 async fn returns_url_from_api_response() {
63 let api_client = ApiClient::new_mocked(|mock| {
64 mock.ciphers_api
65 .expect_renew_file_upload_url()
66 .returning(|id, attachment_id| {
67 assert_eq!(&id.to_string(), TEST_CIPHER_ID);
68 assert_eq!(attachment_id, TEST_ATTACHMENT_ID);
69 Ok(AttachmentUploadDataResponseModel {
70 url: Some(TEST_RENEW_URL.to_string()),
71 ..Default::default()
72 })
73 });
74 });
75
76 let client = client_with_api(api_client);
77 let cipher_id: CipherId = TEST_CIPHER_ID.parse().unwrap();
78 let url = client
79 .renew_file_upload_url(cipher_id, TEST_ATTACHMENT_ID.to_string())
80 .await
81 .unwrap();
82
83 assert_eq!(url, TEST_RENEW_URL);
84 }
85
86 #[tokio::test]
87 async fn returns_missing_field_when_response_has_no_url() {
88 let api_client = ApiClient::new_mocked(|mock| {
89 mock.ciphers_api
90 .expect_renew_file_upload_url()
91 .returning(|_id, _attachment_id| {
92 Ok(AttachmentUploadDataResponseModel {
93 url: None,
94 ..Default::default()
95 })
96 });
97 });
98
99 let client = client_with_api(api_client);
100 let cipher_id: CipherId = TEST_CIPHER_ID.parse().unwrap();
101 let err = client
102 .renew_file_upload_url(cipher_id, TEST_ATTACHMENT_ID.to_string())
103 .await
104 .unwrap_err();
105
106 assert!(matches!(
107 err,
108 CipherRenewFileUploadUrlError::MissingField(_)
109 ));
110 }
111
112 #[tokio::test]
113 async fn propagates_api_errors() {
114 let api_client = ApiClient::new_mocked(|mock| {
115 mock.ciphers_api
116 .expect_renew_file_upload_url()
117 .returning(|_id, _attachment_id| {
118 Err(bitwarden_api_api::ApiError::Response(
119 bitwarden_api_api::ResponseContent {
120 status: StatusCode::INTERNAL_SERVER_ERROR,
121 message: "boom".to_string(),
122 },
123 ))
124 });
125 });
126
127 let client = client_with_api(api_client);
128 let cipher_id: CipherId = TEST_CIPHER_ID.parse().unwrap();
129 let err = client
130 .renew_file_upload_url(cipher_id, TEST_ATTACHMENT_ID.to_string())
131 .await
132 .unwrap_err();
133
134 assert!(matches!(err, CipherRenewFileUploadUrlError::Api(_)));
135 }
136}