diff --git a/Cargo.lock b/Cargo.lock
index fc1dca3..8b7d81c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1185,6 +1185,18 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
+[[package]]
+name = "json_dotpath"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e907e6c22a540a809d003178eae3172a6c54b94338058b9d72d051e53187be7"
+dependencies = [
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "thiserror",
+]
+
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@@ -2567,6 +2579,7 @@ dependencies = [
"heck",
"include_dir",
"itertools",
+ "json_dotpath",
"log",
"once_cell",
"parking_lot",
diff --git a/yopa-web/Cargo.toml b/yopa-web/Cargo.toml
index 86fee3c..ba2099f 100644
--- a/yopa-web/Cargo.toml
+++ b/yopa-web/Cargo.toml
@@ -23,6 +23,7 @@ actix-web-static-files = "3.0"
once_cell = "1.5.2"
rand = "0.8.3"
itertools = "0.10.0"
+json_dotpath = "1.0.3"
tokio = { version="0.2.6", features=["full"] }
diff --git a/yopa-web/resources/templates/objects/index.html.tera b/yopa-web/resources/templates/objects/index.html.tera
index 1c97278..92254bd 100644
--- a/yopa-web/resources/templates/objects/index.html.tera
+++ b/yopa-web/resources/templates/objects/index.html.tera
@@ -20,7 +20,7 @@ Objects
diff --git a/yopa-web/resources/templates/objects/object_create.html.tera b/yopa-web/resources/templates/objects/object_create.html.tera
index a403399..8e86fd1 100644
--- a/yopa-web/resources/templates/objects/object_create.html.tera
+++ b/yopa-web/resources/templates/objects/object_create.html.tera
@@ -14,7 +14,6 @@ Create {{model.name}}
diff --git a/yopa-web/resources/templates/objects/object_update.html.tera b/yopa-web/resources/templates/objects/object_update.html.tera
new file mode 100644
index 0000000..014ee41
--- /dev/null
+++ b/yopa-web/resources/templates/objects/object_update.html.tera
@@ -0,0 +1,21 @@
+{% extends "_layout" %}
+
+{% block title -%}
+Edit {{object.name}}
+{%- endblock %}
+
+{% block nav -%}
+Home
+{%- endblock %}
+
+{% block content -%}
+
+
+
+
+
+{%- endblock %}
diff --git a/yopa-web/src/main.rs b/yopa-web/src/main.rs
index 3caede1..ba98d27 100644
--- a/yopa-web/src/main.rs
+++ b/yopa-web/src/main.rs
@@ -157,6 +157,7 @@ async fn main() -> std::io::Result<()> {
.service(routes::objects::create_form)
.service(routes::objects::create)
.service(routes::objects::detail)
+ .service(routes::objects::update_form)
//
.service(static_files)
.default_service(web::to(|| HttpResponse::NotFound().body("File or endpoint not found")))
diff --git a/yopa-web/src/routes/objects.rs b/yopa-web/src/routes/objects.rs
index ab5f261..3f1dbb0 100644
--- a/yopa-web/src/routes/objects.rs
+++ b/yopa-web/src/routes/objects.rs
@@ -4,7 +4,7 @@ use crate::session_ext::SessionExt;
use crate::routes::models::object::ObjectModelForm;
use crate::TERA;
use crate::tera_ext::TeraExt;
-use yopa::{ID, model};
+use yopa::{ID, model, Storage, data};
use yopa::data::Object;
use serde::{Serialize,Deserialize};
use yopa::insert::InsertObj;
@@ -14,6 +14,8 @@ use serde_json::Value;
use itertools::Itertools;
use yopa::model::{ObjectModel, PropertyModel, RelationModel};
use heck::TitleCase;
+use std::collections::HashMap;
+use json_dotpath::DotPaths;
// we only need references here, Context serializes everything to Value.
// cloning would be a waste of cycles
@@ -48,6 +50,17 @@ pub(crate) async fn create_form(
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 {
+ 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 = relations.iter().map(|r| r.id).collect();
@@ -61,19 +74,15 @@ pub(crate) async fn create_form(
related_ids.sort();
related_ids.dedup();
- let form_data = ObjectCreateData {
+ Ok(ObjectCreateData {
model_id: model.id,
schema: Schema {
obj_models: rg.get_object_models().collect(), // TODO get only the ones that matter here
rel_models: relations,
- prop_models: rg.get_property_models_for_parents(&prop_object_ids).collect()
+ prop_models: rg.get_property_models_for_parents(prop_object_ids).collect()
},
- objects: rg.get_objects_of_type(&related_ids).collect()
- };
-
- context.insert("form_data", &form_data);
-
- TERA.build_response("objects/object_create", &context)
+ objects: rg.get_objects_of_type(related_ids).collect()
+ })
}
#[post("/object/create")]
@@ -189,7 +198,7 @@ pub(crate) async fn detail(
let mut ids_to_get_values_for = relations.iter().map(|r| r.id).collect_vec();
ids_to_get_values_for.push(object_id);
let mut grouped_values = {
- rg.get_grouped_values_for_objects(&ids_to_get_values_for)
+ rg.get_grouped_values_for_objects(ids_to_get_values_for)
};
// object's own properties
@@ -252,3 +261,120 @@ pub(crate) async fn detail(
TERA.build_response("objects/object_detail", &context)
}
+
+#[derive(Serialize,Debug,Clone)]
+struct EnrichedObject<'a> {
+ id: ID,
+ model: ID,
+ name: String,
+ values: HashMap>,
+ relations: HashMap>>,
+}
+
+#[derive(Serialize,Debug,Clone)]
+struct EnrichedRelation<'a> {
+ id: ID,
+ object: ID,
+ model: ID,
+ related: ID,
+ values: HashMap>,
+}
+
+// FIXME relation values are now showing in the edit form!
+// TODO save handling
+
+#[get("/object/update/{id}")]
+pub(crate) async fn update_form(
+ 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;
+
+ 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", &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)
+ .into_group_map_by(|v| v.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 rel_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 grouped_relations = relations.iter()
+ .into_group_map_by(|relation| relation.model);
+
+ let mut relation_properties = rg.get_grouped_prop_models_for_parents(rel_ids.clone());
+
+ let mut relation_values = rg.get_grouped_values_for_objects(rel_ids.clone());
+
+ for (rel_model_id, relations) in grouped_relations {
+ let mut instances = vec![];
+ let props_for_rel = relation_properties.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.remove(&rel.id).unwrap_or_default().into_iter()
+ .into_group_map_by(|relation| relation.model);
+
+ props_for_rel.iter().for_each(|prop_model| {
+ relation_values_map.insert(prop_model.id.to_string(), rel_values.remove(&prop_model.id).unwrap_or_default());
+ });
+
+ let enriched = EnrichedRelation {
+ id: rel.id,
+ object: rel.object,
+ model: rel.model,
+ related: rel.related,
+ values: relation_values_map
+ };
+
+ instances.push(enriched);
+ }
+
+ 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: object.name.clone(),
+ values: value_map,
+ relations: relation_map
+ };
+
+ form.dot_set("object", object);
+ context.insert("form_data", &form);
+
+ TERA.build_response("objects/object_update", &context)
+}
diff --git a/yopa/src/lib.rs b/yopa/src/lib.rs
index 4750986..92ea26d 100644
--- a/yopa/src/lib.rs
+++ b/yopa/src/lib.rs
@@ -368,7 +368,7 @@ impl Storage {
.into_group_map_by(|model| model.object)
}
- pub fn get_grouped_prop_models_for_parents<'p, 'a : 'p>(&'a self, parents: &'p [ID]) -> HashMap> {
+ pub fn get_grouped_prop_models_for_parents(&self, parents: Vec) -> HashMap> {
self.prop_models.values()
.filter(|p| parents.contains(&p.object))
.into_group_map_by(|model| model.object)
@@ -384,7 +384,7 @@ impl Storage {
.filter(move |prop| prop.object == object_id)
}
- pub fn get_grouped_values_for_objects<'p, 'a : 'p>(&'a self, parents: &'p [ID]) -> HashMap> {
+ pub fn get_grouped_values_for_objects(&self, parents: Vec) -> HashMap> {
self.properties.values()
.filter(move |prop| parents.contains(&prop.object))
.into_group_map_by(|model| model.object)
@@ -395,12 +395,12 @@ impl Storage {
.filter(move |model| model.object == model_id)
}
- pub fn get_property_models_for_parents<'p, 'a : 'p>(&'a self, parents: &'p [ID]) -> impl Iterator- {
+ pub fn get_property_models_for_parents(&self, parents: Vec) -> impl Iterator
- {
self.prop_models.values()
.filter(move |model| parents.contains(&model.object))
}
- pub fn get_objects_of_type<'p, 'a : 'p>(&'a self, model_ids : &'p [ID]) -> impl Iterator
- {
+ pub fn get_objects_of_type(&self, model_ids : Vec) -> impl Iterator
- {
self.objects.values()
.filter(move |object| model_ids.contains(&object.model))
}