You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
8.1 KiB
273 lines
8.1 KiB
use actix_web::{web, HttpRequest, Responder, HttpResponse};
|
|
use crate::TERA;
|
|
use crate::tera_ext::TeraExt;
|
|
use yopa::{Storage, StorageError, ID};
|
|
use serde::{Deserialize, Serialize};
|
|
use yopa::model::{PropertyModel, RelationModel, ObjectModel};
|
|
use std::ops::DerefMut;
|
|
use actix_session::Session;
|
|
use crate::session_ext::SessionExt;
|
|
use std::str::FromStr;
|
|
use std::fmt::{Debug, Display};
|
|
use actix_web::http::header::IntoHeaderValue;
|
|
|
|
#[derive(Serialize, Debug)]
|
|
struct ObjectModelDisplay<'a> {
|
|
id : yopa::ID,
|
|
name : &'a str,
|
|
properties: Vec<&'a PropertyModel>,
|
|
relations: Vec<RelationModelDisplay<'a>>,
|
|
}
|
|
|
|
#[derive(Serialize, Debug)]
|
|
struct RelationModelDisplay<'a> {
|
|
model : &'a RelationModel,
|
|
related_name : &'a str,
|
|
properties: Vec<&'a PropertyModel>,
|
|
}
|
|
|
|
fn redirect(path : impl IntoHeaderValue) -> actix_web::Result<HttpResponse> {
|
|
Ok(HttpResponse::SeeOther()
|
|
.header("location", path) // back - to where?
|
|
.finish())
|
|
}
|
|
|
|
trait ParseOrBadReq {
|
|
fn parse_or_bad_request<T, E>(&self) -> actix_web::Result<T>
|
|
where T: FromStr<Err=E>,
|
|
E: Display + Debug + 'static;
|
|
}
|
|
|
|
impl ParseOrBadReq for &str {
|
|
fn parse_or_bad_request<T, E>(&self) -> actix_web::Result<T>
|
|
where T: FromStr<Err=E>,
|
|
E: Display + Debug + 'static
|
|
{
|
|
self.parse::<T>()
|
|
.map_err(|e| actix_web::error::ErrorBadRequest(e))
|
|
}
|
|
}
|
|
|
|
impl ParseOrBadReq for String {
|
|
fn parse_or_bad_request<T, E>(&self) -> actix_web::Result<T>
|
|
where T: FromStr<Err=E>,
|
|
E: Display + Debug + 'static
|
|
{
|
|
self.as_str()
|
|
.parse_or_bad_request()
|
|
}
|
|
}
|
|
|
|
|
|
#[get("/")]
|
|
pub(crate) async fn index(session : Session, store : crate::YopaStoreWrapper) -> actix_web::Result<impl Responder> {
|
|
|
|
let rg = store.read().await;
|
|
|
|
let models_iter = rg.get_object_models();
|
|
|
|
// object and relation props
|
|
let mut model_props = rg.get_grouped_prop_models();
|
|
|
|
let mut model_relations = rg.get_grouped_relation_models();
|
|
|
|
let mut models = vec![];
|
|
for om in models_iter {
|
|
let mut oprops = model_props.remove(&om.id).unwrap_or_default();
|
|
let mut relations = model_relations.remove(&om.id).unwrap_or_default();
|
|
|
|
let rel_displays = relations.into_iter().map(|rm| {
|
|
let mut rprops = model_props.remove(&rm.id).unwrap_or_default();
|
|
rprops.sort_by_key(|m| &m.name);
|
|
|
|
RelationModelDisplay {
|
|
model: rm,
|
|
related_name: rg.get_model_name(rm.related),
|
|
properties: rprops
|
|
}
|
|
|
|
}).collect::<Vec<_>>();
|
|
|
|
oprops.sort_by_key(|m| &m.name);
|
|
|
|
models.push(ObjectModelDisplay {
|
|
id: om.id,
|
|
name: &om.name,
|
|
properties: oprops,
|
|
relations: rel_displays,
|
|
})
|
|
}
|
|
|
|
models.sort_by_key(|m| m.name);
|
|
|
|
let mut ctx = tera::Context::new();
|
|
ctx.insert("models", &models);
|
|
session.render_flash(&mut ctx);
|
|
|
|
TERA.build_response("index", &ctx)
|
|
}
|
|
|
|
#[get("/model/object/create")]
|
|
pub(crate) async fn object_model_create_form(session : Session) -> actix_web::Result<impl Responder> {
|
|
let mut context = tera::Context::new();
|
|
session.render_flash(&mut context);
|
|
|
|
TERA.build_response("model_create", &context)
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub(crate) struct ObjectModelCreate {
|
|
pub name : String,
|
|
}
|
|
|
|
#[post("/model/object/create")]
|
|
pub(crate) async fn object_model_create(
|
|
form : web::Form<ObjectModelCreate>,
|
|
store : crate::YopaStoreWrapper,
|
|
session : Session
|
|
) -> actix_web::Result<impl Responder> {
|
|
let mut wg = store.write().await;
|
|
let form = form.into_inner();
|
|
match wg.define_object(ObjectModel {
|
|
id: Default::default(),
|
|
name: form.name.clone()
|
|
}) {
|
|
Ok(_id) => {
|
|
debug!("Object created, redirecting to root");
|
|
session.flash_success(format!("Object model \"{}\" created.", form.name));
|
|
redirect("/")
|
|
}
|
|
Err(e) => {
|
|
warn!("Error creating model: {:?}", e);
|
|
session.flash_error(e.to_string());
|
|
redirect("/model/object/create")
|
|
}
|
|
}
|
|
}
|
|
|
|
#[get("/model/relation/create/{object_id}")]
|
|
pub(crate) async fn relation_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 = rg.get_object_model(object_id.parse_or_bad_request()?)
|
|
.ok_or_else(|| actix_web::error::ErrorNotFound("No such source object"))?;
|
|
|
|
let mut models: Vec<_> = rg.get_object_models().collect();
|
|
|
|
models.sort_by_key(|m| &m.name);
|
|
|
|
context.insert("models", &models);
|
|
context.insert("object", &object);
|
|
|
|
TERA.build_response("relation_create", &context)
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub(crate) struct RelationModelCreate {
|
|
pub object : ID,
|
|
pub name : String,
|
|
pub optional : Option<i32>,
|
|
pub multiple : Option<i32>,
|
|
pub related : ID,
|
|
}
|
|
|
|
#[post("/model/relation/create")]
|
|
pub(crate) async fn relation_model_create(
|
|
form : web::Form<RelationModelCreate>,
|
|
store : crate::YopaStoreWrapper,
|
|
session : Session
|
|
) -> actix_web::Result<impl Responder> {
|
|
let mut wg = store.write().await;
|
|
let form = form.into_inner();
|
|
match wg.define_relation(RelationModel {
|
|
id: Default::default(),
|
|
object: form.object,
|
|
name: form.name.clone(),
|
|
optional: form.optional.unwrap_or_default() != 0,
|
|
multiple: form.multiple.unwrap_or_default() != 0,
|
|
related: form.related
|
|
}) {
|
|
Ok(_id) => {
|
|
debug!("Relation created, redirecting to root");
|
|
session.flash_success(format!("Relation model \"{}\" created.", form.name));
|
|
redirect("/")
|
|
}
|
|
Err(e) => {
|
|
warn!("Error creating relation model: {:?}", e);
|
|
session.flash_error(e.to_string());
|
|
redirect(format!("/model/relation/create/{}", form.object))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[get("/model/object/delete/{id}")]
|
|
pub(crate) async fn object_model_delete(
|
|
id : web::Path<String>,
|
|
store : crate::YopaStoreWrapper,
|
|
session : Session
|
|
) -> actix_web::Result<impl Responder> {
|
|
let mut wg = store.write().await;
|
|
match wg.undefine_object(id.parse().map_err(|e| actix_web::error::ErrorBadRequest(e))?) {
|
|
Ok(om) => {
|
|
debug!("Object model deleted, redirecting to root");
|
|
session.flash_success(format!("Object model \"{}\" deleted.", om.name));
|
|
redirect("/")
|
|
}
|
|
Err(e) => {
|
|
warn!("Error deleting object model: {:?}", e);
|
|
session.flash_error(e.to_string());
|
|
redirect("/") // back?
|
|
}
|
|
}
|
|
}
|
|
|
|
#[get("/model/relation/delete/{id}")]
|
|
pub(crate) async fn relation_model_delete(
|
|
id : web::Path<String>,
|
|
store : crate::YopaStoreWrapper,
|
|
session : Session
|
|
) -> actix_web::Result<impl Responder> {
|
|
let mut wg = store.write().await;
|
|
match wg.undefine_relation(id.parse_or_bad_request()?) {
|
|
Ok(rm) => {
|
|
debug!("Relation deleted, redirecting to root");
|
|
session.flash_success(format!("Relation model \"{}\" deleted.", rm.name));
|
|
redirect("/")
|
|
}
|
|
Err(e) => {
|
|
warn!("Error deleting relation model: {:?}", e);
|
|
session.flash_error(e.to_string());
|
|
redirect("/") // back?
|
|
}
|
|
}
|
|
}
|
|
|
|
#[get("/model/property/delete/{id}")]
|
|
pub(crate) async fn property_model_delete(
|
|
id : web::Path<String>,
|
|
store : crate::YopaStoreWrapper,
|
|
session : Session
|
|
) -> actix_web::Result<impl Responder> {
|
|
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?
|
|
}
|
|
}
|
|
}
|
|
|