create property

master
Ondřej Hruška 4 years ago
parent ce0b55e64e
commit efbfecc335
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 2
      yopa-web/Cargo.toml
  2. 2
      yopa-web/resources/templates/model_create.html.tera
  3. 11
      yopa-web/resources/templates/property_create.html.tera
  4. 2
      yopa-web/resources/templates/relation_create.html.tera
  5. 2
      yopa-web/src/main.rs
  6. 148
      yopa-web/src/routes.rs
  7. 2
      yopa/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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
yopa = { path = "../yopa" } yopa = { path = "../yopa", features = [] }
serde = "1" serde = "1"
serde_json = "1" serde_json = "1"

@ -12,6 +12,8 @@ Define object
<h1>Define new object model</h1> <h1>Define new object model</h1>
{# TODO implement value restoration on error #}
<form action="/model/object/create" method="POST"> <form action="/model/object/create" method="POST">
<label for="name">Name:</label> <label for="name">Name:</label>
<input type="text" id="name" name="name"><br> <input type="text" id="name" name="name"><br>

@ -10,20 +10,23 @@ Define property
{% block content -%} {% block content -%}
<h1>Add new property to model "{{model.name}}"</h1> {# The parent can be either object or relation model #}
<h1>Add new property to {{object.describe}}</h1>
{# TODO implement value restoration on error #}
<form action="/model/property/create" method="POST"> <form action="/model/property/create" method="POST">
<input type="hidden" name="object" value="{{object}}"> <input type="hidden" name="object" value="{{object.id}}">
<label for="name">Name:</label> <label for="name">Name:</label>
<input type="text" id="name" name="name"><br> <input type="text" id="name" name="name"><br>
<label for="optional">Optional:</label> <label for="optional">Optional:</label>
<input type="checkbox" name="optional" id="optional"> <input type="checkbox" name="optional" id="optional" value="1">
<br> <br>
<label for="multiple">Multiple:</label> <label for="multiple">Multiple:</label>
<input type="checkbox" name="multiple" id="multiple"><br> <input type="checkbox" name="multiple" id="multiple" value="1"><br>
<label for="data_type">Type:</label> <label for="data_type">Type:</label>
<select name="data_type" id="data_type"> <select name="data_type" id="data_type">

@ -12,6 +12,8 @@ Define relation
<h1>Define new relation from "{{object.name}}"</h1> <h1>Define new relation from "{{object.name}}"</h1>
{# TODO implement value restoration on error #}
<form action="/model/relation/create" method="POST"> <form action="/model/relation/create" method="POST">
<input type="hidden" name="object" value="{{object.id}}"> <input type="hidden" name="object" value="{{object.id}}">

@ -99,6 +99,8 @@ async fn main() -> std::io::Result<()> {
.service(routes::object_model_create) .service(routes::object_model_create)
.service(routes::relation_model_create_form) .service(routes::relation_model_create_form)
.service(routes::relation_model_create) .service(routes::relation_model_create)
.service(routes::property_model_create_form)
.service(routes::property_model_create)
.service(routes::object_model_delete) .service(routes::object_model_delete)
.service(routes::relation_model_delete) .service(routes::relation_model_delete)
.service(routes::property_model_delete) .service(routes::property_model_delete)

@ -1,7 +1,7 @@
use actix_web::{web, HttpRequest, Responder, HttpResponse}; use actix_web::{web, HttpRequest, Responder, HttpResponse};
use crate::TERA; use crate::TERA;
use crate::tera_ext::TeraExt; use crate::tera_ext::TeraExt;
use yopa::{Storage, StorageError, ID}; use yopa::{Storage, StorageError, ID, DataType, TypedValue};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use yopa::model::{PropertyModel, RelationModel, ObjectModel}; use yopa::model::{PropertyModel, RelationModel, ObjectModel};
use std::ops::DerefMut; use std::ops::DerefMut;
@ -44,7 +44,10 @@ impl ParseOrBadReq for &str {
E: Display + Debug + 'static E: Display + Debug + 'static
{ {
self.parse::<T>() self.parse::<T>()
.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<String>,
store : crate::YopaStoreWrapper,
session : Session
) -> actix_web::Result<impl Responder> {
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<i32>,
pub multiple : Option<i32>,
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<PropertyModelCreate>,
store : crate::YopaStoreWrapper,
session : Session
) -> actix_web::Result<impl Responder> {
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}")] #[get("/model/object/delete/{id}")]
pub(crate) async fn object_model_delete( pub(crate) async fn object_model_delete(
id : web::Path<String>, id : web::Path<String>,

@ -22,5 +22,5 @@ itertools = "0.10.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
[features] [features]
default = ["uuid-ids"] default = []
uuid-ids = ["uuid"] uuid-ids = ["uuid"]

Loading…
Cancel
Save