improving depot code for user and static repo examples

This commit is contained in:
Gabor Körber 2024-07-26 12:56:23 +02:00
parent 843e432ec4
commit 15e60e6325
10 changed files with 78 additions and 40 deletions

View File

@ -0,0 +1,12 @@
UPDATE:
- The Item is edited successfully: Success Message
- The Item could not be saved, it needs adjustment: Form Errors, Warning Message?
- The Item could not be saved, there was an Error of some sort: Error Message
Widget -builds-> Field
Validation: https://github.com/tokio-rs/axum/blob/main/examples/validator/src/main.rs
AvoRED PRoject seems similar: https://github.com/avored/avored-rust-cms

View File

@ -99,10 +99,21 @@ impl Into<Option<RepositoryItem>> for RepositoryResponse {
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum RepositoryError { pub enum RepositoryError {
// used internally.
#[error("key not found in downcast?")]
WrapperDowncastError,
// to be used by repositories:
#[error("repository item not found")]
ItemNotFound,
#[error("database error: {0}")]
DatabaseError(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("external error: {0}")]
ExternalError(#[source] Box<dyn std::error::Error + Send + Sync>),
// to be removed or refactored:
#[error("an unknown occurred: {0}")] #[error("an unknown occurred: {0}")]
UnknownError(String), UnknownError(String),
#[error("key not found in downcast?")]
KeyNotFound,
} }
pub type RepositoryResult = Result<RepositoryResponse, RepositoryError>; pub type RepositoryResult = Result<RepositoryResponse, RepositoryError>;
@ -332,7 +343,7 @@ impl<T: DepotRepository> DynDepotRepository for DepotRepositoryWrapper<T> {
if let Some(key) = id.as_any().downcast_ref::<T::Key>() { if let Some(key) = id.as_any().downcast_ref::<T::Key>() {
self.inner.get(context, key).await self.inner.get(context, key).await
} else { } else {
Err(RepositoryError::KeyNotFound) Err(RepositoryError::WrapperDowncastError)
} }
} }
@ -349,7 +360,7 @@ impl<T: DepotRepository> DynDepotRepository for DepotRepositoryWrapper<T> {
if let Some(key) = id.as_any().downcast_ref::<T::Key>() { if let Some(key) = id.as_any().downcast_ref::<T::Key>() {
self.inner.update(context, key, data).await self.inner.update(context, key, data).await
} else { } else {
Err(RepositoryError::KeyNotFound) Err(RepositoryError::WrapperDowncastError)
} }
} }
@ -362,7 +373,7 @@ impl<T: DepotRepository> DynDepotRepository for DepotRepositoryWrapper<T> {
if let Some(key) = id.as_any().downcast_ref::<T::Key>() { if let Some(key) = id.as_any().downcast_ref::<T::Key>() {
self.inner.replace(context, key, data).await self.inner.replace(context, key, data).await
} else { } else {
Err(RepositoryError::KeyNotFound) Err(RepositoryError::WrapperDowncastError)
} }
} }

View File

