init
This commit is contained in:
+12
-12
@@ -14,6 +14,15 @@ pub struct MessageResponse {
|
||||
pub reply_to_id: Option<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct MessagePageResponse {
|
||||
pub messages: Vec<MessageResponse>,
|
||||
pub oldest_id: Option<Uuid>,
|
||||
pub newest_id: Option<Uuid>,
|
||||
pub has_more_before: bool,
|
||||
pub has_more_after: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct CreateMessageRequest {
|
||||
pub channel_id: Uuid,
|
||||
@@ -26,19 +35,10 @@ pub struct UpdateMessageRequest {
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, utoipa::IntoParams)]
|
||||
#[derive(Debug, serde::Deserialize, utoipa::IntoParams)]
|
||||
pub struct MessageQueryParams {
|
||||
pub channel_id: Option<uuid::Uuid>,
|
||||
pub channel_id: Uuid,
|
||||
pub before_id: Option<Uuid>,
|
||||
pub after_id: Option<Uuid>,
|
||||
pub limit: Option<u64>,
|
||||
}
|
||||
|
||||
impl Default for MessageQueryParams {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
channel_id: None,
|
||||
before_id: None,
|
||||
limit: Some(50),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-20
@@ -1,20 +1,20 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
pub mod attachment;
|
||||
pub mod category;
|
||||
pub mod channel;
|
||||
pub mod channel_role_permission;
|
||||
pub mod channel_user;
|
||||
pub mod channel_user_permission;
|
||||
pub mod computed_permission;
|
||||
pub mod message;
|
||||
pub mod role;
|
||||
pub mod role_user;
|
||||
pub mod server;
|
||||
pub mod server_item_order;
|
||||
pub mod server_role_permission;
|
||||
pub mod server_user;
|
||||
pub mod server_user_permission;
|
||||
pub mod user;
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
pub mod attachment;
|
||||
pub mod category;
|
||||
pub mod channel;
|
||||
pub mod channel_role_permission;
|
||||
pub mod channel_user;
|
||||
pub mod channel_user_permission;
|
||||
pub mod computed_permission;
|
||||
pub mod message;
|
||||
pub mod role;
|
||||
pub mod role_user;
|
||||
pub mod server;
|
||||
pub mod server_item_order;
|
||||
pub mod server_role_permission;
|
||||
pub mod server_user;
|
||||
pub mod server_user_permission;
|
||||
pub mod user;
|
||||
|
||||
+16
-16
@@ -1,16 +1,16 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
pub use super::attachment::Entity as Attachment;
|
||||
pub use super::category::Entity as Category;
|
||||
pub use super::channel::Entity as Channel;
|
||||
pub use super::channel_user::Entity as ChannelUser;
|
||||
pub use super::computed_permission::Entity as ComputedPermission;
|
||||
pub use super::message::Entity as Message;
|
||||
pub use super::role::Entity as Group;
|
||||
pub use super::role_user::Entity as GroupMember;
|
||||
pub use super::server::Entity as Server;
|
||||
pub use super::server_item_order::Entity as ServerItemOrder;
|
||||
pub use super::server_role_permission::Entity as ServerRolePermission;
|
||||
pub use super::server_user::Entity as ServerUser;
|
||||
pub use super::server_user_permission::Entity as ServerUserPermission;
|
||||
pub use super::user::Entity as User;
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
pub use super::attachment::Entity as Attachment;
|
||||
pub use super::category::Entity as Category;
|
||||
pub use super::channel::Entity as Channel;
|
||||
pub use super::channel_user::Entity as ChannelUser;
|
||||
pub use super::computed_permission::Entity as ComputedPermission;
|
||||
pub use super::message::Entity as Message;
|
||||
pub use super::role::Entity as Group;
|
||||
pub use super::role_user::Entity as GroupMember;
|
||||
pub use super::server::Entity as Server;
|
||||
pub use super::server_item_order::Entity as ServerItemOrder;
|
||||
pub use super::server_role_permission::Entity as ServerRolePermission;
|
||||
pub use super::server_user::Entity as ServerUser;
|
||||
pub use super::server_user_permission::Entity as ServerUserPermission;
|
||||
pub use super::user::Entity as User;
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
use super::types::MessageFilter;
|
||||
use crate::models::{channel, message};
|
||||
use crate::models::message;
|
||||
use crate::repositories::{AnyResult, RepositoryContext};
|
||||
use event_bus::Scope;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect};
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
const DEFAULT_MESSAGE_LIMIT: u64 = 20;
|
||||
pub const MAX_MESSAGE_LIMIT: u64 = 100;
|
||||
|
||||
pub struct MessagePage {
|
||||
pub messages: Vec<message::Model>,
|
||||
pub has_more_before: bool,
|
||||
pub has_more_after: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MessageRepository {
|
||||
@@ -21,7 +30,11 @@ impl MessageRepository {
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn filter(&self, filter: MessageFilter) -> AnyResult<Vec<message::Model>> {
|
||||
pub async fn filter(&self, filter: MessageFilter) -> AnyResult<MessagePage> {
|
||||
let limit = filter
|
||||
.limit
|
||||
.unwrap_or(DEFAULT_MESSAGE_LIMIT)
|
||||
.clamp(1, MAX_MESSAGE_LIMIT);
|
||||
let mut query = message::Entity::find();
|
||||
|
||||
if let Some(channel_id) = filter.channel_id {
|
||||
@@ -32,11 +45,80 @@ impl MessageRepository {
|
||||
query = query.filter(message::Column::Id.lt(before_id));
|
||||
}
|
||||
|
||||
if let Some(limit) = filter.limit {
|
||||
query = query.order_by_desc(message::Column::Id).limit(limit);
|
||||
if let Some(after_id) = filter.after_id {
|
||||
query = query
|
||||
.filter(message::Column::Id.gt(after_id))
|
||||
.order_by_asc(message::Column::Id);
|
||||
} else {
|
||||
query = query.order_by_desc(message::Column::Id);
|
||||
}
|
||||
|
||||
Ok(query.all(&self.context.db).await?)
|
||||
let mut messages = query.limit(limit + 1).all(&self.context.db).await?;
|
||||
let has_more_in_direction = messages.len() > limit as usize;
|
||||
messages.truncate(limit as usize);
|
||||
|
||||
// Queries that walk backwards are executed in descending order so the
|
||||
// database can stop as soon as it has found the requested rows. The UI
|
||||
// always receives chronological order.
|
||||
if filter.after_id.is_none() {
|
||||
messages.reverse();
|
||||
}
|
||||
|
||||
let (has_more_before, has_more_after) = if filter.after_id.is_some() {
|
||||
let has_messages_before = self
|
||||
.exists_on_or_before(filter.channel_id, filter.after_id.unwrap())
|
||||
.await?;
|
||||
(has_messages_before, has_more_in_direction)
|
||||
} else if filter.before_id.is_some() {
|
||||
let has_messages_after = self
|
||||
.exists_on_or_after(filter.channel_id, filter.before_id.unwrap())
|
||||
.await?;
|
||||
(has_more_in_direction, has_messages_after)
|
||||
} else {
|
||||
(has_more_in_direction, false)
|
||||
};
|
||||
|
||||
Ok(MessagePage {
|
||||
messages,
|
||||
has_more_before,
|
||||
has_more_after,
|
||||
})
|
||||
}
|
||||
|
||||
async fn exists_on_or_before(&self, channel_id: Option<Uuid>, id: Uuid) -> AnyResult<bool> {
|
||||
let mut query = message::Entity::find()
|
||||
.select_only()
|
||||
.column(message::Column::Id)
|
||||
.filter(message::Column::Id.lte(id));
|
||||
|
||||
if let Some(channel_id) = channel_id {
|
||||
query = query.filter(message::Column::ChannelId.eq(channel_id));
|
||||
}
|
||||
|
||||
Ok(query
|
||||
.limit(1)
|
||||
.into_tuple::<Uuid>()
|
||||
.one(&self.context.db)
|
||||
.await?
|
||||
.is_some())
|
||||
}
|
||||
|
||||
async fn exists_on_or_after(&self, channel_id: Option<Uuid>, id: Uuid) -> AnyResult<bool> {
|
||||
let mut query = message::Entity::find()
|
||||
.select_only()
|
||||
.column(message::Column::Id)
|
||||
.filter(message::Column::Id.gte(id));
|
||||
|
||||
if let Some(channel_id) = channel_id {
|
||||
query = query.filter(message::Column::ChannelId.eq(channel_id));
|
||||
}
|
||||
|
||||
Ok(query
|
||||
.limit(1)
|
||||
.into_tuple::<Uuid>()
|
||||
.one(&self.context.db)
|
||||
.await?
|
||||
.is_some())
|
||||
}
|
||||
|
||||
pub async fn get_by_channel(&self, channel_id: uuid::Uuid) -> AnyResult<Vec<message::Model>> {
|
||||
|
||||
@@ -24,6 +24,7 @@ pub struct ServerTree {
|
||||
pub struct MessageFilter {
|
||||
pub channel_id: Option<uuid::Uuid>,
|
||||
pub before_id: Option<uuid::Uuid>,
|
||||
pub after_id: Option<uuid::Uuid>,
|
||||
pub limit: Option<u64>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
use crate::domain::dto::message::{CreateMessageRequest, MessageQueryParams, MessageResponse, UpdateMessageRequest};
|
||||
use crate::core::state::AppState;
|
||||
use crate::domain::dto::message::{
|
||||
CreateMessageRequest, MessagePageResponse, MessageQueryParams, MessageResponse,
|
||||
UpdateMessageRequest,
|
||||
};
|
||||
use crate::http::context::CurrentUser;
|
||||
use crate::http::error::HTTPError;
|
||||
use crate::routes::message::mapper;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Path, Query, State},
|
||||
http::StatusCode,
|
||||
Json,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Liste tous les messages
|
||||
/// Liste une fenêtre paginée de messages
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/messages",
|
||||
responses(
|
||||
(status = 200, description = "Liste des messages récupérée avec succès", body = [MessageResponse]),
|
||||
(status = 200, description = "Fenêtre de messages récupérée avec succès", body = MessagePageResponse),
|
||||
(status = 400, description = "Curseurs incompatibles ou canal manquant"),
|
||||
(status = 500, description = "Erreur interne du serveur")
|
||||
),
|
||||
params(
|
||||
@@ -26,15 +30,29 @@ use uuid::Uuid;
|
||||
pub async fn get_all(
|
||||
State(state): State<AppState>,
|
||||
Query(filters): Query<MessageQueryParams>,
|
||||
) -> Result<Json<Vec<MessageResponse>>, HTTPError> {
|
||||
) -> Result<Json<MessagePageResponse>, HTTPError> {
|
||||
if filters.before_id.is_some() && filters.after_id.is_some() {
|
||||
return Err(HTTPError::BadRequest(
|
||||
"before_id and after_id cannot be used together".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let params = mapper::query_params_to_message_filter(filters);
|
||||
let messages = state.repositories.message.filter(params).await?;
|
||||
Ok(Json(
|
||||
messages
|
||||
let page = state.repositories.message.filter(params).await?;
|
||||
let oldest_id = page.messages.first().map(|message| message.id);
|
||||
let newest_id = page.messages.last().map(|message| message.id);
|
||||
|
||||
Ok(Json(MessagePageResponse {
|
||||
messages: page
|
||||
.messages
|
||||
.into_iter()
|
||||
.map(mapper::message_model_to_message_response)
|
||||
.collect(),
|
||||
))
|
||||
oldest_id,
|
||||
newest_id,
|
||||
has_more_before: page.has_more_before,
|
||||
has_more_after: page.has_more_after,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Récupère un message par son ID
|
||||
@@ -105,7 +123,11 @@ pub async fn create(
|
||||
))?;
|
||||
}
|
||||
|
||||
let message = state.services.message.create_message(payload.channel_id, user.id, payload.content).await?;
|
||||
let message = state
|
||||
.services
|
||||
.message
|
||||
.create_message(payload.channel_id, user.id, payload.content)
|
||||
.await?;
|
||||
Ok((
|
||||
StatusCode::CREATED,
|
||||
Json(mapper::message_model_to_message_response(message)),
|
||||
@@ -150,7 +172,11 @@ pub async fn update(
|
||||
return Err(HTTPError::Forbidden);
|
||||
}
|
||||
|
||||
let message = state.services.message.update_message(id, payload.content).await?;
|
||||
let message = state
|
||||
.services
|
||||
.message
|
||||
.update_message(id, payload.content)
|
||||
.await?;
|
||||
|
||||
Ok(Json(mapper::message_model_to_message_response(message)))
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::models::message;
|
||||
use crate::repositories::types::MessageFilter;
|
||||
use crate::domain::dto::message::{
|
||||
CreateMessageRequest, MessageQueryParams, MessageResponse, UpdateMessageRequest,
|
||||
};
|
||||
use crate::models::message;
|
||||
use crate::repositories::types::MessageFilter;
|
||||
use chrono::Utc;
|
||||
use sea_orm::Set;
|
||||
use uuid::Uuid;
|
||||
@@ -48,8 +48,9 @@ pub fn update_request_to_am(
|
||||
|
||||
pub fn query_params_to_message_filter(params: MessageQueryParams) -> MessageFilter {
|
||||
MessageFilter {
|
||||
channel_id: params.channel_id,
|
||||
channel_id: Some(params.channel_id),
|
||||
before_id: params.before_id,
|
||||
after_id: params.after_id,
|
||||
limit: params.limit,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user