mas_handlers/admin/v1/user_sessions/
get.rsuse aide::{OperationIo, transform::TransformOperation};
use axum::{Json, response::IntoResponse};
use hyper::StatusCode;
use ulid::Ulid;
use crate::{
admin::{
call_context::CallContext,
model::UserSession,
params::UlidPathParam,
response::{ErrorResponse, SingleResponse},
},
impl_from_error_for_route,
};
#[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 session 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("getUserSession")
.summary("Get a user session")
.tag("user-session")
.response_with::<200, Json<SingleResponse<UserSession>>, _>(|t| {
let [sample, ..] = UserSession::samples();
let response = SingleResponse::new_canonical(sample);
t.description("User session was found").example(response)
})
.response_with::<404, RouteError, _>(|t| {
let response = ErrorResponse::from_error(&RouteError::NotFound(Ulid::nil()));
t.description("User session was not found")
.example(response)
})
}
#[tracing::instrument(name = "handler.admin.v1.user_sessions.get", skip_all, err)]
pub async fn handler(
CallContext { mut repo, .. }: CallContext,
id: UlidPathParam,
) -> Result<Json<SingleResponse<UserSession>>, RouteError> {
let session = repo
.browser_session()
.lookup(*id)
.await?
.ok_or(RouteError::NotFound(*id))?;
Ok(Json(SingleResponse::new_canonical(UserSession::from(
session,
))))
}
#[cfg(test)]
mod tests {
use hyper::{Request, StatusCode};
use insta::assert_json_snapshot;
use sqlx::PgPool;
use crate::test_utils::{RequestBuilderExt, ResponseExt, TestState, setup};
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
async fn test_get(pool: PgPool) {
setup();
let mut state = TestState::from_pool(pool).await.unwrap();
let token = state.token_with_scope("urn:mas:admin").await;
let mut rng = state.rng();
let mut repo = state.repository().await.unwrap();
let user = repo
.user()
.add(&mut rng, &state.clock, "alice".to_owned())
.await
.unwrap();
let session = repo
.browser_session()
.add(&mut rng, &state.clock, &user, None)
.await
.unwrap();
repo.save().await.unwrap();
let session_id = session.id;
let request = Request::get(format!("/api/admin/v1/user-sessions/{session_id}"))
.bearer(&token)
.empty();
let response = state.request(request).await;
response.assert_status(StatusCode::OK);
let body: serde_json::Value = response.json();
assert_json_snapshot!(body, @r###"
{
"data": {
"type": "user-session",
"id": "01FSHN9AG0AJ6AC5HQ9X6H4RP4",
"attributes": {
"created_at": "2022-01-16T14:40:00Z",
"finished_at": null,
"user_id": "01FSHN9AG0MZAA6S4AF7CTV32E",
"user_agent": null,
"last_active_at": null,
"last_active_ip": null
},
"links": {
"self": "/api/admin/v1/user-sessions/01FSHN9AG0AJ6AC5HQ9X6H4RP4"
}
},
"links": {
"self": "/api/admin/v1/user-sessions/01FSHN9AG0AJ6AC5HQ9X6H4RP4"
}
}
"###);
}
}