@ -36,15 +36,15 @@ impl Widget {
} }
pub fn default() -> Self { pub fn default() -> Self {
Self::widget("/admin/widgets/input_text.jinja") Self::widget("/depot/widgets/input_text.jinja")
} }
pub fn textarea() -> Self { pub fn textarea() -> Self {
Self::widget("/admin/widgets/input_textarea.jinja") Self::widget("/depot/widgets/input_textarea.jinja")
} }
pub fn checkbox() -> Self { pub fn checkbox() -> Self {
Self::widget("/admin/widgets/checkbox_toggle.jinja") Self::widget("/depot/widgets/checkbox_toggle.jinja")
} }
pub fn required(mut self) -> Self { pub fn required(mut self) -> Self {

View File

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use log::debug; use log::{debug, warn};
use rear::depot::prelude::*; use rear::depot::prelude::*;
use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, ModelTrait, Set}; use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, ModelTrait, Set};
use serde_json::Value; use serde_json::Value;
@ -136,8 +136,8 @@ impl DepotRepository for UserRepository {
let user: Option<entity::user::Model> = entity::User::find_by_id(id) let user: Option<entity::user::Model> = entity::User::find_by_id(id)
.one(&self.connection) .one(&self.connection)
.await .await
.unwrap(); .map_err(|e| RepositoryError::DatabaseError(Box::new(e)))?;
let mut user: entity::user::ActiveModel = user.unwrap().into(); let mut user: entity::user::ActiveModel = user.ok_or(RepositoryError::ItemNotFound)?.into();
// should we really allow username change? // should we really allow username change?
if let Some(value) = data.get("username") { if let Some(value) = data.get("username") {
@ -170,14 +170,18 @@ impl DepotRepository for UserRepository {
} }
// update // update
if let Ok(user) = user.update(&self.connection).await { match user.update(&self.connection).await {
Ok(user) => {
let id = user.id.to_string(); let id = user.id.to_string();
return Ok(RepositoryResponse::ItemOnly( return Ok(RepositoryResponse::ItemOnly(
model.build_item(&*id, serde_json::to_value(&user).unwrap()), model.build_item(&*id, serde_json::to_value(&user).unwrap()),
)); ));
} }
Err(err) => {
Ok(RepositoryResponse::NoItem) warn!("Error updating user");
return Err(RepositoryError::DatabaseError(Box::new(err)));
}
}
} }
async fn replace( async fn replace(
@ -197,6 +201,7 @@ impl DepotRepository for UserRepository {
.unwrap(); .unwrap();
if let Some(user) = user { if let Some(user) = user {
let delete_result = user.delete(&self.connection).await.unwrap(); let delete_result = user.delete(&self.connection).await.unwrap();
// .ok_or(RepositoryError::DatabaseError(Box::new(err)))?;
debug!("deleted rows: {}", delete_result.rows_affected); debug!("deleted rows: {}", delete_result.rows_affected);
} }

View File

@ -72,7 +72,16 @@ impl DepotRepository for MyStaticRepository {
.content .content
.clone() .clone()
.into_iter() .into_iter()
.map(|item| model.build_item(&*item.get("id").unwrap().to_string(), item)) .filter_map(|item| match item.get("id") {
Some(id_value) => {
let id_str = id_value.to_string();
Some(model.build_item(&id_str, item))
}
None => {
eprintln!("Skipping item due to missing 'id'");
None
}
})
.collect(), .collect(),
} }
} }
@ -117,6 +126,7 @@ impl DepotRepository for MyStaticRepository {
if let Some(index) = item_index { if let Some(index) = item_index {
let item = &mut self.content[index]; let item = &mut self.content[index];
*item = data.clone(); *item = data.clone();
item["id"] = (*id).into();
if let Some(item_id) = item.get("id") { if let Some(item_id) = item.get("id") {
return Ok(RepositoryResponse::ItemOnly( return Ok(RepositoryResponse::ItemOnly(

View File

@ -1,4 +1,4 @@
{% extends base|none("admin/base.jinja") %} {% extends base|none("depot/base.jinja") %}
{% block content %} {% block content %}
<div id="message-box-area" class="ui message" _="init wait 3s then transition opacity to 0 then remove me"> <div id="message-box-area" class="ui message" _="init wait 3s then transition opacity to 0 then remove me">
@ -8,6 +8,6 @@
<h1>Update {{item_model.name}} in {{item_info.name}}</h1> <h1>Update {{item_model.name}} in {{item_info.name}}</h1>
{% set fields = item_info.fields %} {% set fields = item_info.fields %}
{% set form = { 'action': item.change_url, 'method': 'PATCH' } %} {% set form = { 'action': item.change_url, 'method': 'PATCH' } %}
{% include "/admin/items/item_change_form.jinja" %} {% include "/depot/items/item_change_form.jinja" %}
</div> </div>
{% endblock content %} {% endblock content %}

View File

@ -10,10 +10,10 @@
{% elif form.method|upper == 'PUT' %} {% elif form.method|upper == 'PUT' %}
hx-put="{{ form.action }}" hx-put="{{ form.action }}"
{% else %} {% else %}
unknown-form-method={{form.method}} unknown-form-method="{{ form.method }}"
{% endif %}> {% endif %}>
{% from "/admin/items/items.jinja" import field_widget %} {% from "/depot/items/items.jinja" import field_widget %}
{% for field_name, field_defs in fields %} {% for field_name, field_defs in fields %}
{% if item %} {% if item %}
{% set field_value = item.fields[field_name]|none("") %} {% set field_value = item.fields[field_name]|none("") %}

View File

@ -1,4 +1,4 @@
{% extends base|none("admin/base.jinja") %} {% extends base|none("depot/base.jinja") %}
{% block content %} {% block content %}
<div class="ui container"> <div class="ui container">
@ -12,6 +12,6 @@
{% set fields = item_info.fields %} {% set fields = item_info.fields %}
{% set form = { 'action': item_model.add_url } %} {% set form = { 'action': item_model.add_url } %}
{% include "/admin/items/item_change_form.jinja" %} {% include "/depot/items/item_change_form.jinja" %}
</div> </div>
{% endblock content %} {% endblock content %}

View File

@ -1,4 +1,4 @@
{% extends base|none("admin/base.jinja") %} {% extends base|none("depot/base.jinja") %}
{% block content %} {% block content %}

View File

@ -1,4 +1,4 @@
{% extends base|none("admin/base.jinja") %} {% extends base|none("depot/base.jinja") %}
{% block content %} {% block content %}
<div class="ui container"> <div class="ui container">