use axum::{ extract::{Json, Path, State}, http::StatusCode, response::{IntoResponse, Response}, routing::{delete, get, post}, Router, }; use axum_extra::extract::Query; use super::{app, Channel, Id}; use crate::{ app::App, clock::RequestedAt, error::Internal, event::{Instant, Sequence}, login::Login, message::{self, app::SendError}, }; #[cfg(test)] mod test; pub fn router() -> Router { Router::new() .route("/api/channels", get(list)) .route("/api/channels", post(on_create)) .route("/api/channels/:channel", post(on_send)) .route("/api/channels/:channel", delete(on_delete)) .route("/api/channels/:channel/messages", get(messages)) } #[derive(Default, serde::Deserialize)] struct ResumeQuery { resume_point: Option, } async fn list( State(app): State, _: Login, Query(query): Query, ) -> Result { let channels = app.channels().all(query.resume_point).await?; let response = Channels(channels); Ok(response) } struct Channels(Vec); impl IntoResponse for Channels { fn into_response(self) -> Response { let Self(channels) = self; Json(channels).into_response() } } #[derive(Clone, serde::Deserialize)] struct CreateRequest { name: String, } async fn on_create( State(app): State, _: Login, // requires auth, but doesn't actually care who you are RequestedAt(created_at): RequestedAt, Json(form): Json, ) -> Result, CreateError> { let channel = app .channels() .create(&form.name, &created_at) .await .map_err(CreateError)?; Ok(Json(channel)) } #[derive(Debug)] struct CreateError(app::CreateError); impl IntoResponse for CreateError { fn into_response(self) -> Response { let Self(error) = self; match error { duplicate @ app::CreateError::DuplicateName(_) => { (StatusCode::BAD_REQUEST, duplicate.to_string()).into_response() } other => Internal::from(other).into_response(), } } } #[derive(Clone, serde::Deserialize)] struct SendRequest { message: String, } async fn on_send( State(app): State, Path(channel): Path, RequestedAt(sent_at): RequestedAt, login: Login, Json(request): Json, ) -> Result { app.messages() .send(&channel, &login, &sent_at, &request.message) .await?; Ok(StatusCode::ACCEPTED) } #[derive(Debug, thiserror::Error)] #[error(transparent)] struct SendErrorResponse(#[from] SendError); impl IntoResponse for SendErrorResponse { fn into_response(self) -> Response { let Self(error) = self; match error { not_found @ SendError::ChannelNotFound(_) => { (StatusCode::NOT_FOUND, not_found.to_string()).into_response() } other => Internal::from(other).into_response(), } } } async fn on_delete( State(app): State, Path(channel): Path, RequestedAt(deleted_at): RequestedAt, _: Login, ) -> Result { app.channels().delete(&channel, &deleted_at).await?; Ok(StatusCode::ACCEPTED) } #[derive(Debug, thiserror::Error)] #[error(transparent)] struct ErrorResponse(#[from] app::Error); impl IntoResponse for ErrorResponse { fn into_response(self) -> Response { let Self(error) = self; match error { not_found @ app::Error::NotFound(_) => { (StatusCode::NOT_FOUND, not_found.to_string()).into_response() } other => Internal::from(other).into_response(), } } } async fn messages( State(app): State, Path(channel): Path, _: Login, Query(query): Query, ) -> Result { let messages = app .channels() .messages(&channel, query.resume_point) .await?; let response = Messages( messages .into_iter() .map(|message| MessageView { sent: message.sent, sender: message.sender, message: MessageInner { id: message.id, body: message.body, }, }) .collect(), ); Ok(response) } struct Messages(Vec); #[derive(serde::Serialize)] struct MessageView { #[serde(flatten)] sent: Instant, sender: Login, message: MessageInner, } #[derive(serde::Serialize)] struct MessageInner { id: message::Id, body: String, } impl IntoResponse for Messages { fn into_response(self) -> Response { let Self(messages) = self; Json(messages).into_response() } }