add persistence

This commit is contained in:
2021-02-20 20:05:25 +01:00
parent 1369db85e8
commit 4e26080820
14 changed files with 196 additions and 149 deletions
+3 -1
View File
@@ -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"] }
+2 -2
View File
@@ -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
View File
@@ -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)))
}
+6 -3
View File
@@ -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")
+6 -3
View File
@@ -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))
}
}
}
+4 -1
View File
@@ -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")
+4 -1
View File
@@ -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
View File
@@ -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())
}
}