magnetar/ext_calckey_model/src/notification_model.rs

197 lines
6.2 KiB
Rust

use crate::model_ext::{
AliasColumnExt, AliasSourceExt, AliasSuffixExt, CursorPaginationExt, EntityPrefixExt, MagIden,
ModelPagination, SelectColumnsExt,
};
use crate::note_model::data::NoteData;
use crate::note_model::{NoteResolveOptions, NoteResolver};
use crate::user_model::{UserData, UserResolveOptions, UserResolver};
use crate::{CalckeyDbError, CalckeyModel};
use chrono::{DateTime, Utc};
use ck::sea_orm_active_enums::NotificationTypeEnum;
use ck::{access_token, notification, user};
use ext_calckey_model_migration::{JoinType, SelectStatement};
use magnetar_sdk::types::SpanFilter;
use sea_orm::prelude::Expr;
use sea_orm::sea_query::{IntoCondition, Query};
use sea_orm::{ActiveEnum, Iden, IntoSimpleExpr, QueryTrait};
use sea_orm::{DbErr, EntityTrait, FromQueryResult, QueryFilter, QueryResult, QuerySelect};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NotificationData {
pub notification: notification::Model,
pub access_token: Option<access_token::Model>,
pub notifier: Option<UserData>,
pub notification_note: Option<NoteData>,
}
impl FromQueryResult for NotificationData {
fn from_query_result(res: &QueryResult, prefix: &str) -> Result<Self, DbErr> {
let prefix = if prefix.is_empty() {
notification::Entity.base_prefix()
} else {
MagIden::alias(prefix)
};
Ok(NotificationData {
notification: notification::Model::from_query_result(res, &prefix.to_string())?,
access_token: access_token::Model::from_query_result_optional(
res,
&prefix.join_str_as_str(ACCESS_TOKEN),
)?,
notification_note: NoteData::from_query_result_optional(
res,
&prefix.join_str_as_str(NOTIFICATION_NOTE),
)?,
notifier: UserData::from_query_result_optional(res, &prefix.join_str_as_str(NOTIFIER))?,
})
}
}
impl ModelPagination for NotificationData {
fn id(&self) -> &str {
&self.notification.id
}
fn time(&self) -> DateTime<Utc> {
self.notification.created_at.into()
}
}
const NOTIFIER: &str = "notifier.";
const NOTIFICATION_NOTE: &str = "note.";
const ACCESS_TOKEN: &str = "access_token.";
pub struct NotificationResolveOptions {
pub note_options: NoteResolveOptions,
pub user_options: UserResolveOptions,
}
#[derive(Clone)]
pub struct NotificationResolver {
db: CalckeyModel,
note_resolver: NoteResolver,
user_resolver: UserResolver,
}
impl NotificationResolver {
pub(crate) fn new(
db: CalckeyModel,
user_resolver: UserResolver,
note_resolver: NoteResolver,
) -> Self {
Self {
db,
note_resolver,
user_resolver,
}
}
pub fn resolve(
&self,
q: &mut SelectStatement,
notification_tbl: &MagIden,
resolve_options: &NotificationResolveOptions,
note_resolver: &NoteResolver,
user_resolver: &UserResolver,
) {
q.add_aliased_columns::<notification::Entity>(&notification_tbl);
let notifier_tbl = notification_tbl.join_str(NOTIFIER);
q.add_aliased_columns::<user::Entity>(&notifier_tbl);
q.join_columns(
JoinType::LeftJoin,
notification::Relation::User2.with_from_alias(notification_tbl),
&notifier_tbl,
);
user_resolver.resolve(q, &notifier_tbl, &resolve_options.user_options);
let token_tbl = notification_tbl.join_str(ACCESS_TOKEN);
q.add_aliased_columns::<access_token::Entity>(&token_tbl);
q.join_columns(
JoinType::LeftJoin,
notification::Relation::AccessToken.with_from_alias(notification_tbl),
&token_tbl,
);
let note_tbl = notification_tbl.join_str(NOTIFICATION_NOTE);
q.join_columns(
JoinType::LeftJoin,
notification::Relation::Note.with_from_alias(notification_tbl),
&note_tbl,
);
note_resolver.attach_note(
q,
&note_tbl,
1,
1,
&resolve_options.note_options,
&self.user_resolver,
);
}
pub async fn get(
&self,
resolve_options: &NotificationResolveOptions,
user_id: &str,
notification_types: &[NotificationTypeEnum],
unread_only: bool,
pagination: &SpanFilter,
prev: &mut Option<SpanFilter>,
next: &mut Option<SpanFilter>,
limit: u64,
) -> Result<Vec<NotificationData>, CalckeyDbError> {
let notification_tbl = notification::Entity.base_prefix();
let mut query = Query::select();
query.from_as(notification::Entity, notification_tbl.clone());
self.resolve(
&mut query,
&notification_tbl,
&resolve_options,
&self.note_resolver,
&self.user_resolver,
);
let mut select = notification::Entity::find();
*QuerySelect::query(&mut select) = query;
let notifications = select
.filter(
notification_tbl
.col(notification::Column::NotifieeId)
.eq(user_id)
.and(
notification_tbl.col(notification::Column::Type).is_in(
notification_types
.iter()
.copied()
.map(Expr::val)
.map(|e| e.cast_as(NotificationTypeEnum::name())),
),
),
)
.apply_if(unread_only.then_some(()), |s, _| {
s.filter(
notification_tbl
.col(notification::Column::IsRead)
.not()
.into_condition(),
)
})
.get_paginated_model::<NotificationData, _, _>(
&self.db.0,
Some(notification_tbl),
(notification::Column::CreatedAt, notification::Column::Id),
pagination,
prev,
next,
limit,
)
.await?;
Ok(notifications)
}
}