From 4d2d625d252241a428f6bbec7dbb91b456a21b06 Mon Sep 17 00:00:00 2001 From: Manuel Gugger Date: Wed, 27 Jul 2022 00:37:49 +0200 Subject: [PATCH] implement basic error handling --- actix_admin/actix_admin_macros/src/lib.rs | 16 ++--- .../actix_admin_macros/src/struct_fields.rs | 4 +- actix_admin/src/lib.rs | 8 +-- actix_admin/src/model.rs | 59 ++++++++++-------- actix_admin/src/routes/create_get.rs | 10 +-- actix_admin/src/routes/create_post.rs | 27 ++++++-- actix_admin/src/routes/edit_get.rs | 1 - actix_admin/src/routes/edit_post.rs | 25 +++++++- actix_admin/templates/edit.html | 22 +++++-- database.db-wal | Bin 70072 -> 94792 bytes 10 files changed, 114 insertions(+), 58 deletions(-) diff --git a/actix_admin/actix_admin_macros/src/lib.rs b/actix_admin/actix_admin_macros/src/lib.rs index 3e14346..d593511 100644 --- a/actix_admin/actix_admin_macros/src/lib.rs +++ b/actix_admin/actix_admin_macros/src/lib.rs @@ -78,7 +78,7 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea values: hashmap![ #(#fields_for_from_model),* ], - errors: Vec::new() + errors: HashMap::new() } } } @@ -103,7 +103,8 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea async fn create_entity(db: &DatabaseConnection, mut model: ActixAdminModel) -> ActixAdminModel { let mut validation_errs = Entity::validate_model(&model); - model.errors.append(&mut validation_errs); + //model.errors.append(&mut validation_errs); + model.errors = validation_errs; if !model.has_errors() { let new_model = ActiveModel::from(model.clone()); @@ -123,7 +124,8 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea async fn edit_entity(db: &DatabaseConnection, id: i32, mut model: ActixAdminModel) -> ActixAdminModel { let mut validation_errs = Entity::validate_model(&model); - model.errors.append(&mut validation_errs); + //model.errors.append(&mut validation_errs); + model.errors=validation_errs; if !model.has_errors() { let entity: Option = Entity::find_by_id(id).one(db).await.unwrap(); @@ -182,13 +184,13 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea } - fn validate_model(model: &ActixAdminModel) -> Vec { - let mut errors = Vec::::new(); + fn validate_model(model: &ActixAdminModel) -> HashMap { + let mut errors = HashMap::::new(); #(#fields_for_validate_model);*; - let mut custom_errors = Entity.validate(); - errors.append(&mut custom_errors); + //let mut custom_errors = Entity.validate(); + //errors.append(&mut custom_errors); errors } diff --git a/actix_admin/actix_admin_macros/src/struct_fields.rs b/actix_admin/actix_admin_macros/src/struct_fields.rs index 9f4dfae..097fda3 100644 --- a/actix_admin/actix_admin_macros/src/struct_fields.rs +++ b/actix_admin/actix_admin_macros/src/struct_fields.rs @@ -221,12 +221,12 @@ pub fn get_fields_for_validate_model(fields: &Vec) -> Vec { let inner_ty = model_field.inner_type.to_owned().unwrap(); quote! { - model.get_value::<#inner_ty>(#ident_name).map_err(|err| errors.push(err)).ok(); + model.get_value::<#inner_ty>(#ident_name).map_err(|err| errors.insert(#ident_name.to_string(), err)).ok(); } }, false => { quote! { - model.get_value::<#ty>(#ident_name).map_err(|err| errors.push(err)).ok(); + model.get_value::<#ty>(#ident_name).map_err(|err| errors.insert(#ident_name.to_string(), err)).ok(); } } } diff --git a/actix_admin/src/lib.rs b/actix_admin/src/lib.rs index 93918ce..0b6647f 100644 --- a/actix_admin/src/lib.rs +++ b/actix_admin/src/lib.rs @@ -14,7 +14,7 @@ pub mod prelude { pub use crate::model::{ ActixAdminModel, ActixAdminModelTrait}; pub use crate::view_model::{ ActixAdminViewModel, ActixAdminViewModelTrait, ActixAdminViewModelField}; pub use actix_admin_macros::{ DeriveActixAdminModel, DeriveActixAdminSelectList }; - pub use crate::{ ActixAdminAppDataTrait, ActixAdmin, ActixAdminError }; + pub use crate::{ ActixAdminAppDataTrait, ActixAdmin }; pub use crate::{ hashmap, ActixAdminSelectListTrait }; } @@ -35,12 +35,6 @@ lazy_static! { Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")).unwrap(); } -#[derive(Clone, Debug, Serialize)] -pub struct ActixAdminError { - field_name: Option, - error: String -} - // AppDataTrait pub trait ActixAdminAppDataTrait { fn get_db(&self) -> &DatabaseConnection; diff --git a/actix_admin/src/model.rs b/actix_admin/src/model.rs index 33784ff..0115e63 100644 --- a/actix_admin/src/model.rs +++ b/actix_admin/src/model.rs @@ -1,9 +1,8 @@ +use crate::ActixAdminViewModelField; use async_trait::async_trait; use sea_orm::DatabaseConnection; -use serde::{Serialize}; +use serde::Serialize; use std::collections::HashMap; -use crate::ActixAdminViewModelField; -use crate::ActixAdminError; #[async_trait] pub trait ActixAdminModelTrait { @@ -13,11 +12,10 @@ pub trait ActixAdminModelTrait { posts_per_page: usize, ) -> (usize, Vec); fn get_fields() -> Vec; - fn validate_model(model: &ActixAdminModel) -> Vec; - + fn validate_model(model: &ActixAdminModel) -> HashMap; // function to be overridable for custom error handling - fn validate(&self) -> Vec { - return Vec::new() + fn validate(&self) -> HashMap { + return HashMap::new(); } } @@ -25,7 +23,7 @@ pub trait ActixAdminModelTrait { pub struct ActixAdminModel { pub primary_key: Option, pub values: HashMap, - pub errors: Vec, + pub errors: HashMap, } impl From for ActixAdminModel { @@ -33,38 +31,49 @@ impl From for ActixAdminModel { let mut hashmap = HashMap::new(); let key_values: Vec<&str> = string.split('&').collect(); for key_value in key_values { - let mut iter = key_value.splitn(2, '='); - hashmap.insert( - iter.next().unwrap().to_string(), - iter.next().unwrap().to_string(), - ); + if !key_value.is_empty() { + let mut iter = key_value.splitn(2, '='); + hashmap.insert( + iter.next().unwrap().to_string(), + iter.next().unwrap().to_string(), + ); + } } - ActixAdminModel { primary_key: None, values: hashmap, errors: Vec::new() } + ActixAdminModel { + primary_key: None, + values: hashmap, + errors: HashMap::new(), + } } } impl ActixAdminModel { - pub fn get_value(&self, key: &str) -> Result, ActixAdminError> { + pub fn get_value(&self, key: &str) -> Result, String> { let value = self.values.get(key); - let res: Result, ActixAdminError> = match value { + println!("{:?}", key); + println!("{:?}", value); + + let res: Result, String> = match value { Some(val) => { - let parsed_val = val.parse::(); + if val.is_empty() { + return Ok(None); + } + + let parsed_val = val.parse::(); + match parsed_val { Ok(val) => Ok(Some(val)), - Err(_) => Err(ActixAdminError { - field_name: Some(key.to_string()), - error: "Invalid Value".to_string() - }) + Err(_) => Err("Invalid Value".to_string()), } - }, - _ => Ok(None) + } + _ => Ok(None), }; res } pub fn has_errors(&self) -> bool { - return &self.errors.len() != &0 + return &self.errors.len() != &0; } -} \ No newline at end of file +} diff --git a/actix_admin/src/routes/create_get.rs b/actix_admin/src/routes/create_get.rs index 2925c4c..7812165 100644 --- a/actix_admin/src/routes/create_get.rs +++ b/actix_admin/src/routes/create_get.rs @@ -9,24 +9,26 @@ pub async fn create_get( _req: HttpRequest, data: web::Data, _body: web::Payload, - _text: String, + text: String, ) -> Result { - let _db = &data.get_db(); + let db = &data.get_db(); let entity_name = E::get_entity_name(); let entity_names = &data.get_actix_admin().entity_names; let actix_admin = data.get_actix_admin(); let view_model = actix_admin.view_models.get(&entity_name).unwrap(); + let mut model = ActixAdminModel::from(text); let mut ctx = Context::new(); ctx.insert("entity_names", &entity_names); ctx.insert("view_model", &view_model); - ctx.insert("select_lists", &E::get_select_lists(_db).await); + ctx.insert("select_lists", &E::get_select_lists(db).await); ctx.insert("list_link", &E::get_list_link(&entity_name)); + ctx.insert("model", &model); let body = TERA - .render("create.html", &ctx) + .render("edit.html", &ctx) .map_err(|err| error::ErrorInternalServerError(err))?; Ok(HttpResponse::Ok().content_type("text/html").body(body)) } \ No newline at end of file diff --git a/actix_admin/src/routes/create_post.rs b/actix_admin/src/routes/create_post.rs index d68c296..ac51d90 100644 --- a/actix_admin/src/routes/create_post.rs +++ b/actix_admin/src/routes/create_post.rs @@ -1,5 +1,7 @@ use actix_web::http::header; -use actix_web::{web, Error, HttpRequest, HttpResponse}; +use actix_web::{web, error, Error, HttpRequest, HttpResponse}; +use tera::{Context}; +use crate::TERA; use crate::prelude::*; @@ -10,15 +12,32 @@ pub async fn create_post ) -> Result { let db = &data.get_db(); let entity_name = E::get_entity_name(); + let entity_names = &data.get_actix_admin().entity_names; let actix_admin = data.get_actix_admin(); let view_model = actix_admin.view_models.get(&entity_name).unwrap(); - let mut _admin_model = ActixAdminModel::from(text); - _admin_model = E::create_entity(db, _admin_model).await; + let mut model = ActixAdminModel::from(text); + model = E::create_entity(db, model).await; - Ok(HttpResponse::Found() + if model.has_errors() { + let mut ctx = Context::new(); + ctx.insert("entity_names", &entity_names); + ctx.insert("view_model", &view_model); + ctx.insert("select_lists", &E::get_select_lists(db).await); + ctx.insert("list_link", &E::get_list_link(&entity_name)); + ctx.insert("model", &model); + println!("{:?}", model.errors); + + let body = TERA + .render("edit.html", &ctx) + .map_err(|err| error::ErrorInternalServerError(err))?; + Ok(HttpResponse::Ok().content_type("text/html").body(body)) + } + else { + Ok(HttpResponse::Found() .append_header(( header::LOCATION, format!("/admin/{}/list", view_model.entity_name), )) .finish()) + } } \ No newline at end of file diff --git a/actix_admin/src/routes/edit_get.rs b/actix_admin/src/routes/edit_get.rs index 79ff08a..70eeb8b 100644 --- a/actix_admin/src/routes/edit_get.rs +++ b/actix_admin/src/routes/edit_get.rs @@ -8,7 +8,6 @@ use crate::TERA; pub async fn edit_get( _req: HttpRequest, data: web::Data, - _body: web::Payload, _text: String, id: web::Path ) -> Result { diff --git a/actix_admin/src/routes/edit_post.rs b/actix_admin/src/routes/edit_post.rs index e62388c..c28cf9b 100644 --- a/actix_admin/src/routes/edit_post.rs +++ b/actix_admin/src/routes/edit_post.rs @@ -1,7 +1,10 @@ use actix_web::http::header; -use actix_web::{web, Error, HttpRequest, HttpResponse}; +use actix_web::{web, error, Error, HttpRequest, HttpResponse}; +use tera::{Context}; +use crate::TERA; use crate::prelude::*; +use super::edit_get::edit_get; pub async fn edit_post( _req: HttpRequest, @@ -12,14 +15,30 @@ pub async fn edit_post( let db = &data.get_db(); let entity_name = E::get_entity_name(); let actix_admin = data.get_actix_admin(); + let entity_names = &data.get_actix_admin().entity_names; let view_model = actix_admin.view_models.get(&entity_name).unwrap(); - let mut _admin_model = ActixAdminModel::from(text); - _admin_model = E::edit_entity(db, id.into_inner(), _admin_model).await; + let mut model = ActixAdminModel::from(text); + model = E::edit_entity(db, id.into_inner(), model).await; + if model.has_errors() { + let mut ctx = Context::new(); + ctx.insert("entity_names", &entity_names); + ctx.insert("view_model", &view_model); + ctx.insert("model", &model); + ctx.insert("select_lists", &E::get_select_lists(db).await); + ctx.insert("list_link", &E::get_list_link(&entity_name)); + + let body = TERA + .render("edit.html", &ctx) + .map_err(|err| error::ErrorInternalServerError(err))?; + Ok(HttpResponse::Ok().content_type("text/html").body(body)) + } + else { Ok(HttpResponse::Found() .append_header(( header::LOCATION, format!("/admin/{}/list", view_model.entity_name), )) .finish()) + } } \ No newline at end of file diff --git a/actix_admin/templates/edit.html b/actix_admin/templates/edit.html index 0d5a2d6..53fe937 100644 --- a/actix_admin/templates/edit.html +++ b/actix_admin/templates/edit.html @@ -2,26 +2,38 @@ {% block content %}
+
+
    + {{ model.errors }} +
+
{% for model_field in view_model.fields -%} {% else %} - + aria-label="{{ model_field.field_name }}"> + {% if model.errors | get(key=model_field.field_name, default="") != "" %} + placeholder="Invalid" aria-invalid="true" + {% endif %} {% endif %} {%- endfor %} diff --git a/database.db-wal b/database.db-wal index 82479d86dc7854f21a71dd442e563b4b26db2805..7d322e67bb64264050b7251c551c98d2188328d1 100644 GIT binary patch delta 579 zcmdn7nB~M1)`l&NI|S7j7#Ns{D{QF?^dA2a=3#^kQwYx z1|;KD7}=)RGBT=7*51e~v9;SqR9t<-EhJ|i=BJS}rLa3w6zD`jL1hMQAi#8{G60aI Bs6zk% delta 11 ScmX@{gmuSamWC~iI|KnD*#!>(