refactor: trying to align my naming into something specific. removing dynamic routing code.

This commit is contained in:
2024-01-03 14:50:00 +01:00
parent fb72a5deab
commit eef65cfdd0
7 changed files with 98 additions and 167 deletions

View File

@@ -17,19 +17,33 @@ mod dto {
#[derive(Deserialize, Serialize)]
pub struct AdminModel {
pub key: String,
pub name: String,
pub object_name: String,
pub admin_url: String,
pub view_only: bool,
pub add_url: Option<String>,
}
impl AdminModel {
pub fn object_name(&self) -> &'static str {
"hi there"
}
}
#[derive(Deserialize, Serialize)]
pub struct AdminApp {
pub key: String,
pub name: String,
pub app_label: String,
pub app_url: String,
pub models: Vec<AdminModel>,
}
impl AdminApp {
pub fn app_label(&self) -> &'static str {
"gogo"
}
}
}

View File

@@ -9,13 +9,13 @@ pub mod views;
pub fn routes() -> Router<AppState> {
Router::new()
.route("/", get(views::index).post(views::index_action))
.route("/app/:app_name", get(views::list_app))
.route("/app/:app_key", get(views::list_app))
.route(
"/app/:app_name/:model_name",
"/app/:app_key/models/:model_key",
get(views::list_item_collection),
)
.route(
"/app/:app_name/:model_name/detail/:model_id",
"/app/:app_key/models/:model_key/detail/:id",
get(views::item_details),
)
}

View File

