From 2397874a06bc121123173642da00d843c7e5521d Mon Sep 17 00:00:00 2001 From: Christian <37517470+Christian-Toney@users.noreply.github.com> Date: Wed, 25 Feb 2026 15:03:48 -0500 Subject: [PATCH] Add feature: Update field values --- src/resources/field_value/mod.rs | 58 ++ .../field-values/{field_value_id}/mod.rs | 121 ++--- .../field-values/{field_value_id}/tests.rs | 513 +++++++++--------- 3 files changed, 361 insertions(+), 331 deletions(-) diff --git a/src/resources/field_value/mod.rs b/src/resources/field_value/mod.rs index c11384d..fb5b410 100644 --- a/src/resources/field_value/mod.rs +++ b/src/resources/field_value/mod.rs @@ -91,6 +91,35 @@ pub struct InitialFieldValueProperties { } +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct EditableFieldValueProperties { + + /// The field choice's text value, if applicable. + pub text_value: Option>, + + /// The field choice's number value, if applicable. + pub number_value: Option>, + + /// The field choice's boolean value, if applicable. + pub boolean_value: Option>, + + /// The field choice's date time value, if applicable. + pub timestamp_value: Option>>, + + /// The field choice's stakeholder type, if applicable. + pub stakeholder_type: Option>, + + /// The field choice's stakeholder user ID, if applicable. + pub stakeholder_user_id: Option>, + + /// The field choice's stakeholder group ID, if applicable. + pub stakeholder_group_id: Option>, + + /// The field choice's stakeholder app ID, if applicable. + pub stakeholder_app_id: Option> + +} + #[derive(Debug, Clone, Serialize, Deserialize, ToSql, FromSql)] pub struct FieldValue { @@ -298,6 +327,35 @@ impl FieldValue { } + /// Updates this field value and returns a new instance of the field value. + pub async fn update(&self, properties: &EditableFieldValueProperties, database_pool: &deadpool_postgres::Pool) -> Result { + + let query = String::from("UPDATE field_values SET "); + let parameter_boxes: Vec> = Vec::new(); + let database_client = database_pool.get().await?; + + database_client.query("BEGIN;", &[]).await?; + let (parameter_boxes, query) = slashstepql::add_parameter_to_query(parameter_boxes, query, "text_value", properties.text_value.as_ref()); + let (parameter_boxes, query) = slashstepql::add_parameter_to_query(parameter_boxes, query, "number_value", properties.number_value.as_ref()); + let (parameter_boxes, query) = slashstepql::add_parameter_to_query(parameter_boxes, query, "boolean_value", properties.boolean_value.as_ref()); + let (parameter_boxes, query) = slashstepql::add_parameter_to_query(parameter_boxes, query, "timestamp_value", properties.timestamp_value.as_ref()); + let (parameter_boxes, query) = slashstepql::add_parameter_to_query(parameter_boxes, query, "stakeholder_type", properties.stakeholder_type.as_ref()); + let (parameter_boxes, query) = slashstepql::add_parameter_to_query(parameter_boxes, query, "stakeholder_user_id", properties.stakeholder_user_id.as_ref()); + let (parameter_boxes, query) = slashstepql::add_parameter_to_query(parameter_boxes, query, "stakeholder_group_id", properties.stakeholder_group_id.as_ref()); + let (parameter_boxes, query) = slashstepql::add_parameter_to_query(parameter_boxes, query, "stakeholder_app_id", properties.stakeholder_app_id.as_ref()); + let (mut parameter_boxes, mut query) = (parameter_boxes, query); + + query.push_str(format!(" WHERE id = ${} RETURNING *;", parameter_boxes.len() + 1).as_str()); + parameter_boxes.push(Box::new(&self.id)); + let parameters: Vec<&(dyn ToSql + Sync)> = parameter_boxes.iter().map(|parameter| parameter.as_ref() as &(dyn ToSql + Sync)).collect(); + let row = database_client.query_one(&query, ¶meters).await?; + database_client.query("COMMIT;", &[]).await?; + + let field_value = Self::convert_from_row(&row); + return Ok(field_value); + + } + } impl DeletableResource for FieldValue { diff --git a/src/routes/field-values/{field_value_id}/mod.rs b/src/routes/field-values/{field_value_id}/mod.rs index 35b4ef5..ea20183 100644 --- a/src/routes/field-values/{field_value_id}/mod.rs +++ b/src/routes/field-values/{field_value_id}/mod.rs @@ -17,9 +17,9 @@ use crate::{ HTTPError, middleware::{authentication_middleware, http_request_middleware}, resources::{ - access_policy::{AccessPolicyResourceType, ActionPermissionLevel}, action_log_entry::{ActionLogEntry, ActionLogEntryActorType, ActionLogEntryTargetResourceType, InitialActionLogEntryProperties}, app::{App, EditableAppProperties}, app_authorization::AppAuthorization, field_value::FieldValue, http_transaction::HTTPTransaction, server_log_entry::ServerLogEntry, user::User + access_policy::{AccessPolicyResourceType, ActionPermissionLevel}, action_log_entry::{ActionLogEntry, ActionLogEntryActorType, ActionLogEntryTargetResourceType, InitialActionLogEntryProperties}, app::{App, EditableAppProperties}, app_authorization::AppAuthorization, field_value::{EditableFieldValueProperties, FieldValue}, http_transaction::HTTPTransaction, server_log_entry::ServerLogEntry, user::User }, - utilities::{reusable_route_handlers::delete_resource, route_handler_utilities::{AuthenticatedPrincipal, get_action_by_name, get_action_log_entry_expiration_timestamp, get_app_by_id, get_authenticated_principal, get_field_value_by_id, get_resource_by_id, get_resource_hierarchy, get_uuid_from_string, verify_delegate_permissions, verify_principal_permissions}} + utilities::{reusable_route_handlers::delete_resource, route_handler_utilities::{AuthenticatedPrincipal, get_action_by_name, get_action_log_entry_expiration_timestamp, get_app_by_id, get_authenticated_principal, get_field_value_by_id, get_request_body_without_json_rejection, get_resource_by_id, get_resource_hierarchy, get_uuid_from_string, verify_delegate_permissions, verify_principal_permissions}} }; #[path = "./access-policies/mod.rs"] @@ -99,94 +99,67 @@ async fn handle_delete_field_value_request( } -// /// PATCH /field-values/{field_value_id} -// /// -// /// Updates an app by its ID. -// #[axum::debug_handler] -// async fn handle_patch_app_request( -// Path(field_value_id): Path, -// State(state): State, -// Extension(http_transaction): Extension>, -// Extension(authenticated_user): Extension>>, -// Extension(authenticated_app): Extension>>, -// Extension(authenticated_app_authorization): Extension>>, -// body: Result, JsonRejection> -// ) -> Result, HTTPError> { - -// let http_transaction = http_transaction.clone(); - -// ServerLogEntry::trace("Verifying request body...", Some(&http_transaction.id), &state.database_pool).await.ok(); -// let updated_app_properties = match body { - -// Ok(updated_app_properties) => updated_app_properties, - -// Err(error) => { - -// let http_error = match error { - -// JsonRejection::JsonDataError(error) => HTTPError::BadRequestError(Some(error.to_string())), - -// JsonRejection::JsonSyntaxError(_) => HTTPError::BadRequestError(Some(format!("Failed to parse request body. Ensure the request body is valid JSON."))), - -// JsonRejection::MissingJsonContentType(_) => HTTPError::BadRequestError(Some(format!("Missing request body content type. It should be \"application/json\"."))), - -// JsonRejection::BytesRejection(error) => HTTPError::InternalServerError(Some(format!("Failed to parse request body: {:?}", error))), - -// _ => HTTPError::InternalServerError(Some(error.to_string())) - -// }; - -// ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), &state.database_pool).await.ok(); -// return Err(http_error); - -// } - -// }; +/// PATCH /field-values/{field_value_id} +/// +/// Updates a field value by its ID. +#[axum::debug_handler] +async fn handle_patch_field_value_request( + Path(field_value_id): Path, + State(state): State, + Extension(http_transaction): Extension>, + Extension(authenticated_user): Extension>>, + Extension(authenticated_app): Extension>>, + Extension(authenticated_app_authorization): Extension>>, + body: Result, JsonRejection> +) -> Result, HTTPError> { -// let original_target_field = get_app_by_id(&field_value_id, &http_transaction, &state.database_pool).await?; -// let resource_hierarchy = get_resource_hierarchy(&original_target_field, &AccessPolicyResourceType::App, &original_target_field.id, &http_transaction, &state.database_pool).await?; -// let update_access_policy_action = get_action_by_name("apps.update", &http_transaction, &state.database_pool).await?; -// verify_delegate_permissions(authenticated_app_authorization.as_ref().map(|app_authorization| &app_authorization.id), &update_access_policy_action.id, &http_transaction.id, &ActionPermissionLevel::User, &state.database_pool).await?; -// let authenticated_principal = get_authenticated_principal(authenticated_user.as_ref(), authenticated_app.as_ref())?; -// verify_principal_permissions(&authenticated_principal, &update_access_policy_action, &resource_hierarchy, &http_transaction, &ActionPermissionLevel::User, &state.database_pool).await?; + let http_transaction = http_transaction.clone(); + let updated_field_value_properties = get_request_body_without_json_rejection(body, &http_transaction, &state.database_pool).await?; + let field_value_id = get_uuid_from_string(&field_value_id, "field value", &http_transaction, &state.database_pool).await?; + let original_target_field_value = get_field_value_by_id(&field_value_id, &http_transaction, &state.database_pool).await?; + let resource_hierarchy = get_resource_hierarchy(&original_target_field_value, &AccessPolicyResourceType::FieldValue, &original_target_field_value.id, &http_transaction, &state.database_pool).await?; + let update_access_policy_action = get_action_by_name("fieldValues.update", &http_transaction, &state.database_pool).await?; + verify_delegate_permissions(authenticated_app_authorization.as_ref().map(|app_authorization| &app_authorization.id), &update_access_policy_action.id, &http_transaction.id, &ActionPermissionLevel::User, &state.database_pool).await?; + let authenticated_principal = get_authenticated_principal(authenticated_user.as_ref(), authenticated_app.as_ref())?; + verify_principal_permissions(&authenticated_principal, &update_access_policy_action, &resource_hierarchy, &http_transaction, &ActionPermissionLevel::User, &state.database_pool).await?; -// ServerLogEntry::trace(&format!("Updating authenticated_app {}...", original_target_field.id), Some(&http_transaction.id), &state.database_pool).await.ok(); -// let updated_target_action = match original_target_field.update(&updated_app_properties, &state.database_pool).await { + ServerLogEntry::trace(&format!("Updating field value {}...", original_target_field_value.id), Some(&http_transaction.id), &state.database_pool).await.ok(); + let updated_target_field_value = match original_target_field_value.update(&updated_field_value_properties, &state.database_pool).await { -// Ok(updated_target_action) => updated_target_action, + Ok(updated_target_field_value) => updated_target_field_value, -// Err(error) => { + Err(error) => { -// let http_error = HTTPError::InternalServerError(Some(format!("Failed to update authenticated_app: {:?}", error))); -// ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), &state.database_pool).await.ok(); -// return Err(http_error); + let http_error = HTTPError::InternalServerError(Some(format!("Failed to update field value {}: {:?}", original_target_field_value.id, error))); + ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), &state.database_pool).await.ok(); + return Err(http_error); -// } + } -// }; + }; -// ActionLogEntry::create(&InitialActionLogEntryProperties { -// action_id: update_access_policy_action.id, -// http_transaction_id: Some(http_transaction.id), -// actor_type: if let AuthenticatedPrincipal::User(_) = &authenticated_principal { ActionLogEntryActorType::User } else { ActionLogEntryActorType::App }, -// actor_user_id: if let AuthenticatedPrincipal::User(authenticated_user) = &authenticated_principal { Some(authenticated_user.id.clone()) } else { None }, -// actor_field_value_id: if let AuthenticatedPrincipal::App(authenticated_app) = &authenticated_principal { Some(authenticated_app.id.clone()) } else { None }, -// target_resource_type: ActionLogEntryTargetResourceType::Action, -// target_action_id: Some(updated_target_action.id), -// ..Default::default() -// }, &state.database_pool).await.ok(); -// ServerLogEntry::success(&format!("Successfully updated action {}.", updated_target_action.id), Some(&http_transaction.id), &state.database_pool).await.ok(); + ActionLogEntry::create(&InitialActionLogEntryProperties { + action_id: update_access_policy_action.id, + http_transaction_id: Some(http_transaction.id), + actor_type: if let AuthenticatedPrincipal::User(_) = &authenticated_principal { ActionLogEntryActorType::User } else { ActionLogEntryActorType::App }, + actor_user_id: if let AuthenticatedPrincipal::User(authenticated_user) = &authenticated_principal { Some(authenticated_user.id.clone()) } else { None }, + actor_app_id: if let AuthenticatedPrincipal::App(authenticated_app) = &authenticated_principal { Some(authenticated_app.id.clone()) } else { None }, + target_resource_type: ActionLogEntryTargetResourceType::FieldValue, + target_field_value_id: Some(updated_target_field_value.id), + ..Default::default() + }, &state.database_pool).await.ok(); + ServerLogEntry::success(&format!("Successfully updated field value {}.", updated_target_field_value.id), Some(&http_transaction.id), &state.database_pool).await.ok(); -// return Ok(Json(updated_target_action)); + return Ok(Json(updated_target_field_value)); -// } +} pub fn get_router(state: AppState) -> Router { let router = Router::::new() .route("/field-values/{field_value_id}", axum::routing::get(handle_get_field_value_request)) .route("/field-values/{field_value_id}", axum::routing::delete(handle_delete_field_value_request)) - // .route("/field-values/{field_value_id}", axum::routing::patch(handle_patch_app_request)) + .route("/field-values/{field_value_id}", axum::routing::patch(handle_patch_field_value_request)) .layer(axum::middleware::from_fn_with_state(state.clone(), authentication_middleware::authenticate_user)) .layer(axum::middleware::from_fn_with_state(state.clone(), authentication_middleware::authenticate_app)) .layer(axum::middleware::from_fn_with_state(state.clone(), http_request_middleware::create_http_request)) diff --git a/src/routes/field-values/{field_value_id}/tests.rs b/src/routes/field-values/{field_value_id}/tests.rs index 840a707..b4d583d 100644 --- a/src/routes/field-values/{field_value_id}/tests.rs +++ b/src/routes/field-values/{field_value_id}/tests.rs @@ -14,13 +14,14 @@ use axum_extra::extract::cookie::Cookie; use axum_test::TestServer; use ntest::timeout; use reqwest::StatusCode; +use uuid::Uuid; use crate::{ Action, AppState, get_json_web_token_private_key, initialize_required_tables, predefinitions::{ initialize_predefined_actions, initialize_predefined_configurations, initialize_predefined_roles }, resources::{ ResourceError, access_policy:: - ActionPermissionLevel, field_value::FieldValue + ActionPermissionLevel, field_value::{EditableFieldValueProperties, FieldValue} }, tests::{TestEnvironment, TestSlashstepServerError} }; @@ -371,278 +372,276 @@ async fn verify_resource_exists_when_deleting_by_id() -> Result<(), TestSlashste } -// /// Verifies that the router can return a 200 status code if the resource is successfully patched. -// #[tokio::test] -// async fn verify_successful_patch_by_id() -> Result<(), TestSlashstepServerError> { +/// Verifies that the router can return a 200 status code if the resource is successfully patched. +#[tokio::test] +async fn verify_successful_patch_by_id() -> Result<(), TestSlashstepServerError> { -// let test_environment = TestEnvironment::new().await?; -// initialize_required_tables(&test_environment.database_pool).await?; -// initialize_predefined_actions(&test_environment.database_pool).await?; -// initialize_predefined_roles(&test_environment.database_pool).await?; - -// // Create the user and the session. -// let user = test_environment.create_random_user().await?; -// let session = test_environment.create_random_session(Some(&user.id)).await?; -// let json_web_token_private_key = get_json_web_token_private_key().await?; -// let session_token = session.generate_json_web_token(&json_web_token_private_key).await?; -// let update_fields_action = Action::get_by_name("fieldValues.update", &test_environment.database_pool).await?; -// AccessPolicy::create(&InitialAccessPolicyProperties { -// action_id: update_fields_action.id, -// permission_level: ActionPermissionLevel::Editor, -// is_inheritance_enabled: true, -// principal_type: AccessPolicyPrincipalType::User, -// principal_user_id: Some(user.id), -// scoped_resource_type: AccessPolicyResourceType::Server, -// ..Default::default() -// }, &test_environment.database_pool).await?; - -// // Set up the server and send the request. -// let original_app = test_environment.create_random_app().await?; -// let new_name = Uuid::now_v7().to_string(); -// let new_display_name = Uuid::now_v7().to_string(); -// let new_description = Some(Uuid::now_v7().to_string()); -// let new_client_type = AppClientType::Confidential; - -// let state = AppState { -// database_pool: test_environment.database_pool.clone(), -// }; -// let router = super::get_router(state.clone()) -// .with_state(state) -// .into_make_service_with_connect_info::(); -// let test_server = TestServer::new(router)?; -// let response = test_server.patch(&format!("/field-values/{}", original_field.id)) -// .add_cookie(Cookie::new("sessionToken", format!("Bearer {}", session_token))) -// .json(&serde_json::json!({ -// "name": new_name.clone(), -// "display_name": new_display_name.clone(), -// "description": new_description.clone(), -// "client_type": new_client_type.clone() -// })) -// .await; + let test_environment = TestEnvironment::new().await?; + initialize_required_tables(&test_environment.database_pool).await?; + initialize_predefined_actions(&test_environment.database_pool).await?; + initialize_predefined_roles(&test_environment.database_pool).await?; + initialize_predefined_configurations(&test_environment.database_pool).await?; + + // Create the user and the session. + let user = test_environment.create_random_user().await?; + let session = test_environment.create_random_session(Some(&user.id)).await?; + let json_web_token_private_key = get_json_web_token_private_key().await?; + let session_token = session.generate_json_web_token(&json_web_token_private_key).await?; + let update_field_values_action = Action::get_by_name("fieldValues.update", &test_environment.database_pool).await?; + test_environment.create_server_access_policy(&user.id, &update_field_values_action.id, &ActionPermissionLevel::User).await?; + + // Set up the server and send the request. + let original_field_value = test_environment.create_random_field_value().await?; + let updated_field_value_properties = EditableFieldValueProperties { + text_value: Some(Some(Uuid::now_v7().to_string())), + ..Default::default() + }; + + let state = AppState { + database_pool: test_environment.database_pool.clone(), + }; + let router = super::get_router(state.clone()) + .with_state(state) + .into_make_service_with_connect_info::(); + let test_server = TestServer::new(router)?; + let response = test_server.patch(&format!("/field-values/{}", original_field_value.id)) + .add_cookie(Cookie::new("sessionToken", format!("Bearer {}", session_token))) + .json(&serde_json::json!(updated_field_value_properties)) + .await; -// // Verify the response. -// assert_eq!(response.status_code(), StatusCode::OK); - -// let updated_app: Field = response.json(); -// assert_eq!(original_field.id, updated_field.id); -// assert_eq!(updated_app.name, new_name); -// assert_eq!(updated_app.display_name, new_display_name); -// assert_eq!(updated_app.description, new_description); -// assert_eq!(updated_app.client_type, new_client_type); -// assert_eq!(original_app.parent_resource_type, updated_app.parent_resource_type); -// assert_eq!(original_app.parent_workspace_id, updated_app.parent_workspace_id); -// assert_eq!(original_app.parent_user_id, updated_app.parent_user_id); - -// return Ok(()); - -// } - -// /// Verifies that the router can return a 400 status code if the request doesn't have a valid content type. -// #[tokio::test] -// async fn verify_content_type_when_patching_by_id() -> Result<(), TestSlashstepServerError> { - -// let test_environment = TestEnvironment::new().await?; -// initialize_required_tables(&test_environment.database_pool).await?; -// initialize_predefined_actions(&test_environment.database_pool).await?; -// initialize_predefined_roles(&test_environment.database_pool).await?; - -// // Set up the server and send the request. -// let state = AppState { -// database_pool: test_environment.database_pool.clone(), -// }; -// let router = super::get_router(state.clone()) -// .with_state(state) -// .into_make_service_with_connect_info::(); -// let test_server = TestServer::new(router)?; -// let response = test_server.patch("/field-values/not-a-uuid") -// .await; + // Verify the response. + assert_eq!(response.status_code(), StatusCode::OK); + + let updated_field_value: FieldValue = response.json(); + assert_eq!(updated_field_value.id, original_field_value.id); + assert_eq!(updated_field_value.field_id, original_field_value.field_id); + assert_eq!(updated_field_value.parent_resource_type, original_field_value.parent_resource_type); + assert_eq!(updated_field_value.parent_field_id, original_field_value.parent_field_id); + assert_eq!(updated_field_value.parent_item_id, original_field_value.parent_item_id); + assert_eq!(updated_field_value.value_type, original_field_value.value_type); + assert_eq!(updated_field_value.text_value, updated_field_value_properties.text_value.expect("Expected text_value to be set in the updated field value properties.")); + assert_eq!(updated_field_value.number_value, original_field_value.number_value); + assert_eq!(updated_field_value.boolean_value, original_field_value.boolean_value); + assert_eq!(updated_field_value.timestamp_value, original_field_value.timestamp_value); + assert_eq!(updated_field_value.stakeholder_type, original_field_value.stakeholder_type); + assert_eq!(updated_field_value.stakeholder_user_id, original_field_value.stakeholder_user_id); + assert_eq!(updated_field_value.stakeholder_group_id, original_field_value.stakeholder_group_id); + assert_eq!(updated_field_value.stakeholder_app_id, original_field_value.stakeholder_app_id); + + return Ok(()); + +} + +/// Verifies that the router can return a 400 status code if the request doesn't have a valid content type. +#[tokio::test] +async fn verify_content_type_when_patching_by_id() -> Result<(), TestSlashstepServerError> { + + let test_environment = TestEnvironment::new().await?; + initialize_required_tables(&test_environment.database_pool).await?; + initialize_predefined_actions(&test_environment.database_pool).await?; + initialize_predefined_roles(&test_environment.database_pool).await?; + initialize_predefined_configurations(&test_environment.database_pool).await?; + + // Set up the server and send the request. + let state = AppState { + database_pool: test_environment.database_pool.clone(), + }; + let router = super::get_router(state.clone()) + .with_state(state) + .into_make_service_with_connect_info::(); + let test_server = TestServer::new(router)?; + let response = test_server.patch("/field-values/not-a-uuid") + .await; -// // Verify the response. -// assert_eq!(response.status_code(), StatusCode::BAD_REQUEST); -// return Ok(()); - -// } - -// /// Verifies that the router can return a 400 status code if the request body is not valid JSON. -// #[tokio::test] -// async fn verify_request_body_exists_when_patching_by_id() -> Result<(), TestSlashstepServerError> { - -// let test_environment = TestEnvironment::new().await?; -// initialize_required_tables(&test_environment.database_pool).await?; -// initialize_predefined_actions(&test_environment.database_pool).await?; -// initialize_predefined_roles(&test_environment.database_pool).await?; - -// // Set up the server and send the request. -// let state = AppState { -// database_pool: test_environment.database_pool.clone(), -// }; -// let router = super::get_router(state.clone()) -// .with_state(state) -// .into_make_service_with_connect_info::(); -// let test_server = TestServer::new(router)?; -// let response = test_server.patch("/field-values/not-a-uuid") -// .add_header("Content-Type", "application/json") -// .await; + // Verify the response. + assert_eq!(response.status_code(), StatusCode::BAD_REQUEST); + return Ok(()); + +} + +/// Verifies that the router can return a 400 status code if the request body is not valid JSON. +#[tokio::test] +async fn verify_request_body_exists_when_patching_by_id() -> Result<(), TestSlashstepServerError> { + + let test_environment = TestEnvironment::new().await?; + initialize_required_tables(&test_environment.database_pool).await?; + initialize_predefined_actions(&test_environment.database_pool).await?; + initialize_predefined_roles(&test_environment.database_pool).await?; + initialize_predefined_configurations(&test_environment.database_pool).await?; + + // Set up the server and send the request. + let state = AppState { + database_pool: test_environment.database_pool.clone(), + }; + let router = super::get_router(state.clone()) + .with_state(state) + .into_make_service_with_connect_info::(); + let test_server = TestServer::new(router)?; + let response = test_server.patch("/field-values/not-a-uuid") + .add_header("Content-Type", "application/json") + .await; -// // Verify the response. -// assert_eq!(response.status_code(), StatusCode::BAD_REQUEST); -// return Ok(()); + // Verify the response. + assert_eq!(response.status_code(), StatusCode::BAD_REQUEST); + return Ok(()); -// } +} -// /// Verifies that the router can return a 400 status code if the request body includes unwanted data. -// #[tokio::test] -// async fn verify_request_body_json_when_patching_by_id() -> Result<(), TestSlashstepServerError> { +/// Verifies that the router can return a 400 status code if the request body includes unwanted data. +#[tokio::test] +async fn verify_request_body_json_when_patching_by_id() -> Result<(), TestSlashstepServerError> { -// let test_environment = TestEnvironment::new().await?; -// initialize_required_tables(&test_environment.database_pool).await?; -// initialize_predefined_actions(&test_environment.database_pool).await?; -// initialize_predefined_roles(&test_environment.database_pool).await?; + let test_environment = TestEnvironment::new().await?; + initialize_required_tables(&test_environment.database_pool).await?; + initialize_predefined_actions(&test_environment.database_pool).await?; + initialize_predefined_roles(&test_environment.database_pool).await?; + initialize_predefined_configurations(&test_environment.database_pool).await?; -// // Set up the server and send the request. -// let state = AppState { -// database_pool: test_environment.database_pool.clone(), -// }; -// let router = super::get_router(state.clone()) -// .with_state(state) -// .into_make_service_with_connect_info::(); -// let test_server = TestServer::new(router)?; -// let response = test_server.patch(&format!("/field-values/{}", uuid::Uuid::now_v7())) -// .add_header("Content-Type", "application/json") -// .json(&serde_json::json!({ -// "name": "Super Duper Admin", -// "display_name": "true", -// "description": true, -// })) -// .await; + // Create a dummy delegation policy to patch. + let field_value = test_environment.create_random_field_value().await?; + + // Set up the server and send the request. + let state = AppState { + database_pool: test_environment.database_pool.clone(), + }; + let router = super::get_router(state.clone()) + .with_state(state) + .into_make_service_with_connect_info::(); + let test_server = TestServer::new(router)?; + let response = test_server.patch(&format!("/field-values/{}", field_value.id)) + .add_header("Content-Type", "application/json") + .json(&serde_json::json!({ + "text_value": true + })) + .await; -// // Verify the response. -// assert_eq!(response.status_code(), StatusCode::BAD_REQUEST); -// return Ok(()); - -// } - -// /// Verifies that the router can return a 400 status code if the resource ID is not a UUID. -// #[tokio::test] -// async fn verify_uuid_when_patching_by_id() -> Result<(), TestSlashstepServerError> { - -// let test_environment = TestEnvironment::new().await?; -// initialize_required_tables(&test_environment.database_pool).await?; -// initialize_predefined_actions(&test_environment.database_pool).await?; -// initialize_predefined_roles(&test_environment.database_pool).await?; -// let state = AppState { -// database_pool: test_environment.database_pool.clone(), -// }; -// let router = super::get_router(state.clone()) -// .with_state(state) -// .into_make_service_with_connect_info::(); -// let test_server = TestServer::new(router)?; -// let response = test_server.patch("/field-values/not-a-uuid") -// .add_header("Content-Type", "application/json") -// .json(&serde_json::json!({ -// "display_name": Uuid::now_v7().to_string() -// })) -// .await; + // Verify the response. + assert_eq!(response.status_code(), StatusCode::BAD_REQUEST); + return Ok(()); + +} + +/// Verifies that the router can return a 400 status code if the resource ID is not a UUID. +#[tokio::test] +async fn verify_uuid_when_patching_by_id() -> Result<(), TestSlashstepServerError> { + + let test_environment = TestEnvironment::new().await?; + initialize_required_tables(&test_environment.database_pool).await?; + initialize_predefined_actions(&test_environment.database_pool).await?; + initialize_predefined_roles(&test_environment.database_pool).await?; + let state = AppState { + database_pool: test_environment.database_pool.clone(), + }; + let router = super::get_router(state.clone()) + .with_state(state) + .into_make_service_with_connect_info::(); + let test_server = TestServer::new(router)?; + let response = test_server.patch("/field-values/not-a-uuid") + .add_header("Content-Type", "application/json") + .json(&serde_json::json!({ + "display_name": Uuid::now_v7().to_string() + })) + .await; -// assert_eq!(response.status_code(), StatusCode::BAD_REQUEST); -// return Ok(()); + assert_eq!(response.status_code(), StatusCode::BAD_REQUEST); + return Ok(()); -// } +} -// /// Verifies that the router can return a 401 status code if the user needs authentication. -// #[tokio::test] -// async fn verify_authentication_when_patching_by_id() -> Result<(), TestSlashstepServerError> { +/// Verifies that the router can return a 401 status code if the user needs authentication. +#[tokio::test] +async fn verify_authentication_when_patching_by_id() -> Result<(), TestSlashstepServerError> { -// let test_environment = TestEnvironment::new().await?; -// initialize_required_tables(&test_environment.database_pool).await?; -// initialize_predefined_actions(&test_environment.database_pool).await?; -// initialize_predefined_roles(&test_environment.database_pool).await?; + let test_environment = TestEnvironment::new().await?; + initialize_required_tables(&test_environment.database_pool).await?; + initialize_predefined_actions(&test_environment.database_pool).await?; + initialize_predefined_roles(&test_environment.database_pool).await?; -// // Set up the server and send the request. -// let field_value = test_environment.create_random_field_value().await?; -// let state = AppState { -// database_pool: test_environment.database_pool.clone(), -// }; -// let router = super::get_router(state.clone()) -// .with_state(state) -// .into_make_service_with_connect_info::(); -// let test_server = TestServer::new(router)?; -// let response = test_server.patch(&format!("/field-values/{}", field_value.id)) -// .json(&serde_json::json!({ -// "display_name": Uuid::now_v7().to_string() -// })) -// .await; + // Set up the server and send the request. + let field_value = test_environment.create_random_field_value().await?; + let state = AppState { + database_pool: test_environment.database_pool.clone(), + }; + let router = super::get_router(state.clone()) + .with_state(state) + .into_make_service_with_connect_info::(); + let test_server = TestServer::new(router)?; + let response = test_server.patch(&format!("/field-values/{}", field_value.id)) + .json(&serde_json::json!({ + "display_name": Uuid::now_v7().to_string() + })) + .await; -// assert_eq!(response.status_code(), StatusCode::UNAUTHORIZED); - -// return Ok(()); - -// } - -// /// Verifies that the router can return a 403 status code if the user does not have permission to patch the resource. -// #[tokio::test] -// async fn verify_permission_when_patching() -> Result<(), TestSlashstepServerError> { - -// let test_environment = TestEnvironment::new().await?; -// initialize_required_tables(&test_environment.database_pool).await?; -// initialize_predefined_actions(&test_environment.database_pool).await?; -// initialize_predefined_roles(&test_environment.database_pool).await?; - -// // Create the user and the session. -// let user = test_environment.create_random_user().await?; -// let session = test_environment.create_random_session(Some(&user.id)).await?; -// let json_web_token_private_key = get_json_web_token_private_key().await?; -// let session_token = session.generate_json_web_token(&json_web_token_private_key).await?; - -// // Set up the server and send the request. -// let field_value = test_environment.create_random_field_value().await?; -// let state = AppState { -// database_pool: test_environment.database_pool.clone(), -// }; -// let router = super::get_router(state.clone()) -// .with_state(state) -// .into_make_service_with_connect_info::(); -// let test_server = TestServer::new(router)?; -// let response = test_server.patch(&format!("/field-values/{}", field_value.id)) -// .add_cookie(Cookie::new("sessionToken", format!("Bearer {}", session_token))) -// .json(&serde_json::json!({ -// "display_name": Uuid::now_v7().to_string() -// })) -// .await; + assert_eq!(response.status_code(), StatusCode::UNAUTHORIZED); + + return Ok(()); + +} + +/// Verifies that the router can return a 403 status code if the user does not have permission to patch the resource. +#[tokio::test] +async fn verify_permission_when_patching() -> Result<(), TestSlashstepServerError> { + + let test_environment = TestEnvironment::new().await?; + initialize_required_tables(&test_environment.database_pool).await?; + initialize_predefined_actions(&test_environment.database_pool).await?; + initialize_predefined_roles(&test_environment.database_pool).await?; + + // Create the user and the session. + let user = test_environment.create_random_user().await?; + let session = test_environment.create_random_session(Some(&user.id)).await?; + let json_web_token_private_key = get_json_web_token_private_key().await?; + let session_token = session.generate_json_web_token(&json_web_token_private_key).await?; + + // Set up the server and send the request. + let field_value = test_environment.create_random_field_value().await?; + let state = AppState { + database_pool: test_environment.database_pool.clone(), + }; + let router = super::get_router(state.clone()) + .with_state(state) + .into_make_service_with_connect_info::(); + let test_server = TestServer::new(router)?; + let response = test_server.patch(&format!("/field-values/{}", field_value.id)) + .add_cookie(Cookie::new("sessionToken", format!("Bearer {}", session_token))) + .json(&serde_json::json!({ + "display_name": Uuid::now_v7().to_string() + })) + .await; -// // Verify the response. -// assert_eq!(response.status_code(), StatusCode::FORBIDDEN); - -// return Ok(()); - -// } - -// /// Verifies that the router can return a 404 status code if the resource does not exist. -// #[tokio::test] -// async fn verify_resource_exists_when_patching() -> Result<(), TestSlashstepServerError> { - -// let test_environment = TestEnvironment::new().await?; -// initialize_required_tables(&test_environment.database_pool).await?; -// initialize_predefined_actions(&test_environment.database_pool).await?; -// initialize_predefined_roles(&test_environment.database_pool).await?; - -// // Set up the server and send the request. -// let state = AppState { -// database_pool: test_environment.database_pool.clone(), -// }; -// let router = super::get_router(state.clone()) -// .with_state(state) -// .into_make_service_with_connect_info::(); -// let test_server = TestServer::new(router)?; -// let response = test_server.patch(&format!("/field-values/{}", Uuid::now_v7())) -// .json(&serde_json::json!({ -// "display_name": Uuid::now_v7().to_string() -// })) -// .await; + // Verify the response. + assert_eq!(response.status_code(), StatusCode::FORBIDDEN); + + return Ok(()); + +} + +/// Verifies that the router can return a 404 status code if the resource does not exist. +#[tokio::test] +async fn verify_resource_exists_when_patching() -> Result<(), TestSlashstepServerError> { + + let test_environment = TestEnvironment::new().await?; + initialize_required_tables(&test_environment.database_pool).await?; + initialize_predefined_actions(&test_environment.database_pool).await?; + initialize_predefined_roles(&test_environment.database_pool).await?; + + // Set up the server and send the request. + let state = AppState { + database_pool: test_environment.database_pool.clone(), + }; + let router = super::get_router(state.clone()) + .with_state(state) + .into_make_service_with_connect_info::(); + let test_server = TestServer::new(router)?; + let response = test_server.patch(&format!("/field-values/{}", Uuid::now_v7())) + .json(&serde_json::json!({ + "display_name": Uuid::now_v7().to_string() + })) + .await; -// // Verify the response. -// assert_eq!(response.status_code(), StatusCode::NOT_FOUND); + // Verify the response. + assert_eq!(response.status_code(), StatusCode::NOT_FOUND); -// return Ok(()); + return Ok(()); -// } \ No newline at end of file +} \ No newline at end of file