bitwarden_sm/projects/
update.rs1use bitwarden_api_api::models::ProjectUpdateRequestModel;
2use bitwarden_core::{Client, OrganizationId, key_management::SymmetricKeyId};
3use bitwarden_crypto::PrimitiveEncryptable;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7use validator::Validate;
8
9use crate::{
10 error::{SecretsManagerError, validate_only_whitespaces},
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(OrganizationId::new(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 = config
46 .api_client
47 .projects_api()
48 .update(input.id, project)
49 .await?;
50
51 ProjectResponse::process_response(res, &mut key_store.context())
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 async fn update_project(name: String) -> Result<ProjectResponse, SecretsManagerError> {
59 let input = ProjectPutRequest {
60 id: Uuid::new_v4(),
61 organization_id: Uuid::new_v4(),
62 name,
63 };
64
65 super::update_project(&Client::new(None), &input).await
66 }
67
68 #[tokio::test]
69 async fn test_update_project_request_name_empty_string() {
70 let response = update_project("".into()).await;
71 assert!(response.is_err());
72 assert_eq!(
73 response.err().unwrap().to_string(),
74 "name must not be empty"
75 );
76 }
77
78 #[tokio::test]
79 async fn test_update_project_request_name_all_whitespaces_space() {
80 let response = update_project(" ".into()).await;
81 assert!(response.is_err());
82 assert_eq!(
83 response.err().unwrap().to_string(),
84 "name must not contain only whitespaces"
85 );
86 }
87
88 #[tokio::test]
89 async fn test_update_project_request_name_all_whitespaces_tab() {
90 let response = update_project("\t".into()).await;
91 assert!(response.is_err());
92 assert_eq!(
93 response.err().unwrap().to_string(),
94 "name must not contain only whitespaces"
95 );
96 }
97
98 #[tokio::test]
99 async fn test_update_project_request_name_all_whitespaces_newline() {
100 let response = update_project("\n".into()).await;
101 assert!(response.is_err());
102 assert_eq!(
103 response.err().unwrap().to_string(),
104 "name must not contain only whitespaces"
105 );
106 }
107
108 #[tokio::test]
109 async fn test_update_project_request_name_all_whitespaces_combined() {
110 let response = update_project(" \t\n".into()).await;
111 assert!(response.is_err());
112 assert_eq!(
113 response.err().unwrap().to_string(),
114 "name must not contain only whitespaces"
115 );
116 }
117
118 #[tokio::test]
119 async fn test_update_project_request_name_501_character_length() {
120 let response = update_project("a".repeat(501)).await;
121 assert!(response.is_err());
122 assert_eq!(
123 response.err().unwrap().to_string(),
124 "name must not exceed 500 characters in length"
125 );
126 }
127}