code: database list, get, create, and fixed migration

This commit is contained in:
Gabor Körber 2024-02-16 21:40:27 +01:00
parent 8d07fafabd
commit 1dc24bf244
9 changed files with 96 additions and 34 deletions

View File

@ -1 +1 @@
DATABASE_URL=postgresql://miniweb:miniweb@localhost:5432/miniweb DATABASE_URL=postgresql://miniweb:miniweb@localhost:54321/miniweb

View File

@ -30,7 +30,7 @@ pg-down:
# Run Migrations # Run Migrations
migrate: migrate:
sea-orm-cli up sea-orm-cli migrate
# Install Developer dependencies # Install Developer dependencies
dev-install: dev-install:

View File

@ -54,3 +54,13 @@ Probably options should be transmitted via JSON data.
</div> </div>
``` ```
- -
## Error System for Repository
Repository needs a good error system, and Results instead of just returning data;
These errors should encompass:
- Validation Errors: these should allow forms to display issues, but not disrupt the flow
- Wrong Data / Not Authorized and other 400 base
- Not Found Error: for 404s
- Internal Errors: for 500s

View File

@ -9,17 +9,17 @@ impl MigrationTrait for Migration {
manager manager
.create_table( .create_table(
Table::create() Table::create()
.table(User::Table) .table(Users::Table)
.if_not_exists() .if_not_exists()
.col( .col(
ColumnDef::new(User::Id) ColumnDef::new(Users::Id)
.integer() .integer()
.not_null() .not_null()
.auto_increment() .auto_increment()
.primary_key(), .primary_key(),
) )
.col(ColumnDef::new(User::Username).string().not_null()) .col(ColumnDef::new(Users::Username).string().not_null())
.col(ColumnDef::new(User::Description).string().not_null()) .col(ColumnDef::new(Users::Description).text())
.to_owned(), .to_owned(),
) )
.await .await
@ -27,13 +27,13 @@ impl MigrationTrait for Migration {
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager manager
.drop_table(Table::drop().table(User::Table).to_owned()) .drop_table(Table::drop().table(Users::Table).to_owned())
.await .await
} }
} }
#[derive(DeriveIden)] #[derive(DeriveIden)]
enum User { enum Users {
Table, Table,
Id, Id,
Username, Username,

View File

@ -156,7 +156,7 @@ impl AdminRepository for MyStaticRepository {
} }
} }
pub fn register_example(registry: &mut AdminRegistry) { pub fn register(registry: &mut AdminRegistry) {
let app_key = registry.register_app("Example App"); let app_key = registry.register_app("Example App");
let repo = MyStaticRepository::new(); let repo = MyStaticRepository::new();
let model_config = AdminModelConfig { let model_config = AdminModelConfig {

View File

@ -4,7 +4,7 @@ use crate::admin::state::AdminRegistry;
use async_trait::async_trait; use async_trait::async_trait;
use entity; use entity;
use log::{debug, warn}; use log::{debug, warn};
use sea_orm::{Database, DatabaseConnection, EntityTrait}; use sea_orm::{ActiveModelTrait, Database, DatabaseConnection, EntityTrait, Set};
use serde_json::{json, Value}; use serde_json::{json, Value};
struct UserRepository { struct UserRepository {
@ -25,32 +25,48 @@ impl AdminRepository for UserRepository {
RepoInfo { RepoInfo {
name: "User", name: "User",
lookup_key: "id", lookup_key: "id",
display_list: &["id"], display_list: &["id", "username"],
fields: &[], fields: &["username", "description"],
} }
.into() .into()
} }
async fn get(&self, model: &RepositoryContext, id: LookupKey) -> Option<RepositoryItem> { async fn get(&self, model: &RepositoryContext, id: LookupKey) -> Option<RepositoryItem> {
if let LookupKey::Integer(id) = id {
let id: i32 = id as i32; // use try_into() instead.
let get_user = entity::User::find_by_id(id).one(&self.connection).await;
match get_user {
Ok(get_user) => {
if let Some(user) = get_user {
let id = user.id.to_string();
match serde_json::to_value(&user) {
Ok(item) => {
return Some(model.build_item(&*id, item));
}
Err(_) => return None,
}
}
}
Err(_) => return None,
}
}
None None
} }
async fn list(&self, model: &RepositoryContext) -> RepositoryList { async fn list(&self, model: &RepositoryContext) -> RepositoryList {
let results = entity::User::find() let results = if let Ok(results) = entity::User::find().all(&self.connection).await {
.all(&self.connection) results
.await } else {
.expect("Error loading users."); return RepositoryList::Empty;
};
let repository_items: Vec<RepositoryItem> = results let repository_items: Vec<RepositoryItem> = results
.into_iter() .into_iter()
.filter_map(|item| { .filter_map(|item| match serde_json::to_value(&item) {
match serde_json::to_value(item) { Ok(fields) => {
Ok(fields) => Some(RepositoryItem { let id = item.id.to_string();
fields, Some(model.build_item(&*id, fields))
detail_url: None, // replace with actual value if available
change_url: None, // replace with actual value if available
}),
Err(_) => None,
} }
Err(_) => None,
}) })
.collect(); .collect();
RepositoryList::List { RepositoryList::List {
@ -58,11 +74,22 @@ impl AdminRepository for UserRepository {
} }
} }
async fn create( async fn create(&mut self, model: &RepositoryContext, data: Value) -> Option<RepositoryItem> {
&mut self, let username = data.get("username").unwrap().as_str().unwrap();
model: &RepositoryContext,
mut data: Value, let mut user = entity::user::ActiveModel {
) -> Option<RepositoryItem> { username: Set(username.to_owned()),
..Default::default()
};
if let Some(value) = data.get("description") {
user.description = Set(value.as_str().map(|s| s.to_owned()));
}
if let Ok(user) = user.insert(&self.connection).await {
let id = user.id.to_string();
return Some(model.build_item(&*id, serde_json::to_value(&user).unwrap()));
}
None None
} }
@ -88,3 +115,17 @@ impl AdminRepository for UserRepository {
None None
} }
} }
pub fn register(registry: &mut AdminRegistry, db: DatabaseConnection) {
let app_key = registry.register_app("Auth");
let repo = UserRepository::new(db);
let model_config = AdminModelConfig {
app_key: app_key,
name: "User".to_owned(),
};
let model_result = registry.register_model(model_config, repo);
match model_result {
Err(err) => panic!("{}", err),
_ => (),
}
}

View File

@ -1,6 +1,6 @@
use dotenvy::dotenv; use dotenvy::dotenv;
use entity; use entity;
use sea_orm::{DatabaseConnection, Database, EntityTrait}; use sea_orm::{Database, DatabaseConnection, EntityTrait};
use std::env; use std::env;
pub async fn establish_connection() -> DatabaseConnection { pub async fn establish_connection() -> DatabaseConnection {
@ -10,13 +10,14 @@ pub async fn establish_connection() -> DatabaseConnection {
db.unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) db.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
} }
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let connection = establish_connection().await; let connection = establish_connection().await;
let results = entity::User::find() let results = entity::User::find()
.all(&connection).await.expect("Error loading users."); .all(&connection)
.await
.expect("Error loading users.");
println!("Displaying {} users", results.len()); println!("Displaying {} users", results.len());
for user in results { for user in results {

View File

@ -7,6 +7,7 @@ mod state;
use crate::service::{handlers, templates}; use crate::service::{handlers, templates};
use crate::state::AppState; use crate::state::AppState;
use admin_examples::static_repository; use admin_examples::static_repository;
use admin_examples::user_repository;
use axum::{ use axum::{
body::Bytes, body::Bytes,
extract::MatchedPath, extract::MatchedPath,
@ -34,6 +35,12 @@ async fn hello_world(templates: State<templates::Templates>) -> impl IntoRespons
templates.render_html("hello_world.html", ()) templates.render_html("hello_world.html", ())
} }
pub async fn establish_connection() -> sea_orm::DatabaseConnection {
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let db = sea_orm::Database::connect(&database_url).await;
db.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
// Load environment // Load environment
@ -41,10 +48,13 @@ async fn main() {
env_logger::init(); env_logger::init();
info!("Miniweb starting..."); info!("Miniweb starting...");
let db_connection = establish_connection().await;
// Prepare App State // Prepare App State
let tmpl = templates::Templates::initialize().expect("Template Engine could not be loaded."); let tmpl = templates::Templates::initialize().expect("Template Engine could not be loaded.");
let mut admin = admin::state::AdminRegistry::new("admin"); let mut admin = admin::state::AdminRegistry::new("admin");
static_repository::register_example(&mut admin); static_repository::register(&mut admin);
user_repository::register(&mut admin, db_connection);
let state: AppState = AppState { let state: AppState = AppState {
templates: tmpl, templates: tmpl,