244 lines
8.2 KiB
Rust
244 lines
8.2 KiB
Rust
use crate::domain::dto::server::{
|
|
CreateServerRequest, ServerExplorerItemResponse, ServerResponse, ServerRolePermissionResponse,
|
|
ServerTreeResponse, ServerUserPermissionResponse, UpdateServerRequest,
|
|
};
|
|
use crate::models::{server, server_item_order, server_role_permission, server_user_permission};
|
|
use crate::repositories::types::{CategoryWithPermissions, ChannelWithPermissions};
|
|
use crate::routes::category::mapper::category_model_to_category_response_with_permission;
|
|
use crate::routes::channel::mapper::channel_model_to_channel_response_with_permission;
|
|
use sea_orm::Set;
|
|
use std::collections::HashMap;
|
|
use uuid::Uuid;
|
|
|
|
pub fn server_model_to_server_response(model: server::Model) -> ServerResponse {
|
|
ServerResponse {
|
|
id: model.id,
|
|
name: model.name,
|
|
is_default: model.is_default,
|
|
created_at: model.created_at,
|
|
updated_at: model.updated_at,
|
|
}
|
|
}
|
|
|
|
pub fn create_request_to_am(req: CreateServerRequest) -> server::ActiveModel {
|
|
server::ActiveModel {
|
|
id: Set(Uuid::new_v4()),
|
|
name: Set(req.name),
|
|
password: Set(req.password),
|
|
is_default: Set(req.is_default),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
pub fn update_request_to_am(id: Uuid, req: UpdateServerRequest) -> server::ActiveModel {
|
|
server::ActiveModel {
|
|
id: Set(id),
|
|
name: Set(req.name),
|
|
password: Set(req.password),
|
|
is_default: Set(req.is_default),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
pub fn server_user_permission_to_response(
|
|
model: server_user_permission::Model,
|
|
) -> ServerUserPermissionResponse {
|
|
ServerUserPermissionResponse {
|
|
id: model.id,
|
|
server_id: model.server_id,
|
|
user_id: model.user_id,
|
|
permissions: model.permissions as u64,
|
|
}
|
|
}
|
|
|
|
pub fn server_role_permission_to_response(
|
|
model: server_role_permission::Model,
|
|
) -> ServerRolePermissionResponse {
|
|
ServerRolePermissionResponse {
|
|
id: model.id,
|
|
server_id: model.server_id,
|
|
role_id: model.role_id,
|
|
permissions: model.permissions as u64,
|
|
}
|
|
}
|
|
|
|
pub fn build_server_tree(
|
|
orders: Vec<server_item_order::Model>,
|
|
channels: Vec<ChannelWithPermissions>,
|
|
categories: Vec<CategoryWithPermissions>,
|
|
) -> ServerTreeResponse {
|
|
let order_map: HashMap<(Option<Uuid>, Uuid), i64> = orders
|
|
.into_iter()
|
|
.map(|order| {
|
|
(
|
|
(order.parent_category_id, order.resource_id),
|
|
order.order_key,
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
let mut category_channels: HashMap<Uuid, Vec<ChannelWithPermissions>> = HashMap::new();
|
|
let mut orphan_channels: Vec<ChannelWithPermissions> = Vec::new();
|
|
|
|
for channel_with_permissions in channels {
|
|
if let Some(category_id) = channel_with_permissions.channel.category_id {
|
|
category_channels
|
|
.entry(category_id)
|
|
.or_default()
|
|
.push(channel_with_permissions);
|
|
} else {
|
|
orphan_channels.push(channel_with_permissions);
|
|
}
|
|
}
|
|
|
|
// Trier les canaux dans chaque catégorie par leur order_key
|
|
for chans in category_channels.values_mut() {
|
|
chans.sort_by_key(|c| {
|
|
order_map
|
|
.get(&(c.channel.category_id, c.channel.id))
|
|
.copied()
|
|
.unwrap_or(i64::MAX)
|
|
});
|
|
}
|
|
|
|
// Préparer les éléments racine (catégories & canaux orphelins) avec leur order_key
|
|
let mut root_items: Vec<(ServerExplorerItemResponse, i64)> = Vec::new();
|
|
|
|
for cat_with_perm in categories {
|
|
let cat_id = cat_with_perm.category.id;
|
|
let order_key = order_map.get(&(None, cat_id)).copied().unwrap_or(i64::MAX);
|
|
let chans = category_channels.remove(&cat_id).unwrap_or_default();
|
|
|
|
let cat_perm_bits = cat_with_perm.permissions.map(|p| p.bits()).unwrap_or(0);
|
|
let cat_response = category_model_to_category_response_with_permission(
|
|
cat_with_perm.category,
|
|
Some(cat_perm_bits),
|
|
);
|
|
|
|
let chan_responses = chans
|
|
.into_iter()
|
|
.map(|c| {
|
|
let chan_perm_bits = c.permissions.map(|p| p.bits()).unwrap_or(0);
|
|
channel_model_to_channel_response_with_permission(c.channel, Some(chan_perm_bits))
|
|
})
|
|
.collect();
|
|
|
|
root_items.push((
|
|
ServerExplorerItemResponse::Category(cat_response, chan_responses),
|
|
order_key,
|
|
));
|
|
}
|
|
|
|
for chan_with_perm in orphan_channels {
|
|
let order_key = order_map
|
|
.get(&(None, chan_with_perm.channel.id))
|
|
.copied()
|
|
.unwrap_or(i64::MAX);
|
|
let chan_perm_bits = chan_with_perm.permissions.map(|p| p.bits()).unwrap_or(0);
|
|
let chan_response = channel_model_to_channel_response_with_permission(
|
|
chan_with_perm.channel,
|
|
Some(chan_perm_bits),
|
|
);
|
|
root_items.push((
|
|
ServerExplorerItemResponse::Channel(chan_response),
|
|
order_key,
|
|
));
|
|
}
|
|
|
|
// Tri du niveau racine par order_key
|
|
root_items.sort_by_key(|(_, order_key)| *order_key);
|
|
|
|
let items = root_items.into_iter().map(|(item, _)| item).collect();
|
|
|
|
ServerTreeResponse { items }
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::models::{category, channel};
|
|
use crate::permissions::ChannelPermission;
|
|
use chrono::Utc;
|
|
|
|
fn channel(id: Uuid, category_id: Option<Uuid>, name: &str) -> ChannelWithPermissions {
|
|
ChannelWithPermissions {
|
|
channel: channel::Model {
|
|
id,
|
|
server_id: Some(Uuid::new_v4()),
|
|
category_id,
|
|
channel_type: channel::ChannelType::Text,
|
|
name: Some(name.to_owned()),
|
|
created_at: Utc::now(),
|
|
updated_at: Utc::now(),
|
|
},
|
|
permissions: Some(ChannelPermission::empty()),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn sorts_channels_by_order_key_within_each_category() {
|
|
let category_id = Uuid::new_v4();
|
|
let first_id = Uuid::new_v4();
|
|
let second_id = Uuid::new_v4();
|
|
|
|
let category = CategoryWithPermissions {
|
|
category: category::Model {
|
|
id: category_id,
|
|
server_id: Uuid::new_v4(),
|
|
name: "Discussion".to_owned(),
|
|
created_at: Utc::now(),
|
|
updated_at: Utc::now(),
|
|
},
|
|
permissions: Some(ChannelPermission::empty()),
|
|
};
|
|
|
|
let orders = vec![
|
|
server_item_order::Model {
|
|
id: Uuid::new_v4(),
|
|
server_id: category.category.server_id,
|
|
resource_id: category_id,
|
|
resource_type: server_item_order::OrderedResourceType::Category,
|
|
parent_category_id: None,
|
|
order_key: 1,
|
|
created_at: Utc::now(),
|
|
updated_at: Utc::now(),
|
|
},
|
|
server_item_order::Model {
|
|
id: Uuid::new_v4(),
|
|
server_id: category.category.server_id,
|
|
resource_id: first_id,
|
|
resource_type: server_item_order::OrderedResourceType::Channel,
|
|
parent_category_id: Some(category_id),
|
|
order_key: 2,
|
|
created_at: Utc::now(),
|
|
updated_at: Utc::now(),
|
|
},
|
|
server_item_order::Model {
|
|
id: Uuid::new_v4(),
|
|
server_id: category.category.server_id,
|
|
resource_id: second_id,
|
|
resource_type: server_item_order::OrderedResourceType::Channel,
|
|
parent_category_id: Some(category_id),
|
|
order_key: 1,
|
|
created_at: Utc::now(),
|
|
updated_at: Utc::now(),
|
|
},
|
|
];
|
|
|
|
let response = build_server_tree(
|
|
orders,
|
|
vec![
|
|
channel(first_id, Some(category_id), "second"),
|
|
channel(second_id, Some(category_id), "first"),
|
|
],
|
|
vec![category],
|
|
);
|
|
|
|
let ServerExplorerItemResponse::Category(_, channels) = &response.items[0] else {
|
|
panic!("expected a category");
|
|
};
|
|
assert_eq!(channels[0].name.as_deref(), Some("first"));
|
|
assert_eq!(channels[1].name.as_deref(), Some("second"));
|
|
}
|
|
}
|