@@ -29,18 +29,6 @@ impl AdminRegistry {
}
}
pub fn generate_router<T>(&self) -> Router<T>
where
T: Clone,
T: Send,
T: Sync,
T: 'static,
crate::service::templates::Templates: axum::extract::FromRef<T>,
AdminState: axum::extract::FromRef<T>,
{
routing::generate_routes::<T>(&self)
}
pub fn get_apps(&self) -> Vec<AdminApp> {
self.apps
.iter()
@@ -49,37 +37,41 @@ impl AdminRegistry {
}
fn get_app(&self, name: &str, node: &internal::AdminApp) -> AdminApp {
let label = node.label.as_ref().unwrap_or(&String::new()).clone();
AdminApp {
name: name.to_owned(),
app_label: label,
key: node.key.to_owned(),
app_url: name.to_owned().to_lowercase(),
models: vec![],
}
}
pub fn register_app(&mut self, name: &str, app_label: &str, app_url: &str) {
let key = self.get_key(name);
self.apps.insert(
name.to_owned(),
key.to_owned(),
internal::AdminApp {
object_name: name.to_owned(),
label: Some(app_label.to_owned()),
key: key,
name: name.to_owned(),
},
);
}
pub fn get_models(&self, app_name: &str) -> Vec<AdminModel> {
fn get_key(&self, name: &str) -> String {
slug::slugify(name)
}
pub fn get_models(&self, app_key: &str) -> Vec<AdminModel> {
vec![]
}
pub fn get_model(&self, app_name: &str, model_name: &str) -> Option<AdminModel> {
let full_model_name = format!("{}.{}", app_name, model_name);
let internal_model = self.models.get(&full_model_name)?;
pub fn get_model(&self, app_key: &str, model_key: &str) -> Option<AdminModel> {
let full_model_key = format!("{}.{}", app_key, model_key);
let internal_model = self.models.get(&full_model_key)?;
// unfinished: we need to think about model_name vs. model_id vs. entry_id, as "name" is ambiguous.
// unfinished: we need to think about model_key vs. model_id vs. entry_id, as "name" is ambiguous.
Some(AdminModel {
object_name: internal_model.model_name.clone(),
name: internal_model.model_name.clone(),
key: internal_model.model_key.clone(),
name: internal_model.model_key.clone(),
admin_url: "".to_owned(),
view_only: false,
add_url: None,
@@ -88,15 +80,15 @@ impl AdminRegistry {
fn register_model_config(&mut self, model: config::AdminModelConfig) -> Result<String, String> {
let local_config = internal::AdminModel::from(model);
if local_config.model_name.is_empty() {
if local_config.model_key.is_empty() {
return Err("No model name".to_owned());
}
let local_config_name = format!("{}.{}", local_config.app_name, local_config.model_name);
let local_config_name = format!("{}.{}", local_config.app_key, local_config.model_key);
if self.models.contains_key(&local_config_name) {
return Err(format!("Model {} already exists", local_config_name));
}
if let Some(local_config) = self.models.insert(local_config_name, local_config) {
return Ok(local_config.model_name);
return Ok(local_config.model_key);
}
Err("Model could not be added!".to_owned())
}
@@ -106,111 +98,41 @@ impl AdminRegistry {
model: config::AdminModelConfig,
repository: R,
) -> Result<(), String> {
let model_name = self.register_model_config(model)?;
self.repositories.insert(model_name, Box::new(repository));
let model_key = self.register_model_config(model)?;
self.repositories.insert(model_key, Box::new(repository));
Ok(())
}
}
pub mod config {
use axum::routing::MethodRouter;
// user uses this configuration object to register another model.
pub struct AdminModelConfig {
pub name: String,
pub app_name: String,
pub custom_index_handler: Option<MethodRouter>,
pub app_key: String,
}
}
mod internal {
#[derive(Clone)]
pub struct AdminApp {
pub object_name: String,
pub label: Option<String>,
pub key: String,
pub name: String,
}
#[derive(Clone)]
pub struct AdminModel {
pub app_name: String,
pub model_name: String,
pub app_key: String,
pub model_key: String,
pub name: String,
}
impl From<super::config::AdminModelConfig> for AdminModel {
fn from(value: super::config::AdminModelConfig) -> Self {
AdminModel {
app_name: value.app_name,
model_name: value.name,
app_key: value.app_key,
model_key: value.name.clone(),
name: value.name,
}
}
}
}
mod routing {
use axum::{routing::get, Router};
pub fn generate_routes<T>(registry: &super::AdminRegistry) -> Router<T>
where
T: Clone,
T: Send,
T: Sync,
T: 'static,
crate::service::templates::Templates: axum::extract::FromRef<T>,
super::AdminState: axum::extract::FromRef<T>,
{
let mut router: Router<T> = Router::new();
let apps = registry.get_apps();
router = router.route(
&format!("/{}", registry.base_path),
get(crate::admin::views::index),
);
for app in apps {
let app_route = format!("/{}/{}", registry.base_path, app.app_url);
// Add a route for the app
router = router.route(
&app_route,
get(move || async move { format!("Admin panel for {}", app.app_label) }),
);
// Add routes for each model in the app
/*for model in data_node.models.iter() {
let model_route = format!("{}/{}", app_route, model.name);
router = router.route(
&model_route,
get(move || async move { format!("Model page for {}", model.name) }),
);
}*/
}
router
}
fn define_example_data(registry: &mut super::AdminRegistry) {
/*let models = vec![
AdminModel {
name: "User".to_owned(),
object_name: "User".to_owned(),
admin_url: "/admin/users/user".to_owned(),
view_only: false,
add_url: Some("/admin/users/user/add".to_owned()),
},
AdminModel {
name: "Group".to_owned(),
object_name: "Group".to_owned(),
admin_url: "/admin/users/group".to_owned(),
view_only: false,
add_url: None,
},
AdminModel {
name: "Permission".to_owned(),
object_name: "Permission".to_owned(),
admin_url: "/admin/users/permission".to_owned(),
view_only: true,
add_url: None,
},
];*/
registry.register_app("auth", "Authorities", "auth");
}
}

View File

@@ -60,46 +60,6 @@ impl Default for AdminContext {
}
}
// Index is the entry point of Admin. It should display the Dashboard to a logged in user.
pub async fn index_example(templates: State<templates::Templates>) -> impl IntoResponse {
let models = vec![
AdminModel {
name: "User".to_owned(),
object_name: "User".to_owned(),
admin_url: "/admin/users/user".to_owned(),
view_only: false,
add_url: Some("/admin/users/user/add".to_owned()),
},
AdminModel {
name: "Group".to_owned(),
object_name: "Group".to_owned(),
admin_url: "/admin/users/group".to_owned(),
view_only: false,
add_url: None,
},
AdminModel {
name: "Permission".to_owned(),
object_name: "Permission".to_owned(),
admin_url: "/admin/users/permission".to_owned(),
view_only: true,
add_url: None,
},
];
let core_app = AdminApp {
name: "Admin".to_owned(),
app_label: "admin".to_owned(),
app_url: "/admin/users/".to_owned(),
models: models,
};
templates.render_html(
"admin/index.html",
AdminContext {
available_apps: vec![core_app],
..Default::default()
},
)
}
pub async fn index(
templates: State<templates::Templates>,
administration: State<Arc<state::AdminRegistry>>,
@@ -107,21 +67,21 @@ pub async fn index(
let models = vec![
AdminModel {
name: "User".to_owned(),
object_name: "User".to_owned(),
admin_url: "/admin/users/user".to_owned(),
key: "user".to_owned(),
admin_url: "/admin/app/users/user".to_owned(),
view_only: false,
add_url: Some("/admin/users/user/add".to_owned()),
},
AdminModel {
name: "Group".to_owned(),
object_name: "Group".to_owned(),
key: "group".to_owned(),
admin_url: "/admin/users/group".to_owned(),
view_only: false,
add_url: None,
},
AdminModel {
name: "Permission".to_owned(),
object_name: "Permission".to_owned(),
key: "permission".to_owned(),
admin_url: "/admin/users/permission".to_owned(),
view_only: true,
add_url: None,
@@ -129,7 +89,7 @@ pub async fn index(
];
let core_app = AdminApp {
name: "Admin".to_owned(),
app_label: "admin".to_owned(),
key: "admin".to_owned(),
app_url: "/admin/users/".to_owned(),
models: models,
};
@@ -153,7 +113,7 @@ pub async fn index_action(Form(example_data): Form<ExampleData>) -> impl IntoRes
pub async fn list_app(
templates: State<templates::Templates>,
Path(app_name): Path<String>,
Path(app_key): Path<String>,
) -> impl IntoResponse {
templates.render_html("admin/app_list.html", ())
}
@@ -161,7 +121,7 @@ pub async fn list_app(
// List Items renders the entire list item page.
pub async fn list_item_collection(
templates: State<templates::Templates>,
Path((app_name, model_name)): Path<(String, String)>,
Path((app_key, model_key)): Path<(String, String)>,
) -> impl IntoResponse {
templates.render_html("admin/items/list_items.html", ())
}
@@ -169,7 +129,7 @@ pub async fn list_item_collection(
// Items Action is a POST to an item list. By default these are actions, that work on a list of items as input.
pub async fn item_collection_action(
Form(question): Form<Question>,
Path((app_name, model_name)): Path<(String, String)>,
Path((app_key, model_key)): Path<(String, String)>,
) -> impl IntoResponse {
"There is your answer!".to_owned()
}
@@ -177,15 +137,15 @@ pub async fn item_collection_action(
// Item Details shows one single dataset.
pub async fn item_details(
templates: State<templates::Templates>,
Path((app_name, model_name, model_id)): Path<(String, String, String)>,
Path((app_key, model_key, id)): Path<(String, String, String)>,
) -> impl IntoResponse {
templates.render_html("admin/item_detail.html", ())
templates.render_html("admin/items/item_detail.html", ())
}
// Item Action allows running an action on one single dataset.
pub async fn item_action(
Form(question): Form<Question>,
Path((app_name, model_name, model_id)): Path<(String, String, String)>,
Path((app_key, model_key, model_id)): Path<(String, String, String)>,
) -> impl IntoResponse {
"There is your answer!".to_owned()
}