bitwarden_sm/projects/
update.rs

1use 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    /// 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(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}