split into multiple files

This commit is contained in:
Manuel Gugger 2022-05-27 12:49:21 +02:00
parent 90a17e64d3
commit 6dc1401550
10 changed files with 289 additions and 216 deletions

View File

@ -0,0 +1,69 @@
use actix_web::{web};
use std::collections::HashMap;
use crate::ActixAdmin;
use crate::ActixAdminViewModelTrait;
use crate::ActixAdminViewModel;
use crate::AppDataTrait;
use crate::routes::list;
use crate::routes::create_get;
use crate::routes::create_post;
use crate::routes::index;
pub struct ActixAdminBuilder {
pub scopes: Vec<actix_web::Scope>,
pub actix_admin: ActixAdmin,
}
pub trait ActixAdminBuilderTrait {
fn new() -> Self;
fn add_entity<T: AppDataTrait + 'static, E: ActixAdminViewModelTrait + 'static>(&mut self, view_model: &ActixAdminViewModel);
fn get_scope<T: AppDataTrait + 'static>(self) -> actix_web::Scope;
fn get_actix_admin(&self) -> ActixAdmin;
}
impl ActixAdminBuilderTrait for ActixAdminBuilder {
fn new() -> Self {
ActixAdminBuilder {
actix_admin: ActixAdmin {
entity_names: Vec::new(),
view_models: HashMap::new(),
},
scopes: Vec::new(),
}
}
fn add_entity<T: AppDataTrait + 'static, E: ActixAdminViewModelTrait + 'static>(
&mut self,
view_model: &ActixAdminViewModel,
) {
self.scopes.push(
web::scope(&format!("/{}", E::get_entity_name()))
.route("/list", web::get().to(list::<T, E>))
.route("/create", web::get().to(create_get::<T, E>))
.route("/create", web::post().to(create_post::<T, E>)),
);
self.actix_admin.entity_names.push(E::get_entity_name());
//let view_model_cloned = view_model.clone();
let key = E::get_entity_name();
self.actix_admin.view_models.insert(key, view_model.clone());
}
fn get_scope<T: AppDataTrait + 'static>(self) -> actix_web::Scope {
let mut scope = web::scope("/admin").route("/", web::get().to(index::<T>));
for entity_scope in self.scopes {
scope = scope.service(entity_scope);
}
scope
}
fn get_actix_admin(&self) -> ActixAdmin {
self.actix_admin.clone()
}
}

View File

