Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/resources/field_value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<String>>,

/// The field choice's number value, if applicable.
pub number_value: Option<Option<Decimal>>,

/// The field choice's boolean value, if applicable.
pub boolean_value: Option<Option<bool>>,

/// The field choice's date time value, if applicable.
pub timestamp_value: Option<Option<DateTime<Utc>>>,

/// The field choice's stakeholder type, if applicable.
pub stakeholder_type: Option<Option<StakeholderType>>,

/// The field choice's stakeholder user ID, if applicable.
pub stakeholder_user_id: Option<Option<Uuid>>,

/// The field choice's stakeholder group ID, if applicable.
pub stakeholder_group_id: Option<Option<Uuid>>,

/// The field choice's stakeholder app ID, if applicable.
pub stakeholder_app_id: Option<Option<Uuid>>

}

#[derive(Debug, Clone, Serialize, Deserialize, ToSql, FromSql)]
pub struct FieldValue {

Expand Down Expand Up @@ -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<Self, ResourceError> {

let query = String::from("UPDATE field_values SET ");
let parameter_boxes: Vec<Box<dyn ToSql + Sync + Send>> = 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, &parameters).await?;
database_client.query("COMMIT;", &[]).await?;

let field_value = Self::convert_from_row(&row);
return Ok(field_value);

}

}

impl DeletableResource for FieldValue {
Expand Down
121 changes: 47 additions & 74 deletions src/routes/field-values/{field_value_id}/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -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<String>,
// State(state): State<AppState>,
// Extension(http_transaction): Extension<Arc<HTTPTransaction>>,
// Extension(authenticated_user): Extension<Option<Arc<User>>>,
// Extension(authenticated_app): Extension<Option<Arc<App>>>,
// Extension(authenticated_app_authorization): Extension<Option<Arc<AppAuthorization>>>,
// body: Result<Json<EditableAppProperties>, JsonRejection>
// ) -> Result<Json<App>, 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<String>,
State(state): State<AppState>,
Extension(http_transaction): Extension<Arc<HTTPTransaction>>,
Extension(authenticated_user): Extension<Option<Arc<User>>>,
Extension(authenticated_app): Extension<Option<Arc<App>>>,
Extension(authenticated_app_authorization): Extension<Option<Arc<AppAuthorization>>>,
body: Result<Json<EditableFieldValueProperties>, JsonRejection>
) -> Result<Json<FieldValue>, 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<AppState> {

let router = Router::<AppState>::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))
Expand Down
Loading