mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-10 19:29:50 +08:00
Merge remote-tracking branch 'origin/pr/564'
This commit is contained in:
@@ -7,8 +7,8 @@ use super::types::{
|
||||
normalize_user_group_name, LdapAuthUserProvisioningOutcome, StoredUserAuthRecord,
|
||||
StoredUserExportRow, StoredUserGroup, StoredUserGroupMember, StoredUserGroupMembership,
|
||||
StoredUserOAuthLinkSummary, StoredUserPreferenceRecord, StoredUserSessionRecord,
|
||||
StoredUserSummary, UpsertUserGroupRecord, UserExportListQuery, UserExportSummary,
|
||||
UserReadRepository,
|
||||
StoredUserSummary, UpsertUserGroupRecord, UserExportListQuery, UserExportSortBy,
|
||||
UserExportSummary, UserReadRepository,
|
||||
};
|
||||
use crate::DataLayerError;
|
||||
|
||||
@@ -409,6 +409,34 @@ fn filter_memory_export_rows(
|
||||
.contains(&search)
|
||||
});
|
||||
}
|
||||
match query.sort_by {
|
||||
UserExportSortBy::CreatedAt => {
|
||||
let created_at_by_id = repository
|
||||
.auth_by_id
|
||||
.read()
|
||||
.expect("user repository lock")
|
||||
.iter()
|
||||
.filter_map(|(user_id, user)| {
|
||||
user.created_at
|
||||
.map(|created_at| (user_id.clone(), created_at.timestamp_millis()))
|
||||
})
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
rows.sort_by(|left, right| {
|
||||
let primary = created_at_by_id
|
||||
.get(&left.id)
|
||||
.cmp(&created_at_by_id.get(&right.id));
|
||||
let ordered = if query.sort_order.is_desc() {
|
||||
primary.reverse()
|
||||
} else {
|
||||
primary
|
||||
};
|
||||
ordered.then_with(|| left.id.cmp(&right.id))
|
||||
});
|
||||
}
|
||||
UserExportSortBy::Id => {
|
||||
rows.sort_by(|left, right| left.id.cmp(&right.id));
|
||||
}
|
||||
}
|
||||
rows
|
||||
}
|
||||
|
||||
@@ -2682,6 +2710,7 @@ mod tests {
|
||||
is_active: Some(true),
|
||||
search: None,
|
||||
group_id: None,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.expect("paged export should succeed");
|
||||
|
||||
@@ -12,5 +12,6 @@ pub use types::{
|
||||
normalize_user_group_name, StoredUserAuthRecord, StoredUserExportRow, StoredUserGroup,
|
||||
StoredUserGroupMember, StoredUserGroupMembership, StoredUserOAuthLinkSummary,
|
||||
StoredUserPreferenceRecord, StoredUserSessionRecord, StoredUserSummary, UpsertUserGroupRecord,
|
||||
UserExportListQuery, UserExportSummary, UserReadRepository,
|
||||
UserExportListQuery, UserExportSortBy, UserExportSortOrder, UserExportSummary,
|
||||
UserReadRepository,
|
||||
};
|
||||
|
||||
@@ -6,8 +6,8 @@ use super::types::{
|
||||
normalize_user_group_name, LdapAuthUserProvisioningOutcome, StoredUserAuthRecord,
|
||||
StoredUserExportRow, StoredUserGroup, StoredUserGroupMember, StoredUserGroupMembership,
|
||||
StoredUserOAuthLinkSummary, StoredUserPreferenceRecord, StoredUserSessionRecord,
|
||||
StoredUserSummary, UpsertUserGroupRecord, UserExportListQuery, UserExportSummary,
|
||||
UserReadRepository,
|
||||
StoredUserSummary, UpsertUserGroupRecord, UserExportListQuery, UserExportSortBy,
|
||||
UserExportSummary, UserReadRepository,
|
||||
};
|
||||
use crate::driver::mysql::MysqlPool;
|
||||
use crate::error::SqlResultExt;
|
||||
@@ -313,8 +313,24 @@ impl UserReadRepository for MysqlUserReadRepository {
|
||||
.push_bind(pattern)
|
||||
.push(")");
|
||||
}
|
||||
match query.sort_by {
|
||||
UserExportSortBy::CreatedAt => {
|
||||
builder
|
||||
.push(" ORDER BY created_at ")
|
||||
.push(if query.sort_order.is_desc() {
|
||||
"DESC"
|
||||
} else {
|
||||
"ASC"
|
||||
})
|
||||
.push(", id ASC");
|
||||
}
|
||||
UserExportSortBy::Id => {
|
||||
builder.push(" ORDER BY id ASC");
|
||||
}
|
||||
}
|
||||
|
||||
builder
|
||||
.push(" ORDER BY id ASC LIMIT ")
|
||||
.push(" LIMIT ")
|
||||
.push_bind(i64::try_from(query.limit).map_err(|_| {
|
||||
DataLayerError::InvalidInput(format!("invalid user export limit: {}", query.limit))
|
||||
})?)
|
||||
|
||||
@@ -6,8 +6,8 @@ use super::types::{
|
||||
normalize_user_group_name, LdapAuthUserProvisioningOutcome, StoredUserAuthRecord,
|
||||
StoredUserExportRow, StoredUserGroup, StoredUserGroupMember, StoredUserGroupMembership,
|
||||
StoredUserOAuthLinkSummary, StoredUserPreferenceRecord, StoredUserSessionRecord,
|
||||
StoredUserSummary, UpsertUserGroupRecord, UserExportListQuery, UserExportSummary,
|
||||
UserReadRepository,
|
||||
StoredUserSummary, UpsertUserGroupRecord, UserExportListQuery, UserExportSortBy,
|
||||
UserExportSummary, UserReadRepository,
|
||||
};
|
||||
use crate::{error::SqlxResultExt, DataLayerError};
|
||||
|
||||
@@ -1001,8 +1001,24 @@ WHERE user_group_members.user_id IN (
|
||||
.push(")");
|
||||
}
|
||||
|
||||
match query.sort_by {
|
||||
UserExportSortBy::CreatedAt => {
|
||||
builder
|
||||
.push(" ORDER BY created_at ")
|
||||
.push(if query.sort_order.is_desc() {
|
||||
"DESC"
|
||||
} else {
|
||||
"ASC"
|
||||
})
|
||||
.push(", id ASC");
|
||||
}
|
||||
UserExportSortBy::Id => {
|
||||
builder.push(" ORDER BY id ASC");
|
||||
}
|
||||
}
|
||||
|
||||
builder
|
||||
.push(" ORDER BY id ASC OFFSET ")
|
||||
.push(" OFFSET ")
|
||||
.push_bind(i64::try_from(query.skip).map_err(|_| {
|
||||
DataLayerError::InvalidInput(format!("invalid user export skip: {}", query.skip))
|
||||
})?)
|
||||
|
||||
@@ -6,8 +6,8 @@ use super::types::{
|
||||
normalize_user_group_name, LdapAuthUserProvisioningOutcome, StoredUserAuthRecord,
|
||||
StoredUserExportRow, StoredUserGroup, StoredUserGroupMember, StoredUserGroupMembership,
|
||||
StoredUserOAuthLinkSummary, StoredUserPreferenceRecord, StoredUserSessionRecord,
|
||||
StoredUserSummary, UpsertUserGroupRecord, UserExportListQuery, UserExportSummary,
|
||||
UserReadRepository,
|
||||
StoredUserSummary, UpsertUserGroupRecord, UserExportListQuery, UserExportSortBy,
|
||||
UserExportSummary, UserReadRepository,
|
||||
};
|
||||
use crate::driver::sqlite::SqlitePool;
|
||||
use crate::error::SqlResultExt;
|
||||
@@ -313,8 +313,24 @@ impl UserReadRepository for SqliteUserReadRepository {
|
||||
.push_bind(pattern)
|
||||
.push(")");
|
||||
}
|
||||
match query.sort_by {
|
||||
UserExportSortBy::CreatedAt => {
|
||||
builder
|
||||
.push(" ORDER BY created_at ")
|
||||
.push(if query.sort_order.is_desc() {
|
||||
"DESC"
|
||||
} else {
|
||||
"ASC"
|
||||
})
|
||||
.push(", id ASC");
|
||||
}
|
||||
UserExportSortBy::Id => {
|
||||
builder.push(" ORDER BY id ASC");
|
||||
}
|
||||
}
|
||||
|
||||
builder
|
||||
.push(" ORDER BY id ASC LIMIT ")
|
||||
.push(" LIMIT ")
|
||||
.push_bind(i64::try_from(query.limit).map_err(|_| {
|
||||
DataLayerError::InvalidInput(format!("invalid user export limit: {}", query.limit))
|
||||
})?)
|
||||
@@ -2199,6 +2215,7 @@ INSERT INTO users (
|
||||
is_active: Some(true),
|
||||
search: None,
|
||||
group_id: None,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.expect("export page should load");
|
||||
|
||||
@@ -642,6 +642,46 @@ pub struct UserExportListQuery {
|
||||
pub is_active: Option<bool>,
|
||||
pub search: Option<String>,
|
||||
pub group_id: Option<String>,
|
||||
pub sort_by: UserExportSortBy,
|
||||
pub sort_order: UserExportSortOrder,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum UserExportSortBy {
|
||||
#[default]
|
||||
Id,
|
||||
CreatedAt,
|
||||
}
|
||||
|
||||
impl UserExportSortBy {
|
||||
pub fn parse(value: &str) -> Option<Self> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"created_at" => Some(Self::CreatedAt),
|
||||
"id" => Some(Self::Id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum UserExportSortOrder {
|
||||
#[default]
|
||||
Asc,
|
||||
Desc,
|
||||
}
|
||||
|
||||
impl UserExportSortOrder {
|
||||
pub fn parse(value: &str) -> Option<Self> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"asc" => Some(Self::Asc),
|
||||
"desc" => Some(Self::Desc),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_desc(self) -> bool {
|
||||
matches!(self, Self::Desc)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
|
||||
|
||||
Reference in New Issue
Block a user