bitwarden_sm/projects/
update.rs1use bitwarden_api_api::models::ProjectUpdateRequestModel;
2use bitwarden_core::{key_management::SymmetricKeyId, Client};
3use bitwarden_crypto::Encryptable;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7use validator::Validate;
8
9use crate::{
10 error::{validate_only_whitespaces, SecretsManagerError},
11 projects::ProjectResponse,
12};
13
14#[allow(missing_docs)]
15#[derive(Serialize, Deserialize, Debug, JsonSchema, Validate)]
16#[serde(rename_all = "camelCase", deny_unknown_fields)]
17pub struct ProjectPutRequest {
18 pub id: Uuid,
20 pub organization_id: Uuid,
22 #[validate(length(min = 1, max = 500), custom(function = validate_only_whitespaces))]
23 pub name: String,
24}
25
26pub(crate) async fn update_project(
27 client: &Client,
28 input: &ProjectPutRequest,
29) -> Result<ProjectResponse, SecretsManagerError> {
30 input.validate()?;
31
32 let key_store = client.internal.get_key_store();
33 let key = SymmetricKeyId::Organization(input.organization_id);
34
35 let project = Some(ProjectUpdateRequestModel {
36 name: input
37 .name
38 .clone()
39 .trim()
40 .encrypt(&mut key_store.context(), key)?
41 .to_string(),
42 });
43
44 let config = client.internal.get_api_configurations().await;
45 let res =
46 bitwarden_api_api::apis::projects_api::projects_id_put(&config.api, input.id, project)
47 .await?;
48
49 ProjectResponse::process_response(res, &mut key_store.context())
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 async fn update_project(name: String) -> Result<ProjectResponse, SecretsManagerError> {
57 let input = ProjectPutRequest {
58 id: Uuid::new_v4(),
59 organization_id: Uuid::new_v4(),
60 name,
61 };
62
63 super::update_project(&Client::new(None), &input).await
64 }
65
66 #[tokio::test]
67 async fn test_update_project_request_name_empty_string() {
68 let response = update_project("".into()).await;
69 assert!(response.is_err());
70 assert_eq!(
71 response.err().unwrap().to_string(),
72 "name must not be empty"
73 );
74 }
75
76 #[tokio::test]
77 async fn test_update_project_request_name_all_whitespaces_space() {
78 let response = update_project(" ".into()).await;
79 assert!(response.is_err());
80 assert_eq!(
81 response.err().unwrap().to_string(),
82 "name must not contain only whitespaces"
83 );
84 }
85
86 #[tokio::test]
87 async fn test_update_project_request_name_all_whitespaces_tab() {
88 let response = update_project("\t".into()).await;
89 assert!(response.is_err());
90 assert_eq!(
91 response.err().unwrap().to_string(),
92 "name must not contain only whitespaces"
93 );
94 }
95
96 #[tokio::test]
97 async fn test_update_project_request_name_all_whitespaces_newline() {
98 let response = update_project("\n".into()).await;
99 assert!(response.is_err());
100 assert_eq!(
101 response.err().unwrap().to_string(),
102 "name must not contain only whitespaces"
103 );
104 }
105
106 #[tokio::test]
107 async fn test_update_project_request_name_all_whitespaces_combined() {
108 let response = update_project(" \t\n".into()).await;
109 assert!(response.is_err());
110 assert_eq!(
111 response.err().unwrap().to_string(),
112 "name must not contain only whitespaces"
113 );
114 }
115
116 #[tokio::test]
117 async fn test_update_project_request_name_501_character_length() {
118 let response = update_project("a".repeat(501)).await;
119 assert!(response.is_err());
120 assert_eq!(
121 response.err().unwrap().to_string(),
122 "name must not exceed 500 characters in length"
123 );
124 }
125}