create actix_admin_builder function

This commit is contained in:
Manuel Gugger 2022-05-25 19:34:29 +02:00
parent 7c8cf72668
commit 90a17e64d3
3 changed files with 108 additions and 67 deletions

View File

@ -1,11 +1,11 @@
use actix_web::http::header;
use actix_web::{error, web, Error, HttpRequest, HttpResponse};
use async_trait::async_trait;
use lazy_static::lazy_static;
use sea_orm::{ DatabaseConnection};
use sea_orm::DatabaseConnection;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use actix_web::http::header;
use tera::{Context, Tera};
use async_trait::async_trait;
pub use actix_admin_macros::DeriveActixAdminModel;
@ -48,19 +48,27 @@ pub trait AppDataTrait {
// ActixAdminModel
#[async_trait]
pub trait ActixAdminModelTrait {
async fn list_model(db: &DatabaseConnection, page: usize, posts_per_page: usize) -> Vec<ActixAdminModel>;
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>
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 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;
}
@ -71,34 +79,64 @@ pub struct ActixAdminViewModel {
pub fields: Vec<(String, ActixAdminField)>,
}
// ActixAdminController
#[derive(Clone, Debug)]
pub struct ActixAdmin {
pub entity_names: Vec<String>,
pub view_models: HashMap<String, ActixAdminViewModel>
pub view_models: HashMap<String, ActixAdminViewModel>,
}
impl ActixAdmin {
pub fn new() -> Self {
let actix_admin = ActixAdmin {
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()
};
actix_admin
view_models: HashMap::new(),
},
scopes: Vec::new(),
}
}
pub fn add_entity<E: ActixAdminViewModelTrait>(mut self, view_model: &ActixAdminViewModel) -> Self {
self.entity_names.push(E::get_entity_name());
let view_model_cloned = view_model.clone();
let key = view_model.entity_name.to_string();
self.view_models.insert(key, view_model_cloned);
self
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);
}
pub fn create_scope<T: AppDataTrait + 'static >(&self) -> actix_web::Scope {
let scope = web::scope("/admin").route("/", web::get().to(index::<T>));
scope
}
fn get_actix_admin(&self) -> ActixAdmin {
self.actix_admin.clone()
}
}
impl From<String> for ActixAdminModel {
@ -107,13 +145,13 @@ impl From<String> for ActixAdminModel {
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());
hashmap.insert(
iter.next().unwrap().to_string(),
iter.next().unwrap().to_string(),
);
}
ActixAdminModel {
// TODO: create dynamically
values: hashmap
}
ActixAdminModel { values: hashmap }
}
}
@ -123,7 +161,7 @@ impl ActixAdminModel {
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)
Err(_) => None, //panic!("key {} could not be parsed", key)
}
}
}
@ -139,7 +177,10 @@ pub async fn index<T: AppDataTrait>(data: web::Data<T>) -> Result<HttpResponse,
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> {
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();
@ -148,7 +189,9 @@ pub async fn list<T: AppDataTrait, E: ActixAdminViewModelTrait>(req: HttpRequest
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 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;
@ -167,7 +210,12 @@ pub async fn list<T: AppDataTrait, E: ActixAdminViewModelTrait>(req: HttpRequest
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> {
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;
@ -187,16 +235,22 @@ pub async fn create_get<T: AppDataTrait, E: ActixAdminViewModelTrait>(_req: Http
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> {
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)))
.append_header((
header::LOCATION,
format!("/admin/{}/list", view_model.entity_name),
))
.finish())
}

Binary file not shown.

View File

@ -1,7 +1,7 @@
extern crate serde_derive;
use actix_admin::{
ActixAdmin, ActixAdminViewModel,
ActixAdmin, ActixAdminBuilder, ActixAdminBuilderTrait, ActixAdminViewModel,
AppDataTrait as ActixAdminAppDataTrait,
};
use actix_session::{CookieSession, Session};
@ -55,24 +55,15 @@ async fn index(session: Session, data: web::Data<AppState>) -> HttpResponse {
HttpResponse::Ok().body(rendered)
}
// TODO: Generate this with a Macro accepting Tuples of (Entity, viewmodel)
fn setup_actix_admin(
actix_admin: &ActixAdmin,
) -> actix_web::Scope {
actix_admin
.create_scope::<AppState>()
.service(
web::scope("/post")
.route("/list", web::get().to(actix_admin::list::<AppState, Post>))
.route("/create", web::get().to(actix_admin::create_get::<AppState, Post>))
.route("/create", web::post().to(actix_admin::create_post::<AppState, Post>))
)
.service(
web::scope("/comment")
.route("/list", web::get().to(actix_admin::list::<AppState, Comment>))
.route("/create", web::get().to(actix_admin::create_get::<AppState, Comment>))
.route("/create", web::post().to(actix_admin::create_post::<AppState, Comment>))
)
fn create_actix_admin_builder() -> ActixAdminBuilder {
let post_view_model = ActixAdminViewModel::from(Post);
let comment_view_model = ActixAdminViewModel::from(Comment);
let mut admin_builder = ActixAdminBuilder::new();
admin_builder.add_entity::<AppState, Post>(&post_view_model);
admin_builder.add_entity::<AppState, Comment>(&comment_view_model);
admin_builder
}
#[actix_rt::main]
@ -109,17 +100,13 @@ async fn main() {
let conn = sea_orm::Database::connect(opt).await.unwrap();
let _ = entity::create_post_table(&conn).await;
let post_view_model = ActixAdminViewModel::from(Post);
let comment_view_model = ActixAdminViewModel::from(Comment);
let actix_admin = ActixAdmin::new()
.add_entity::<Post>(&post_view_model)
.add_entity::<Comment>(&comment_view_model)
;
let actix_admin = create_actix_admin_builder().get_actix_admin();
let app_state = AppState {
oauth: client,
tmpl: tera,
db: conn,
actix_admin: actix_admin.clone(),
actix_admin: actix_admin,
};
HttpServer::new(move || {
@ -128,9 +115,9 @@ async fn main() {
.wrap(CookieSession::signed(&[0; 32]).secure(false))
.route("/", web::get().to(index))
.service(azure_auth.clone().create_scope::<AppState>())
.service(setup_actix_admin(
&actix_admin
))
.service(
create_actix_admin_builder().get_scope::<AppState>()
)
.wrap(middleware::Logger::default())
})
.bind("127.0.0.1:5000")