From efbfecc3352b826dcb79e4948306a7585903a22d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 7 Feb 2021 20:42:10 +0100 Subject: [PATCH] create property --- yopa-web/Cargo.toml | 2 +- .../templates/model_create.html.tera | 2 + .../templates/property_create.html.tera | 11 +- .../templates/relation_create.html.tera | 2 + yopa-web/src/main.rs | 2 + yopa-web/src/routes.rs | 148 +++++++++++++++++- yopa/Cargo.toml | 2 +- 7 files changed, 161 insertions(+), 8 deletions(-) diff --git a/yopa-web/Cargo.toml b/yopa-web/Cargo.toml index e7b3e23..f227731 100644 --- a/yopa-web/Cargo.toml +++ b/yopa-web/Cargo.toml @@ -8,7 +8,7 @@ build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -yopa = { path = "../yopa" } +yopa = { path = "../yopa", features = [] } serde = "1" serde_json = "1" diff --git a/yopa-web/resources/templates/model_create.html.tera b/yopa-web/resources/templates/model_create.html.tera index 75508d3..4b737f3 100644 --- a/yopa-web/resources/templates/model_create.html.tera +++ b/yopa-web/resources/templates/model_create.html.tera @@ -12,6 +12,8 @@ Define object

Define new object model

+{# TODO implement value restoration on error #} +

diff --git a/yopa-web/resources/templates/property_create.html.tera b/yopa-web/resources/templates/property_create.html.tera index 625f5db..1af482e 100644 --- a/yopa-web/resources/templates/property_create.html.tera +++ b/yopa-web/resources/templates/property_create.html.tera @@ -10,20 +10,23 @@ Define property {% block content -%} -

Add new property to model "{{model.name}}"

+{# The parent can be either object or relation model #} +

Add new property to {{object.describe}}

+ +{# TODO implement value restoration on error #} - +
- +
-
+
diff --git a/yopa-web/src/main.rs b/yopa-web/src/main.rs index 443e972..c892905 100644 --- a/yopa-web/src/main.rs +++ b/yopa-web/src/main.rs @@ -99,6 +99,8 @@ async fn main() -> std::io::Result<()> { .service(routes::object_model_create) .service(routes::relation_model_create_form) .service(routes::relation_model_create) + .service(routes::property_model_create_form) + .service(routes::property_model_create) .service(routes::object_model_delete) .service(routes::relation_model_delete) .service(routes::property_model_delete) diff --git a/yopa-web/src/routes.rs b/yopa-web/src/routes.rs index f5b54f6..a415229 100644 --- a/yopa-web/src/routes.rs +++ b/yopa-web/src/routes.rs @@ -1,7 +1,7 @@ use actix_web::{web, HttpRequest, Responder, HttpResponse}; use crate::TERA; use crate::tera_ext::TeraExt; -use yopa::{Storage, StorageError, ID}; +use yopa::{Storage, StorageError, ID, DataType, TypedValue}; use serde::{Deserialize, Serialize}; use yopa::model::{PropertyModel, RelationModel, ObjectModel}; use std::ops::DerefMut; @@ -44,7 +44,10 @@ impl ParseOrBadReq for &str { E: Display + Debug + 'static { self.parse::() - .map_err(|e| actix_web::error::ErrorBadRequest(e)) + .map_err(|e| { + error!("Parse error for \"{}\"", self); + actix_web::error::ErrorBadRequest(e) + }) } } @@ -209,6 +212,147 @@ pub(crate) async fn relation_model_create( } } +#[derive(Serialize, Debug)] +struct ObjectOrRelationModelDisplay { + id : ID, + describe : String, +} + +#[get("/model/property/create/{object_id}")] +pub(crate) async fn property_model_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, + pub optional : Option, + pub multiple : Option, + 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 property_model_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.unwrap_or_default() != 0; + let multiple = form.multiple.unwrap_or_default() != 0; + + 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/object/delete/{id}")] pub(crate) async fn object_model_delete( id : web::Path, diff --git a/yopa/Cargo.toml b/yopa/Cargo.toml index ca36030..b83f136 100644 --- a/yopa/Cargo.toml +++ b/yopa/Cargo.toml @@ -22,5 +22,5 @@ itertools = "0.10.0" lazy_static = "1.4.0" [features] -default = ["uuid-ids"] +default = [] uuid-ids = ["uuid"]