add persistence
This commit is contained in:
+3
-1
@@ -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", features = [] }
|
||||
yopa = { path = "../yopa", features = ["uuid-ids"] }
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
heck = "0.3.2"
|
||||
@@ -24,6 +24,8 @@ once_cell = "1.5.2"
|
||||
rand = "0.8.3"
|
||||
itertools = "0.10.0"
|
||||
json_dotpath = "1.0.3"
|
||||
anyhow = "1.0.38"
|
||||
thiserror = "1.0.24"
|
||||
|
||||
tokio = { version="0.2.6", features=["full"] }
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ export function objCopy(object) {
|
||||
}
|
||||
|
||||
export function castId(id) {
|
||||
// TODO no-op after switching to UUIDs
|
||||
return +id;
|
||||
return id.toString();
|
||||
//return +id;
|
||||
}
|
||||
|
||||
// like _.isEmpty, but less stupid
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+8
-129
@@ -2,6 +2,8 @@
|
||||
extern crate actix_web;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate thiserror;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
@@ -106,7 +108,8 @@ async fn main() -> std::io::Result<()> {
|
||||
// Ensure the lazy ref is initialized early (to catch template bugs ASAP)
|
||||
let _ = TERA.deref();
|
||||
|
||||
let yopa_store: YopaStoreWrapper = init_yopa();
|
||||
let yopa_store: YopaStoreWrapper = init_yopa()
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
|
||||
let mut session_key = [0u8; 32];
|
||||
rand::thread_rng().fill(&mut session_key);
|
||||
@@ -164,133 +167,9 @@ async fn main() -> std::io::Result<()> {
|
||||
.await
|
||||
}
|
||||
|
||||
fn init_yopa() -> YopaStoreWrapper {
|
||||
let mut store = Storage::new();
|
||||
fn init_yopa() -> anyhow::Result<YopaStoreWrapper> {
|
||||
let mut store = Storage::new_bincode(std::env::current_dir()?.join("yopa-store.dat"));
|
||||
store.load()?;
|
||||
|
||||
/*
|
||||
|
||||
// Seed the store with some dummy data for view development
|
||||
use yopa::model;
|
||||
use yopa::DataType;
|
||||
|
||||
let id_recipe = store
|
||||
.define_object(model::ObjectModel {
|
||||
id: Default::default(),
|
||||
name: "Recipe".to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let id_book = store
|
||||
.define_object(model::ObjectModel {
|
||||
id: Default::default(),
|
||||
name: "Book".to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let _id_ing = store
|
||||
.define_object(model::ObjectModel {
|
||||
id: Default::default(),
|
||||
name: "Ingredient".to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let val_descr = store
|
||||
.define_property(model::PropertyModel {
|
||||
id: Default::default(),
|
||||
object: id_recipe,
|
||||
name: "description".to_string(),
|
||||
optional: true,
|
||||
multiple: true,
|
||||
data_type: DataType::String,
|
||||
default: TypedValue::String("".into()),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
store
|
||||
.define_property(model::PropertyModel {
|
||||
id: Default::default(),
|
||||
object: id_book,
|
||||
name: "author".to_string(),
|
||||
optional: true,
|
||||
multiple: true,
|
||||
data_type: DataType::String,
|
||||
default: TypedValue::String("Pepa Novák".into()),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let rel_book_id = store
|
||||
.define_relation(model::RelationModel {
|
||||
id: Default::default(),
|
||||
object: id_recipe,
|
||||
name: "book reference".to_string(),
|
||||
reciprocal_name: "recipes".to_string(),
|
||||
optional: true,
|
||||
multiple: true,
|
||||
related: id_book,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let page = store
|
||||
.define_property(model::PropertyModel {
|
||||
id: Default::default(),
|
||||
object: rel_book_id,
|
||||
name: "page".to_string(),
|
||||
optional: true,
|
||||
multiple: false,
|
||||
data_type: DataType::Integer,
|
||||
default: TypedValue::Integer(0),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
store
|
||||
.define_relation(model::RelationModel {
|
||||
id: Default::default(),
|
||||
object: id_recipe,
|
||||
name: "related recipe".to_string(),
|
||||
reciprocal_name: "related recipe".to_string(),
|
||||
optional: true,
|
||||
multiple: true,
|
||||
related: id_recipe,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let book1 = store
|
||||
.insert_object(InsertObj {
|
||||
model: id_book,
|
||||
name: "Book 1".to_string(),
|
||||
values: vec![],
|
||||
relations: vec![],
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
store
|
||||
.insert_object(InsertObj {
|
||||
model: id_book,
|
||||
name: "Book 2".to_string(),
|
||||
values: vec![],
|
||||
relations: vec![],
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
store
|
||||
.insert_object(InsertObj {
|
||||
model: id_recipe,
|
||||
name: "Recipe1".to_string(),
|
||||
values: vec![InsertValue {
|
||||
model: val_descr,
|
||||
value: TypedValue::String("Bla bla bla".into()),
|
||||
}],
|
||||
relations: vec![InsertRel {
|
||||
model: rel_book_id,
|
||||
related: book1,
|
||||
values: vec![InsertValue {
|
||||
model: page,
|
||||
value: TypedValue::Integer(15),
|
||||
}],
|
||||
}],
|
||||
})
|
||||
.unwrap();
|
||||
*/
|
||||
|
||||
web::Data::new(tokio::sync::RwLock::new(store))
|
||||
Ok(web::Data::new(tokio::sync::RwLock::new(store)))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use actix_session::Session;
|
||||
use actix_web::{web, Responder};
|
||||
use actix_web::{web, Responder, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use yopa::model::{ObjectModel, PropertyModel};
|
||||
@@ -8,7 +8,7 @@ use yopa::ID;
|
||||
use crate::routes::models::relation::RelationModelDisplay;
|
||||
use crate::session_ext::SessionExt;
|
||||
use crate::tera_ext::TeraExt;
|
||||
use crate::utils::redirect;
|
||||
use crate::utils::{redirect, StorageErrorIntoResponseError};
|
||||
use crate::TERA;
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
@@ -45,7 +45,7 @@ pub(crate) async fn create(
|
||||
form: web::Form<ObjectModelForm>,
|
||||
store: crate::YopaStoreWrapper,
|
||||
session: Session,
|
||||
) -> actix_web::Result<impl Responder> {
|
||||
) -> actix_web::Result<HttpResponse> {
|
||||
let mut wg = store.write().await;
|
||||
let form = form.into_inner();
|
||||
match wg.define_object(ObjectModel {
|
||||
@@ -53,6 +53,7 @@ pub(crate) async fn create(
|
||||
name: form.name.clone(),
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
debug!("Object created, redirecting to root");
|
||||
session.flash_success(format!("Object model \"{}\" created.", form.name));
|
||||
redirect("/models")
|
||||
@@ -108,6 +109,7 @@ pub(crate) async fn update(
|
||||
name: form.name.clone(),
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
debug!("Object updated, redirecting to root");
|
||||
session.flash_success(format!("Object model \"{}\" updated.", form.name));
|
||||
redirect("/models")
|
||||
@@ -133,6 +135,7 @@ pub(crate) async fn delete(
|
||||
.map_err(|e| actix_web::error::ErrorBadRequest(e))?,
|
||||
) {
|
||||
Ok(om) => {
|
||||
wg.persist().err_to_500()?;
|
||||
debug!("Object model deleted, redirecting to root");
|
||||
session.flash_success(format!("Object model \"{}\" deleted.", om.name));
|
||||
redirect("/models")
|
||||
|
||||
@@ -8,7 +8,7 @@ use yopa::{DataType, TypedValue, ID};
|
||||
use crate::routes::models::relation::ObjectOrRelationModelDisplay;
|
||||
use crate::session_ext::SessionExt;
|
||||
use crate::tera_ext::TeraExt;
|
||||
use crate::utils::{redirect, ParseOrBadReq};
|
||||
use crate::utils::{redirect, ParseOrBadReq, StorageErrorIntoResponseError};
|
||||
use crate::TERA;
|
||||
|
||||
#[get("/model/property/create/{object_id}")]
|
||||
@@ -148,6 +148,7 @@ pub(crate) async fn create(
|
||||
default,
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
debug!("Property created, redirecting to root");
|
||||
session.flash_success(format!("Property model \"{}\" created.", form.name));
|
||||
redirect("/models")
|
||||
@@ -170,6 +171,7 @@ pub(crate) async fn delete(
|
||||
let mut wg = store.write().await;
|
||||
match wg.undefine_property(id.parse_or_bad_request()?) {
|
||||
Ok(rm) => {
|
||||
wg.persist().err_to_500()?;
|
||||
debug!("Property deleted, redirecting to root");
|
||||
session.flash_success(format!("Property \"{}\" deleted.", rm.name));
|
||||
redirect("/models")
|
||||
@@ -256,7 +258,8 @@ pub(crate) async fn update(
|
||||
default,
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
debug!("Relation updated, redirecting to root");
|
||||
wg.persist().err_to_500()?;
|
||||
debug!("Property updated, redirecting to root");
|
||||
session.flash_success(format!("Property \"{}\" updated.", form.name));
|
||||
redirect("/models")
|
||||
}
|
||||
@@ -264,7 +267,7 @@ pub(crate) async fn update(
|
||||
warn!("Error updating model: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
session.set("old", form).unwrap();
|
||||
redirect(format!("/model/relation/update/{}", id))
|
||||
redirect(format!("/model/property/update/{}", id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use yopa::ID;
|
||||
|
||||
use crate::session_ext::SessionExt;
|
||||
use crate::tera_ext::TeraExt;
|
||||
use crate::utils::{redirect, ParseOrBadReq};
|
||||
use crate::utils::{redirect, ParseOrBadReq, StorageErrorIntoResponseError};
|
||||
use crate::TERA;
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
@@ -90,6 +90,7 @@ pub(crate) async fn create(
|
||||
related: form.related,
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
debug!("Relation created, redirecting to root");
|
||||
session.flash_success(format!("Relation model \"{}\" created.", form.name));
|
||||
redirect("/models")
|
||||
@@ -118,6 +119,7 @@ pub(crate) async fn delete(
|
||||
let mut wg = store.write().await;
|
||||
match wg.undefine_relation(id.parse_or_bad_request()?) {
|
||||
Ok(rm) => {
|
||||
wg.persist().err_to_500()?;
|
||||
debug!("Relation deleted, redirecting to root");
|
||||
session.flash_success(format!("Relation model \"{}\" deleted.", rm.name));
|
||||
redirect("/models")
|
||||
@@ -189,6 +191,7 @@ pub(crate) async fn update(
|
||||
related: Default::default(), // dummy
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
debug!("Relation updated, redirecting to root");
|
||||
session.flash_success(format!("Relation model \"{}\" updated.", form.name));
|
||||
redirect("/models")
|
||||
|
||||
@@ -3,7 +3,7 @@ use actix_session::Session;
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
|
||||
use crate::tera_ext::TeraExt;
|
||||
use crate::utils::redirect;
|
||||
use crate::utils::{redirect, StorageErrorIntoResponseError};
|
||||
use crate::TERA;
|
||||
use serde::Serialize;
|
||||
use yopa::data::Object;
|
||||
@@ -110,6 +110,7 @@ pub(crate) async fn create(
|
||||
|
||||
match wg.insert_object(form) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
debug!("Object created, redirecting to root");
|
||||
session.flash_success(format!("{} \"{}\" created.", model_name, name));
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
@@ -512,6 +513,7 @@ pub(crate) async fn update(
|
||||
|
||||
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, name));
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
@@ -532,6 +534,7 @@ pub(crate) async fn delete(
|
||||
let mut wg = store.write().await;
|
||||
match wg.delete_object(*id) {
|
||||
Ok(obj) => {
|
||||
wg.persist().err_to_500()?;
|
||||
debug!("Object deleted, redirecting to root");
|
||||
session.flash_success(format!("Object \"{}\" deleted.", obj.name));
|
||||
redirect("/")
|
||||
|
||||
+22
-1
@@ -1,7 +1,8 @@
|
||||
use actix_web::http::header::IntoHeaderValue;
|
||||
use actix_web::HttpResponse;
|
||||
use actix_web::{HttpResponse, ResponseError, Error};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::str::FromStr;
|
||||
use yopa::StorageError;
|
||||
|
||||
pub fn redirect(path: impl IntoHeaderValue) -> actix_web::Result<HttpResponse> {
|
||||
Ok(HttpResponse::SeeOther()
|
||||
@@ -38,3 +39,23 @@ impl ParseOrBadReq for String {
|
||||
self.as_str().parse_or_bad_request()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ActixErrorWrapper {
|
||||
#[error(transparent)]
|
||||
Storage(#[from] StorageError),
|
||||
}
|
||||
|
||||
impl ResponseError for ActixErrorWrapper {
|
||||
|
||||
}
|
||||
|
||||
pub trait StorageErrorIntoResponseError<T> {
|
||||
fn err_to_500(self) -> Result<T, ActixErrorWrapper>;
|
||||
}
|
||||
|
||||
impl<T> StorageErrorIntoResponseError<T> for Result<T, StorageError> {
|
||||
fn err_to_500(self) -> Result<T, ActixErrorWrapper> {
|
||||
self.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user