use actix_session::Session; use actix_web::{Responder, web}; use serde::{Serialize, Deserialize}; use yopa::{DataType, ID, TypedValue}; use yopa::model::{PropertyModel, RelationModel}; use crate::session_ext::SessionExt; use crate::TERA; use crate::tera_ext::TeraExt; use crate::utils::{ParseOrBadReq, redirect}; use crate::routes::relation_model::ObjectOrRelationModelDisplay; #[get("/model/property/create/{object_id}")] pub(crate) async fn create_form( object_id: web::Path, store: crate::YopaStoreWrapper, session: Session, ) -> actix_web::Result { let mut context = tera::Context::new(); session.render_flash(&mut context); let rg = store.read().await; debug!("ID = {}", object_id); let object = { let id = object_id.parse_or_bad_request()?; debug!("Create property for ID={}", id); if let Some(om) = rg.get_object_model(id) { ObjectOrRelationModelDisplay { id: om.id, describe: format!("object model \"{}\"", om.name), } } else if let Some(rm) = rg.get_relation_model(id) { ObjectOrRelationModelDisplay { id: rm.id, describe: format!("relation model \"{}\"", rm.name), } } else { return Err(actix_web::error::ErrorNotFound("No such source object")); } }; context.insert("object", &object); TERA.build_response("property_create", &context) } #[derive(Deserialize)] pub(crate) struct PropertyModelCreate { pub object: ID, pub name: String, #[serde(default)] pub optional: bool, #[serde(default)] pub multiple: bool, pub data_type: DataType, /// Default value to be parsed to the data type /// May be unused if empty and optional pub default: String, } #[post("/model/property/create")] pub(crate) async fn create( form: web::Form, store: crate::YopaStoreWrapper, session: Session, ) -> actix_web::Result { let mut wg = store.write().await; let form = form.into_inner(); let optional = form.optional; let multiple = form.multiple; match wg.define_property(PropertyModel { id: Default::default(), object: form.object, name: form.name.clone(), optional, multiple, data_type: form.data_type, default: { match form.data_type { DataType::String => { if form.default.is_empty() && optional { None } else { Some(TypedValue::String(form.default.into())) } } DataType::Integer => { if form.default.is_empty() { if optional { None } else { Some(TypedValue::Integer(0)) } } else { // TODO better error reporting Some(TypedValue::Integer(form.default.parse() .map_err(|_| { actix_web::error::ErrorBadRequest(format!("Error parsing \"{}\" as integer", form.default)) })?)) } } DataType::Decimal => { if form.default.is_empty() { if optional { None } else { Some(TypedValue::Decimal(0.0)) } } else { // TODO better error reporting Some(TypedValue::Decimal(form.default.parse() .map_err(|_| { actix_web::error::ErrorBadRequest(format!("Error parsing \"{}\" as decimal", form.default)) })?)) } } DataType::Boolean => { if form.default.is_empty() { if optional { None } else { Some(TypedValue::Boolean(false)) } } else { Some(TypedValue::String(form.default.clone().into()) .cast_to(DataType::Boolean).map_err(|_| { actix_web::error::ErrorBadRequest(format!("Error parsing \"{}\" as boolean", form.default)) })?) } } } }, }) { Ok(_id) => { debug!("Property created, redirecting to root"); session.flash_success(format!("Property model \"{}\" created.", form.name)); redirect("/") } Err(e) => { warn!("Error creating property model: {:?}", e); session.flash_error(e.to_string()); redirect(format!("/model/property/create/{}", form.object)) } } } #[get("/model/property/delete/{id}")] pub(crate) async fn delete( id: web::Path, store: crate::YopaStoreWrapper, session: Session, ) -> actix_web::Result { let mut wg = store.write().await; match wg.undefine_property(id.parse_or_bad_request()?) { Ok(rm) => { debug!("Property deleted, redirecting to root"); session.flash_success(format!("Property \"{}\" deleted.", rm.name)); redirect("/") } Err(e) => { warn!("Error deleting property: {:?}", e); session.flash_error(e.to_string()); redirect("/") // back? } } }