bitwarden_sm/projects/
update.rs

1use 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    /// ID of the project to modify
19    pub id: Uuid,
20    /// Organization ID of the project to modify
21    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}