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