Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions crates/handlers/src/admin/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,42 @@ pub trait Resource {
}
}

/// A user's email address
#[derive(Serialize, JsonSchema)]
pub struct UserEmail {
#[serde(skip)]
id: Ulid,

/// The email address
email: String,

/// When the email address was created/verified
created_at: DateTime<Utc>,
}

impl UserEmail {
/// Samples of user emails
pub fn samples() -> [Vec<Self>; 1] {
[
Vec::from(Self {
id: Ulid::from_bytes([0x01; 16]),
created_at: DateTime::default(),
email: "[email protected]".to_owned(),
}),
]
}
}

impl From<mas_data_model::UserEmail> for UserEmail {
fn from(email: mas_data_model::UserEmail) -> Self {
Self {
id: email.id,
email: email.email,
created_at: email.created_at,
}
}
}

/// A user
#[derive(Serialize, JsonSchema)]
pub struct User {
Expand Down
4 changes: 4 additions & 0 deletions crates/handlers/src/admin/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ where
"/users/{id}",
get_with(self::users::get, self::users::get_doc),
)
.api_route(
"/users/{id}/emails",
get_with(self::users::get_emails, self::users::get_emails_doc),
)
.api_route(
"/users/{id}/set-password",
post_with(self::users::set_password, self::users::set_password_doc),
Expand Down
83 changes: 83 additions & 0 deletions crates/handlers/src/admin/v1/users/emails.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2024 New Vector Ltd.
// Copyright 2024 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.

use aide::{transform::TransformOperation, OperationIo};
use axum::{response::IntoResponse, Json};
use hyper::StatusCode;
use ulid::Ulid;
use mas_storage::{Pagination, RepositoryAccess};
use mas_storage::user::{UserEmailFilter, UserEmailRepository};
use crate::{
admin::{
call_context::CallContext,
model::User,
params::UlidPathParam,
response::{ErrorResponse, SingleResponse},
},
impl_from_error_for_route,
};
use crate::admin::model::UserEmail;

#[derive(Debug, thiserror::Error, OperationIo)]
#[aide(output_with = "Json<ErrorResponse>")]
pub enum RouteError {
#[error(transparent)]
Internal(Box<dyn std::error::Error + Send + Sync + 'static>),

#[error("User ID {0} not found")]
NotFound(Ulid),
}

impl_from_error_for_route!(mas_storage::RepositoryError);

impl IntoResponse for RouteError {
fn into_response(self) -> axum::response::Response {
let error = ErrorResponse::from_error(&self);
let status = match self {
Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
Self::NotFound(_) => StatusCode::NOT_FOUND,
};
(status, Json(error)).into_response()
}
}

pub fn doc(operation: TransformOperation) -> TransformOperation {
operation
.id("getUserEmails")
.summary("Get a user's emails")
.tag("user")
.response_with::<200, Json<SingleResponse<Vec<UserEmail>>>, _>(|t| {
let [sample, ..] = UserEmail::samples();
let response = SingleResponse::new_canonical(sample);
t.description("User was found").example(response)
})
.response_with::<404, RouteError, _>(|t| {
let response = ErrorResponse::from_error(&RouteError::NotFound(Ulid::nil()));
t.description("User was not found").example(response)
})
}

#[tracing::instrument(name = "handler.admin.v1.users.get_emails", skip_all, err)]
pub async fn handler(
CallContext { mut repo, .. }: CallContext,
id: UlidPathParam,
) -> Result<Json<SingleResponse<Vec<UserEmail>>>, RouteError> {
let user = repo
.user()
.lookup(*id)
.await?
.ok_or(RouteError::NotFound(*id))?;

let emails: Vec<UserEmail> = repo
.user_email()
.all(&user)
.await?
.iter()
.map(|e| UserEmail::from(e))
.into();

Ok(Json(SingleResponse::new_canonical(emails)))
}
3 changes: 2 additions & 1 deletion crates/handlers/src/admin/v1/users/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use aide::{transform::TransformOperation, OperationIo};
use axum::{response::IntoResponse, Json};
use hyper::StatusCode;
use ulid::Ulid;

use mas_storage::{RepositoryAccess};
use mas_storage::user::{UserEmailRepository};
use crate::{
admin::{
call_context::CallContext,
Expand Down
2 changes: 2 additions & 0 deletions crates/handlers/src/admin/v1/users/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
mod add;
mod by_username;
mod deactivate;
mod emails;
mod get;
mod list;
mod lock;
Expand All @@ -18,6 +19,7 @@ pub use self::{
add::{doc as add_doc, handler as add},
by_username::{doc as by_username_doc, handler as by_username},
deactivate::{doc as deactivate_doc, handler as deactivate},
emails::{doc as get_emails_doc, handler as get_emails},
get::{doc as get_doc, handler as get},
list::{doc as list_doc, handler as list},
lock::{doc as lock_doc, handler as lock},
Expand Down
Loading