@ -1,15 +1,24 @@
use actix_web::http::header;
use actix_web::{error, web, Error, HttpRequest, HttpResponse};
use async_trait::async_trait;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use sea_orm::DatabaseConnection; use sea_orm::DatabaseConnection;
use serde::{Deserialize, Serialize}; use serde::{Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use tera::{Context, Tera}; use tera::{Tera};
pub use actix_admin_macros::DeriveActixAdminModel; pub use actix_admin_macros::DeriveActixAdminModel;
const DEFAULT_ENTITIES_PER_PAGE: usize = 5; mod view_model;
pub use view_model::ActixAdminViewModel;
pub use view_model::ActixAdminViewModelTrait;
mod model;
pub use model::ActixAdminModel;
pub use model::ActixAdminModelTrait;
mod builder;
pub use builder::ActixAdminBuilder;
pub use builder::ActixAdminBuilderTrait;
mod routes;
#[macro_export] #[macro_export]
macro_rules! hashmap { macro_rules! hashmap {
@ -26,13 +35,6 @@ lazy_static! {
Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")).unwrap(); Tera::new(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")).unwrap();
} }
// Paging
#[derive(Debug, Deserialize)]
pub struct Params {
page: Option<usize>,
entities_per_page: Option<usize>,
}
// Fields // Fields
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
pub enum ActixAdminField { pub enum ActixAdminField {
@ -46,211 +48,9 @@ pub trait AppDataTrait {
} }
// ActixAdminModel // ActixAdminModel
#[async_trait]
pub trait ActixAdminModelTrait {
async fn list_model(
db: &DatabaseConnection,
page: usize,
posts_per_page: usize,
) -> Vec<ActixAdminModel>;
fn get_fields() -> Vec<(String, ActixAdminField)>;
}
#[derive(Clone, Debug, Serialize)]
pub struct ActixAdminModel {
pub values: HashMap<String, String>,
}
// ActixAdminViewModel
#[async_trait(?Send)]
pub trait ActixAdminViewModelTrait {
async fn list(
db: &DatabaseConnection,
page: usize,
entities_per_page: usize,
) -> Vec<ActixAdminModel>;
async fn create_entity(db: &DatabaseConnection, model: ActixAdminModel) -> ActixAdminModel;
fn get_entity_name() -> String;
}
#[derive(Clone, Debug, Serialize)]
pub struct ActixAdminViewModel {
pub entity_name: String,
pub fields: Vec<(String, ActixAdminField)>,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ActixAdmin { pub struct ActixAdmin {
pub entity_names: Vec<String>, pub entity_names: Vec<String>,
pub view_models: HashMap<String, ActixAdminViewModel>, pub view_models: HashMap<String, ActixAdminViewModel>,
} }
pub struct ActixAdminBuilder {
pub scopes: Vec<actix_web::Scope>,
pub actix_admin: ActixAdmin,
}
pub trait ActixAdminBuilderTrait {
fn new() -> Self;
fn add_entity<T: AppDataTrait + 'static, E: ActixAdminViewModelTrait + 'static>(&mut self, view_model: &ActixAdminViewModel);
fn get_scope<T: AppDataTrait + 'static>(self) -> actix_web::Scope;
fn get_actix_admin(&self) -> ActixAdmin;
}
impl ActixAdminBuilderTrait for ActixAdminBuilder {
fn new() -> Self {
ActixAdminBuilder {
actix_admin: ActixAdmin {
entity_names: Vec::new(),
view_models: HashMap::new(),
},
scopes: Vec::new(),
}
}
fn add_entity<T: AppDataTrait + 'static, E: ActixAdminViewModelTrait + 'static>(
&mut self,
view_model: &ActixAdminViewModel,
) {
self.scopes.push(
web::scope(&format!("/{}", E::get_entity_name()))
.route("/list", web::get().to(self::list::<T, E>))
.route("/create", web::get().to(self::create_get::<T, E>))
.route("/create", web::post().to(self::create_post::<T, E>)),
);
self.actix_admin.entity_names.push(E::get_entity_name());
//let view_model_cloned = view_model.clone();
let key = E::get_entity_name();
self.actix_admin.view_models.insert(key, view_model.clone());
}
fn get_scope<T: AppDataTrait + 'static>(self) -> actix_web::Scope {
let mut scope = web::scope("/admin").route("/", web::get().to(index::<T>));
for entity_scope in self.scopes {
scope = scope.service(entity_scope);
}
scope
}
fn get_actix_admin(&self) -> ActixAdmin {
self.actix_admin.clone()
}
}
impl From<String> for ActixAdminModel {
fn from(string: String) -> Self {
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(),
);
}
ActixAdminModel { values: hashmap }
}
}
impl ActixAdminModel {
pub fn get_value<T: std::str::FromStr>(&self, key: &str) -> Option<T> {
println!("get value for key {}", key);
let value = self.values.get(key).unwrap().to_string().parse::<T>();
match value {
Ok(val) => Some(val),
Err(_) => None, //panic!("key {} could not be parsed", key)
}
}
}
pub async fn index<T: AppDataTrait>(data: web::Data<T>) -> Result<HttpResponse, Error> {
let entity_names = &data.get_actix_admin().entity_names;
let mut ctx = Context::new();
ctx.insert("entity_names", &entity_names);
let body = TERA
.render("index.html", &ctx)
.map_err(|_| error::ErrorInternalServerError("Template error"))?;
Ok(HttpResponse::Ok().content_type("text/html").body(body))
}
pub async fn list<T: AppDataTrait, E: ActixAdminViewModelTrait>(
req: HttpRequest,
data: web::Data<T>,
) -> Result<HttpResponse, Error> {
let entity_name = E::get_entity_name();
let actix_admin = data.get_actix_admin();
let view_model: &ActixAdminViewModel = actix_admin.view_models.get(&entity_name).unwrap();
let entity_names = &data.get_actix_admin().entity_names;
let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
let page = params.page.unwrap_or(1);
let entities_per_page = params
.entities_per_page
.unwrap_or(DEFAULT_ENTITIES_PER_PAGE);
let db = data.get_db();
let entities: Vec<ActixAdminModel> = E::list(db, page, entities_per_page).await;
let mut ctx = Context::new();
ctx.insert("entity_names", &entity_names);
ctx.insert("entities", &entities);
ctx.insert("page", &page);
ctx.insert("entities_per_page", &entities_per_page);
ctx.insert("num_pages", "5" /*&num_pages*/);
ctx.insert("view_model", &view_model);
let body = TERA
.render("list.html", &ctx)
.map_err(|err| error::ErrorInternalServerError(err))?;
Ok(HttpResponse::Ok().content_type("text/html").body(body))
}
pub async fn create_get<T: AppDataTrait, E: ActixAdminViewModelTrait>(
_req: HttpRequest,
data: web::Data<T>,
_body: web::Payload,
_text: String,
) -> Result<HttpResponse, Error> {
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 ctx = Context::new();
ctx.insert("entity_names", &entity_names);
ctx.insert("view_model", &view_model);
ctx.insert("model_fields", &view_model.fields);
let body = TERA
.render("create.html", &ctx)
.map_err(|err| error::ErrorInternalServerError(err))?;
Ok(HttpResponse::Ok().content_type("text/html").body(body))
}
pub async fn create_post<T: AppDataTrait, E: ActixAdminViewModelTrait>(
_req: HttpRequest,
data: web::Data<T>,
text: String,
) -> Result<HttpResponse, Error> {
let db = &data.get_db();
let entity_name = E::get_entity_name();
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;
Ok(HttpResponse::Found()
.append_header((
header::LOCATION,
format!("/admin/{}/list", view_model.entity_name),
))
.finish())
}

