use axum::{
extract::{Path, Query, State},
http::HeaderMap,
Json,
};
use serde::Deserialize;
use std::sync::Arc;
use uuid::Uuid;
use crate::callback::AuthCallback;
use crate::errors::AppError;
use crate::models::{ListMembersResponse, MemberResponse};
use crate::services::EmailService;
use crate::utils::authenticate;
use crate::AppState;
const MAX_LIMIT: u32 = 200;
pub async fn list_members<C: AuthCallback, E: EmailService>(
State(state): State<Arc<AppState<C, E>>>,
headers: HeaderMap,
Path(org_id): Path<Uuid>,
Query(params): Query<ListMembersQueryParams>,
) -> Result<Json<ListMembersResponse>, AppError> {
let auth = authenticate(&state, &headers).await?;
let _caller_membership = state
.membership_repo
.find_by_user_and_org(auth.user_id, org_id)
.await?
.ok_or(AppError::Forbidden(
"Not a member of this organization".into(),
))?;
let limit = clamp_limit(params.limit);
let members_with_users = state
.membership_repo
.find_by_org_with_users_paged(org_id, limit, params.offset)
.await?;
let total = state.membership_repo.count_by_org(org_id).await?;
let members: Vec<MemberResponse> = members_with_users
.into_iter()
.map(|m| MemberResponse::from_membership(&m.membership, m.email, m.name))
.collect();
Ok(Json(ListMembersResponse { members, total }))
}
#[derive(Debug, Deserialize)]
pub struct ListMembersQueryParams {
#[serde(default = "default_limit")]
pub limit: u32,
#[serde(default)]
pub offset: u32,
}
fn default_limit() -> u32 {
50
}
fn clamp_limit(limit: u32) -> u32 {
limit.min(MAX_LIMIT)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_clamp_limit() {
assert_eq!(clamp_limit(10), 10);
assert_eq!(clamp_limit(500), MAX_LIMIT);
}
}