|
|
|
use std::borrow::Cow;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use actix_session::Session;
|
|
|
|
use actix_web::{web, HttpResponse, Responder};
|
|
|
|
use heck::TitleCase;
|
|
|
|
use itertools::Itertools;
|
|
|
|
use json_dotpath::DotPaths;
|
|
|
|
use serde::Serialize;
|
|
|
|
|
|
|
|
use yopa::{data, model, Storage, ID};
|
|
|
|
|
|
|
|
use yopa::insert::InsertObj;
|
|
|
|
use yopa::model::{ObjectModel, PropertyModel, RelationModel};
|
|
|
|
use yopa::update::UpdateObj;
|
|
|
|
|
|
|
|
use crate::session_ext::SessionExt;
|
|
|
|
use crate::tera_ext::TeraExt;
|
|
|
|
use crate::utils::{redirect, StorageErrorIntoResponseError};
|
|
|
|
use crate::TERA;
|
|
|
|
use yopa::helpers::GroupByModel;
|
|
|
|
|
|
|
|
// we only need references here, Context serializes everything to Value.
|
|
|
|
// cloning would be a waste of cycles
|
|
|
|
|
|
|
|
#[derive(Debug, Default, Serialize, Clone)]
|
|
|
|
pub struct Schema<'a> {
|
|
|
|
pub obj_models: Vec<&'a model::ObjectModel>,
|
|
|
|
pub rel_models: Vec<&'a model::RelationModel>,
|
|
|
|
pub prop_models: Vec<&'a model::PropertyModel>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
|
|
pub struct ObjectDisplay<'a> {
|
|
|
|
pub id: ID,
|
|
|
|
pub model: ID,
|
|
|
|
pub name: Cow<'a, str>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Debug, Clone)]
|
|
|
|
pub struct ObjectCreateData<'a> {
|
|
|
|
pub model_id: ID,
|
|
|
|
pub schema: Schema<'a>,
|
|
|
|
pub objects: Vec<ObjectDisplay<'a>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/object/create/{model_id}")]
|
|
|
|
pub(crate) async fn create_form(
|
|
|
|
model_id: web::Path<ID>,
|
|
|
|
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;
|
|
|
|
|
|
|
|
let model = rg
|
|
|
|
.get_object_model(*model_id)
|
|
|
|
.ok_or_else(|| actix_web::error::ErrorNotFound("No such model"))?;
|
|
|
|
|
|
|
|
context.insert("model", model);
|
|
|
|
|
|
|
|
let form_data = prepare_object_create_data(&rg, model.id)?;
|
|
|
|
|
|
|
|
context.insert("form_data", &form_data);
|
|
|
|
|
|
|
|
TERA.build_response("objects/object_create", &context)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn prepare_object_create_data(rg: &Storage, model_id: ID) -> actix_web::Result<ObjectCreateData> {
|
|
|
|
let model = rg
|
|
|
|
.get_object_model(model_id)
|
|
|
|
.ok_or_else(|| actix_web::error::ErrorNotFound("No such model"))?;
|
|
|
|
|
|
|
|
let relations: Vec<_> = rg.get_relation_models_for_object_model(model.id).collect();
|
|
|
|
|
|
|
|
let mut prop_object_ids: Vec<ID> = relations.iter().map(|r| r.id).collect();
|
|
|
|
prop_object_ids.push(model.id);
|
|
|
|
|
|
|
|
prop_object_ids.sort();
|
|
|
|
prop_object_ids.dedup();
|
|
|
|
|
|
|
|
let mut related_ids: Vec<_> = relations.iter().map(|r| r.related).collect();
|
|
|
|
|
|
|
|
related_ids.sort();
|
|
|
|
related_ids.dedup();
|
|
|
|
|
|
|
|
let mut models_to_fetch = vec![model_id];
|
|
|
|
models_to_fetch.extend(&related_ids);
|
|
|
|
|
|
|
|
Ok(ObjectCreateData {
|
|
|
|
model_id: model.id,
|
|
|
|
schema: Schema {
|
|
|
|
obj_models: rg.get_object_models_by_ids(models_to_fetch).collect(), // TODO get only the ones that matter here
|
|
|
|
rel_models: relations,
|
|
|
|
prop_models: rg
|
|
|
|
.get_property_models_for_parents(prop_object_ids)
|
|
|
|
.collect(),
|
|
|
|
},
|
|
|
|
objects: rg
|
|
|
|
.get_objects_of_types(related_ids)
|
|
|
|
.map(|o| ObjectDisplay {
|
|
|
|
id: o.id,
|
|
|
|
model: o.model,
|
|
|
|
name: rg.get_object_name(o),
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/object/create")]
|
|
|
|
pub(crate) async fn create(
|
|
|
|
form: web::Json<InsertObj>,
|
|
|
|
store: crate::YopaStoreWrapper,
|
|
|
|
session: Session,
|
|
|
|
) -> actix_web::Result<impl Responder> {
|
|
|
|
warn!("{:?}", form);
|
|
|
|
|
|
|
|
let mut wg = store.write().await;
|
|
|
|
let form = form.into_inner();
|
|
|
|
|
|
|
|
let model_name = wg.get_model_name(form.model).to_owned().to_title_case();
|
|
|
|
|
|
|
|
match wg.insert_object(form) {
|
|
|
|
Ok(id) => {
|
|
|
|
wg.persist().err_to_500()?;
|
|
|
|
let obj_name = wg.get_object_name_by_id(id);
|
|
|
|
debug!("Object created, redirecting to root");
|
|
|
|
session.flash_success(format!("{} \"{}\" created.", model_name, obj_name));
|
|
|
|
Ok(HttpResponse::Ok().finish())
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Error creating model: {}", e);
|
|
|
|
Ok(HttpResponse::BadRequest().body(e.to_string()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Clone)]
|
|
|
|
struct ModelWithObjects<'a> {
|
|
|
|
model: &'a ObjectModel,
|
|
|
|
objects: Vec<ObjectDisplay<'a>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/objects")]
|
|
|
|
pub(crate) async fn list(
|
|
|
|
session: Session,
|
|
|
|
store: crate::YopaStoreWrapper,
|
|
|
|
) -> actix_web::Result<impl Responder> {
|
|
|
|
list_inner(session, store).await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) async fn list_inner(
|
|
|
|
session: Session,
|
|
|
|
store: crate::YopaStoreWrapper,
|
|
|
|
) -> actix_web::Result<HttpResponse> {
|
|
|
|
let rg = store.read().await;
|
|
|
|
|
|
|
|
let mut objects_by_model = rg.get_grouped_objects();
|
|
|
|
|
|
|
|
let models: Vec<_> = rg
|
|
|
|
.get_object_models()
|
|
|
|
.sorted_by_key(|m| &m.name)
|
|
|
|
.map(|model| {
|
|
|
|
let objects = objects_by_model.remove(&model.id).unwrap_or_default();
|
|
|
|
let mut objects = objects
|
|
|
|
.into_iter()
|
|
|
|
.map(|o| {
|
|
|
|
ObjectDisplay {
|
|
|
|
id: o.id,
|
|
|
|
model: o.model,
|
|
|
|
name: rg.get_object_name(o), // TODO optimize
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect_vec();
|
|
|
|
|
|
|
|
objects.sort_by(|a, b| a.name.cmp(&b.name));
|
|
|
|
ModelWithObjects { model, objects }
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let mut ctx = tera::Context::new();
|
|
|
|
ctx.insert("models", &models);
|
|
|
|
session.render_flash(&mut ctx);
|
|
|
|
|
|
|
|
TERA.build_response("objects/index", &ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
struct PropertyView<'a> {
|
|
|
|
model: &'a PropertyModel,
|
|
|
|
values: Vec<&'a yopa::data::Value>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
struct RelationView<'a> {
|
|
|
|
model: &'a RelationModel,
|
|
|
|
related_name: &'a str,
|
|
|
|
instances: Vec<RelationInstanceView<'a>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
struct RelationInstanceView<'a> {
|
|
|
|
related: ObjectDisplay<'a>,
|
|
|
|
related_name: Cow<'a, str>,
|
|
|
|
properties: Vec<PropertyView<'a>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/object/detail/{id}")]
|
|
|
|
pub(crate) async fn detail(
|
|
|
|
id: web::Path<ID>,
|
|
|
|
store: crate::YopaStoreWrapper,
|
|
|
|
session: Session,
|
|
|
|
) -> actix_web::Result<impl Responder> {
|
|
|
|
let object_id = *id;
|
|
|
|
|
|
|
|
let mut context = tera::Context::new();
|
|
|
|
session.render_flash(&mut context);
|
|
|
|
|
|
|
|
let rg = store.read().await;
|
|
|
|
|
|
|
|
let object = rg
|
|
|
|
.get_object(object_id)
|
|
|
|
.ok_or_else(|| actix_web::error::ErrorNotFound("No such object"))?;
|
|
|
|
|
|
|
|
let model = rg
|
|
|
|
.get_object_model(object.model)
|
|
|
|
.ok_or_else(|| actix_web::error::ErrorNotFound("No such model"))?;
|
|
|
|
|
|
|
|
context.insert(
|
|
|
|
"object",
|
|
|
|
&ObjectDisplay {
|
|
|
|
id: object_id,
|
|
|
|
model: object.model,
|
|
|
|
name: rg.get_object_name(object),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
context.insert("model", model);
|
|
|
|
context.insert("kind", &rg.get_model_name(object.model));
|
|
|
|
|
|
|
|
let relations = rg.get_relations_for_object(object_id).collect_vec();
|
|
|
|
let reci_relations = rg
|
|
|
|
.get_reciprocal_relations_for_object(object_id)
|
|
|
|
.collect_vec();
|
|
|
|
|
|
|
|
// values by parent ID
|
|
|
|
let mut ids_to_get_values_for = relations.iter().map(|r| r.id).collect_vec();
|
|
|
|
ids_to_get_values_for.extend(reci_relations.iter().map(|r| r.id));
|
|
|
|
ids_to_get_values_for.push(object_id);
|
|
|
|
|
|
|
|
let mut grouped_values = rg.get_grouped_values_for_objects(ids_to_get_values_for);
|
|
|
|
|
|
|
|
// object's own properties
|
|
|
|
{
|
|
|
|
let object_values_by_model = grouped_values
|
|
|
|
.remove(&object_id)
|
|
|
|
.unwrap_or_default()
|
|
|
|
.group_by_model();
|
|
|
|
|
|
|
|
let mut view_object_properties = vec![];
|
|
|
|
for (prop_model_id, values) in object_values_by_model {
|
|
|
|
view_object_properties.push(PropertyView {
|
|
|
|
model: rg.get_property_model(prop_model_id).unwrap(),
|
|
|
|
values,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
view_object_properties.sort_by_key(|p| &p.model.name);
|
|
|
|
|
|
|
|
context.insert("properties", &view_object_properties);
|
|
|
|
}
|
|
|
|
|
|
|
|
// go through relations
|
|
|
|
{
|
|
|
|
let grouped_relations = relations
|
|
|
|
.iter()
|
|
|
|
.group_by_model();
|
|
|
|
|
|
|
|
let mut relation_views = vec![];
|
|
|
|
for (model_id, relations) in grouped_relations {
|
|
|
|
let mut instances = vec![];
|
|
|
|
|
|
|
|
for rel in relations {
|
|
|
|
let related_obj = match rg.get_object(rel.related) {
|
|
|
|
None => continue,
|
|
|
|
Some(obj) => obj,
|
|
|
|
};
|
|
|
|
|
|
|
|
let rel_values_by_model = grouped_values
|
|
|
|
.remove(&rel.id)
|
|
|
|
.unwrap_or_default()
|
|
|
|
.group_by_model();
|
|
|
|
|
|
|
|
let mut view_rel_properties = vec![];
|
|
|
|
for (prop_model_id, values) in rel_values_by_model {
|
|
|
|
view_rel_properties.push(PropertyView {
|
|
|
|
model: rg.get_property_model(prop_model_id).unwrap(),
|
|
|
|
values,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
view_rel_properties.sort_by_key(|p| &p.model.name);
|
|
|
|
|
|
|
|
let related_name = rg.get_object_name(related_obj);
|
|
|
|
|
|
|
|
instances.push(RelationInstanceView {
|
|
|
|
related: ObjectDisplay {
|
|
|
|
id: related_obj.id,
|
|
|
|
model: related_obj.model,
|
|
|
|
name: rg.get_object_name(related_obj),
|
|
|
|
},
|
|
|
|
related_name,
|
|
|
|
properties: view_rel_properties,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
instances.sort_by(|a, b| a.related_name.cmp(&b.related_name));
|
|
|
|
|
|
|
|
relation_views.push(RelationView {
|
|
|
|
model: rg.get_relation_model(model_id).unwrap(),
|
|
|
|
related_name: rg.get_model_name(model_id),
|
|
|
|
instances,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
relation_views.sort_by_key(|r| &r.model.name);
|
|
|
|
|
|
|
|
context.insert("relations", &relation_views);
|
|
|
|
}
|
|
|
|
|
|
|
|
// near-copypasta for reciprocal
|
|
|
|
{
|
|
|
|
let grouped_relations = reci_relations
|
|
|
|
.iter()
|
|
|
|
.group_by_model();
|
|
|
|
|
|
|
|
let mut relation_views = vec![];
|
|
|
|
for (model_id, relations) in grouped_relations {
|
|
|
|
let mut instances = vec![];
|
|
|
|
|
|
|
|
for rel in relations {
|
|
|
|
let related_obj = match rg.get_object(rel.object) {
|
|
|
|
None => continue,
|
|
|
|
Some(obj) => obj,
|
|
|
|
};
|
|
|
|
|
|
|
|
let rel_values_by_model = grouped_values
|
|
|
|
.remove(&rel.id)
|
|
|
|
.unwrap_or_default()
|
|
|
|
.group_by_model();
|
|
|
|
|
|
|
|
let mut view_rel_properties = vec![];
|
|
|
|
for (prop_model_id, values) in rel_values_by_model {
|
|
|
|
view_rel_properties.push(PropertyView {
|
|
|
|
model: rg.get_property_model(prop_model_id).unwrap(),
|
|
|
|
values,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
view_rel_properties.sort_by_key(|p| &p.model.name);
|
|
|
|
|
|
|
|
let related_name = rg.get_object_name(related_obj);
|
|
|
|
|
|
|
|
instances.push(RelationInstanceView {
|
|
|
|
related: ObjectDisplay {
|
|
|
|
id: related_obj.id,
|
|
|
|
model: related_obj.model,
|
|
|
|
name: rg.get_object_name(related_obj),
|
|
|
|
},
|
|
|
|
related_name,
|
|
|
|
properties: view_rel_properties,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort_by_key is not possible because rust is still stupid about lifetimes
|
|
|
|
instances.sort_by(|a, b| a.related_name.cmp(&b.related_name));
|
|
|
|
|
|
|
|
relation_views.push(RelationView {
|
|
|
|
model: rg.get_relation_model(model_id).unwrap(),
|
|
|
|
related_name: rg.get_model_name(model_id),
|
|
|
|
instances,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
relation_views.sort_by_key(|r| &r.model.reciprocal_name);
|
|
|
|
|
|
|
|
context.insert("reciprocal_relations", &relation_views);
|
|
|
|
}
|
|
|
|
|
|
|
|
TERA.build_response("objects/object_detail", &context)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Debug, Clone)]
|
|
|
|
struct EnrichedObject<'a> {
|
|
|
|
id: ID,
|
|
|
|
model: ID,
|
|
|
|
name: Cow<'a, str>,
|
|
|
|
values: HashMap<
|
|
|
|
String, /* ID but as string so serde will stop exploding */
|
|
|
|
Vec<&'a data::Value>,
|
|
|
|
>,
|
|
|
|
relations: HashMap<String /* ID */, Vec<EnrichedRelation<'a>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Debug, Clone)]
|
|
|
|
struct EnrichedRelation<'a> {
|
|
|
|
id: ID,
|
|
|
|
object: ID,
|
|
|
|
model: ID,
|
|
|
|
related: ID,
|
|
|
|
values: HashMap<String /* ID */, Vec<&'a data::Value>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/object/update/{id}")]
|
|
|
|
pub(crate) async fn update_form(
|
|
|
|
id: web::Path<ID>,
|
|
|
|
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;
|
|
|
|
|
|
|
|
let object = rg
|
|
|
|
.get_object(*id)
|
|
|
|
.ok_or_else(|| actix_web::error::ErrorNotFound("No such object"))?;
|
|
|
|
|
|
|
|
let model = rg
|
|
|
|
.get_object_model(object.model)
|
|
|
|
.ok_or_else(|| actix_web::error::ErrorNotFound("Object has no model"))?;
|
|
|
|
|
|
|
|
// maybe its useful,idk
|
|
|
|
context.insert("model", &model);
|
|
|
|
context.insert(
|
|
|
|
"object",
|
|
|
|
&ObjectDisplay {
|
|
|
|
id: object.id,
|
|
|
|
model: object.model,
|
|
|
|
name: rg.get_object_name(object),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
let create_data = prepare_object_create_data(&rg, model.id)?;
|
|
|
|
|
|
|
|
let mut value_map = HashMap::new();
|
|
|
|
let mut relation_map = HashMap::new();
|
|
|
|
|
|
|
|
// Some properties may have no values, so we first check what IDs to expect
|
|
|
|
let prop_ids = create_data
|
|
|
|
.schema
|
|
|
|
.prop_models
|
|
|
|
.iter()
|
|
|
|
.filter(|p| p.object == model.id)
|
|
|
|
.map(|p| p.id)
|
|
|
|
.collect_vec();
|
|
|
|
|
|
|
|
let mut values_grouped = rg.get_values_for_object(*id)
|
|
|
|
.group_by_model();
|
|
|
|
|
|
|
|
prop_ids.into_iter().for_each(|id| {
|
|
|
|
value_map.insert(
|
|
|
|
id.to_string(),
|
|
|
|
values_grouped.remove(&id).unwrap_or_default(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
{
|
|
|
|
// Some properties may have no values, so we first check what IDs to expect
|
|
|
|
let relation_model_ids = create_data
|
|
|
|
.schema
|
|
|
|
.rel_models
|
|
|
|
.iter()
|
|
|
|
.filter(|p| p.object == model.id)
|
|
|
|
.map(|p| p.id)
|
|
|
|
.collect_vec();
|
|
|
|
|
|
|
|
let relations = rg.get_relations_for_object(*id).collect_vec();
|
|
|
|
let relation_ids = relations.iter().map(|r| r.id).collect_vec();
|
|
|
|
|
|
|
|
let mut relations_grouped_by_model = relations
|
|
|
|
.iter()
|
|
|
|
.group_by_model();
|
|
|
|
|
|
|
|
let mut property_models_grouped_by_parent =
|
|
|
|
rg.get_grouped_prop_models_for_parents(relation_model_ids.clone());
|
|
|
|
|
|
|
|
let mut relation_values_grouped_by_instance =
|
|
|
|
rg.get_grouped_values_for_objects(relation_ids);
|
|
|
|
|
|
|
|
for rel_model_id in relation_model_ids {
|
|
|
|
let relations = relations_grouped_by_model
|
|
|
|
.remove(&rel_model_id)
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
let mut instances = vec![];
|
|
|
|
let prop_models_for_relation = property_models_grouped_by_parent
|
|
|
|
.remove(&rel_model_id)
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
for rel in relations {
|
|
|
|
let mut relation_values_map = HashMap::new();
|
|
|
|
|
|
|
|
// values keyed by model
|
|
|
|
let mut rel_values = relation_values_grouped_by_instance
|
|
|
|
.remove(&rel.id)
|
|
|
|
.unwrap_or_default()
|
|
|
|
.group_by_model();
|
|
|
|
|
|
|
|
prop_models_for_relation.iter().for_each(|prop_model| {
|
|
|
|
relation_values_map.insert(
|
|
|
|
prop_model.id.to_string(),
|
|
|
|
rel_values.remove(&prop_model.id).unwrap_or_default(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
instances.push(EnrichedRelation {
|
|
|
|
id: rel.id,
|
|
|
|
object: rel.object,
|
|
|
|
model: rel.model,
|
|
|
|
related: rel.related,
|
|
|
|
values: relation_values_map,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
relation_map.insert(rel_model_id.to_string(), instances);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut form = serde_json::to_value(create_data)?;
|
|
|
|
|
|
|
|
let object = EnrichedObject {
|
|
|
|
id: object.id,
|
|
|
|
model: object.model,
|
|
|
|
name: rg.get_object_name(object),
|
|
|
|
values: value_map,
|
|
|
|
relations: relation_map,
|
|
|
|
};
|
|
|
|
|
|
|
|
form.dot_remove("model_id").unwrap();
|
|
|
|
form.dot_set("object", object).unwrap();
|
|
|
|
context.insert("form_data", &form);
|
|
|
|
|
|
|
|
TERA.build_response("objects/object_update", &context)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/object/update/{id}")]
|
|
|
|
pub(crate) async fn update(
|
|
|
|
id: web::Path<ID>,
|
|
|
|
form: web::Json<UpdateObj>,
|
|
|
|
store: crate::YopaStoreWrapper,
|
|
|
|
session: Session,
|
|
|
|
) -> actix_web::Result<HttpResponse> {
|
|
|
|
let mut wg = store.write().await;
|
|
|
|
let form = form.into_inner();
|
|
|
|
|
|
|
|
let object = wg
|
|
|
|
.get_object(*id)
|
|
|
|
.ok_or_else(|| actix_web::error::ErrorNotFound("No such object"))?;
|
|
|
|
|
|
|
|
let model_name = wg.get_model_name(object.model).to_owned().to_title_case();
|
|
|
|
let id = object.id;
|
|
|
|
|
|
|
|
match wg.update_object(form) {
|
|
|
|
Ok(_id) => {
|
|
|
|
wg.persist().err_to_500()?;
|
|
|
|
debug!("Object created, redirecting to root");
|
|
|
|
session.flash_success(format!(
|
|
|
|
"{} \"{}\" updated.",
|
|
|
|
model_name,
|
|
|
|
wg.get_object_name_by_id(id)
|
|
|
|
));
|
|
|
|
Ok(HttpResponse::Ok().finish())
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Error updating model: {}", e);
|
|
|
|
Ok(HttpResponse::BadRequest().body(e.to_string()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/object/delete/{id}")]
|
|
|
|
pub(crate) async fn delete(
|
|
|
|
id: web::Path<ID>,
|
|
|
|
store: crate::YopaStoreWrapper,
|
|
|
|
session: Session,
|
|
|
|
) -> actix_web::Result<impl Responder> {
|
|
|
|
let mut wg = store.write().await;
|
|
|
|
|
|
|
|
let name = wg.get_object_name_by_id(*id).to_string();
|
|
|
|
|
|
|
|
match wg.delete_object(*id) {
|
|
|
|
Ok(_obj) => {
|
|
|
|
wg.persist().err_to_500()?;
|
|
|
|
debug!("Object deleted, redirecting to root");
|
|
|
|
session.flash_success(format!("Object \"{}\" deleted.", name));
|
|
|
|
redirect("/")
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Error deleting object: {}", e);
|
|
|
|
session.flash_error(e.to_string());
|
|
|
|
redirect("/") // back?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|