48
actix_admin/src/model.rs Normal file
View File

@ -0,0 +1,48 @@
use async_trait::async_trait;
use sea_orm::DatabaseConnection;
use serde::{Serialize};
use std::collections::HashMap;
use crate::ActixAdminField;
#[async_trait]
pub trait ActixAdminModelTrait {
async fn list_model(
db: &DatabaseConnection,
page: usize,
posts_per_page: usize,
) -> Vec<ActixAdminModel>;
fn get_fields() -> Vec<(String, ActixAdminField)>;
}
#[derive(Clone, Debug, Serialize)]
pub struct ActixAdminModel {
pub values: HashMap<String, String>,
}
impl From<String> for ActixAdminModel {
fn from(string: String) -> Self {
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(),
);
}
ActixAdminModel { values: hashmap }
}
}
impl ActixAdminModel {
pub fn get_value<T: std::str::FromStr>(&self, key: &str) -> Option<T> {
println!("get value for key {}", key);
let value = self.values.get(key).unwrap().to_string().parse::<T>();
match value {
Ok(val) => Some(val),
Err(_) => None, //panic!("key {} could not be parsed", key)
}
}
}

View File

@ -0,0 +1,31 @@
use actix_web::{error, web, Error, HttpRequest, HttpResponse};
use tera::{Context};
use crate::AppDataTrait;
use crate::ActixAdminViewModelTrait;
use crate::TERA;
pub async fn create_get<T: AppDataTrait, E: ActixAdminViewModelTrait>(
_req: HttpRequest,
data: web::Data<T>,
_body: web::Payload,
_text: String,
) -> Result<HttpResponse, Error> {
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 ctx = Context::new();
ctx.insert("entity_names", &entity_names);
ctx.insert("view_model", &view_model);
ctx.insert("model_fields", &view_model.fields);
let body = TERA
.render("create.html", &ctx)
.map_err(|err| error::ErrorInternalServerError(err))?;
Ok(HttpResponse::Ok().content_type("text/html").body(body))
}

View File

@ -0,0 +1,26 @@
use actix_web::http::header;
use actix_web::{web, Error, HttpRequest, HttpResponse};
use crate::AppDataTrait;
use crate::ActixAdminViewModelTrait;
use crate::ActixAdminModel;
pub async fn create_post<T: AppDataTrait, E: ActixAdminViewModelTrait>(
_req: HttpRequest,
data: web::Data<T>,
text: String,
) -> Result<HttpResponse, Error> {
let db = &data.get_db();
let entity_name = E::get_entity_name();
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;
Ok(HttpResponse::Found()
.append_header((
header::LOCATION,
format!("/admin/{}/list", view_model.entity_name),
))
.finish())
}

