Skip to main content

bitwarden_sm/projects/
update.rs

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