View File

@ -0,0 +1,16 @@
use actix_web::{error, web, Error, HttpResponse};
use tera::{Context};
use crate::AppDataTrait;
use crate::TERA;
pub async fn index<T: AppDataTrait>(data: web::Data<T>) -> Result<HttpResponse, Error> {
let entity_names = &data.get_actix_admin().entity_names;
let mut ctx = Context::new();
ctx.insert("entity_names", &entity_names);
let body = TERA
.render("index.html", &ctx)
.map_err(|_| error::ErrorInternalServerError("Template error"))?;
Ok(HttpResponse::Ok().content_type("text/html").body(body))
}

View File

@ -0,0 +1,50 @@
use actix_web::{error, web, Error, HttpRequest, HttpResponse};
use serde::{Deserialize};
use tera::{Context};
use crate::AppDataTrait;
use crate::ActixAdminViewModelTrait;
use crate::ActixAdminViewModel;
use crate::ActixAdminModel;
use crate::TERA;
const DEFAULT_ENTITIES_PER_PAGE: usize = 5;
#[derive(Debug, Deserialize)]
pub struct Params {
page: Option<usize>,
entities_per_page: Option<usize>,
}
pub async fn list<T: AppDataTrait, E: ActixAdminViewModelTrait>(
req: HttpRequest,
data: web::Data<T>,
) -> Result<HttpResponse, Error> {
let entity_name = E::get_entity_name();
let actix_admin = data.get_actix_admin();
let view_model: &ActixAdminViewModel = actix_admin.view_models.get(&entity_name).unwrap();
let entity_names = &data.get_actix_admin().entity_names;
let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
let page = params.page.unwrap_or(1);
let entities_per_page = params
.entities_per_page
.unwrap_or(DEFAULT_ENTITIES_PER_PAGE);
let db = data.get_db();
let entities: Vec<ActixAdminModel> = E::list(db, page, entities_per_page).await;
let mut ctx = Context::new();
ctx.insert("entity_names", &entity_names);
ctx.insert("entities", &entities);
ctx.insert("page", &page);
ctx.insert("entities_per_page", &entities_per_page);
ctx.insert("num_pages", "5" /*&num_pages*/);
ctx.insert("view_model", &view_model);
let body = TERA
.render("list.html", &ctx)
.map_err(|err| error::ErrorInternalServerError(err))?;
Ok(HttpResponse::Ok().content_type("text/html").body(body))
}

View File

@ -0,0 +1,11 @@
mod create_get;
pub use create_get::create_get;
mod create_post;
pub use create_post::create_post;
mod index;
pub use index::index;
mod list;
pub use list::list;

View File

@ -0,0 +1,23 @@
use async_trait::async_trait;
use sea_orm::DatabaseConnection;
use serde::{Serialize};
use crate::ActixAdminModel;
use crate::ActixAdminField;
#[async_trait(?Send)]
pub trait ActixAdminViewModelTrait {
async fn list(
db: &DatabaseConnection,
page: usize,
entities_per_page: usize,
) -> Vec<ActixAdminModel>;
async fn create_entity(db: &DatabaseConnection, model: ActixAdminModel) -> ActixAdminModel;
fn get_entity_name() -> String;
}
#[derive(Clone, Debug, Serialize)]
pub struct ActixAdminViewModel {
pub entity_name: String,
pub fields: Vec<(String, ActixAdminField)>,
}

View File

@ -10,7 +10,6 @@ use azure_auth::{AppDataTrait as AzureAuthAppDataTrait, AzureAuth, UserInfo};
use oauth2::basic::BasicClient; use oauth2::basic::BasicClient;
use oauth2::RedirectUrl; use oauth2::RedirectUrl;
use sea_orm::{ConnectOptions, DatabaseConnection}; use sea_orm::{ConnectOptions, DatabaseConnection};
//use sea_orm::{entity::*, query::*};
use std::env; use std::env;
use std::time::Duration; use std::time::Duration;
use tera::{Context, Tera}; use tera::{Context, Tera};