From be053ac0dce66263cd8fc4feda720af56e26c287 Mon Sep 17 00:00:00 2001 From: phoenix Date: Sun, 24 Aug 2025 19:02:20 -0400 Subject: [PATCH] tsk-56: Added API docs --- src/callers/common.rs | 21 +++++++++++++-- src/callers/login.rs | 57 ++++++++++++++++++++++++++++++++++++----- src/callers/register.rs | 19 ++++++++++++-- src/main.rs | 31 +++++++++++++++++++++- 4 files changed, 117 insertions(+), 11 deletions(-) diff --git a/src/callers/common.rs b/src/callers/common.rs index bf178b3..b33a3fe 100644 --- a/src/callers/common.rs +++ b/src/callers/common.rs @@ -1,7 +1,7 @@ pub mod response { use serde::{Deserialize, Serialize}; - #[derive(Deserialize, Serialize)] + #[derive(Deserialize, Serialize, utoipa::ToSchema)] pub struct TestResult { pub message: String, } @@ -11,11 +11,28 @@ pub mod endpoint { use super::*; use axum::{Extension, Json, http::StatusCode}; - // basic handler that responds with a static string + /// Endpoint to hit the root + /// basic handler that responds with a static string + #[utoipa::path( + get, + path = super::super::endpoints::ROOT, + responses( + (status = 200, description = "Test", body = &str), + ) + )] pub async fn root() -> &'static str { "Hello, World!" } + /// Endpoint to do a database ping + #[utoipa::path( + get, + path = super::super::endpoints::DBTEST, + responses( + (status = 200, description = "Successful ping of the db", body = super::response::TestResult), + (status = 400, description = "Failure in pinging the db", body = super::response::TestResult) + ) + )] pub async fn db_ping( Extension(pool): Extension, ) -> (StatusCode, Json) { diff --git a/src/callers/login.rs b/src/callers/login.rs index fb065e5..5772d86 100644 --- a/src/callers/login.rs +++ b/src/callers/login.rs @@ -1,21 +1,21 @@ pub mod request { use serde::{Deserialize, Serialize}; - #[derive(Default, Deserialize, Serialize)] + #[derive(Default, Deserialize, Serialize, utoipa::ToSchema)] pub struct Request { pub username: String, pub password: String, } pub mod service_login { - #[derive(Debug, serde::Deserialize, serde::Serialize)] + #[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] pub struct Request { pub passphrase: String, } } pub mod refresh_token { - #[derive(Debug, serde::Deserialize, serde::Serialize)] + #[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] pub struct Request { pub access_token: String, } @@ -25,14 +25,14 @@ pub mod request { pub mod response { use serde::{Deserialize, Serialize}; - #[derive(Default, Deserialize, Serialize)] + #[derive(Default, Deserialize, Serialize, utoipa::ToSchema)] pub struct Response { pub message: String, pub data: Vec, } pub mod service_login { - #[derive(Debug, Default, serde::Deserialize, serde::Serialize)] + #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] pub struct Response { pub message: String, pub data: Vec, @@ -40,7 +40,7 @@ pub mod response { } pub mod refresh_token { - #[derive(Debug, Default, serde::Deserialize, serde::Serialize)] + #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] pub struct Response { pub message: String, pub data: Vec, @@ -48,6 +48,7 @@ pub mod response { } } +/// Module for login endpoints pub mod endpoint { use axum::{Json, http::StatusCode}; @@ -72,6 +73,20 @@ pub mod endpoint { ) } + /// Endpoint to login + #[utoipa::path( + post, + path = super::super::endpoints::LOGIN, + request_body( + content = request::Request, + description = "Data required to login", + content_type = "application/json" + ), + responses( + (status = 200, description = "Successfully logged in", body = response::Response), + (status = 404, description = "Could not login with credentials", body = response::Response) + ) + )] pub async fn login( axum::Extension(pool): axum::Extension, Json(payload): Json, @@ -115,6 +130,20 @@ pub mod endpoint { } } + /// Endpoint to login as a service user + #[utoipa::path( + post, + path = super::super::endpoints::SERVICE_LOGIN, + request_body( + content = request::service_login::Request, + description = "Data required to login as a service user", + content_type = "application/json" + ), + responses( + (status = 200, description = "Login successful", body = response::Response), + (status = 400, description = "Error logging in with credentials", body = response::Response) + ) + )] pub async fn service_login( axum::Extension(pool): axum::Extension, axum::Json(payload): axum::Json, @@ -154,6 +183,22 @@ pub mod endpoint { } } + /// Endpoint to retrieve a refresh token + #[utoipa::path( + post, + path = super::super::endpoints::REFRESH_TOKEN, + request_body( + content = request::refresh_token::Request, + description = "Data required to retrieve a refresh token", + content_type = "application/json" + ), + responses( + (status = 200, description = "Refresh token generated", body = response::Response), + (status = 400, description = "Error verifying token", body = response::Response), + (status = 404, description = "Could not validate token", body = response::Response), + (status = 500, description = "Error extracting token", body = response::Response) + ) + )] pub async fn refresh_token( axum::Extension(pool): axum::Extension, axum::Json(payload): axum::Json, diff --git a/src/callers/register.rs b/src/callers/register.rs index b9d07e4..bd8711f 100644 --- a/src/callers/register.rs +++ b/src/callers/register.rs @@ -6,7 +6,7 @@ use crate::repo; pub mod request { use serde::{Deserialize, Serialize}; - #[derive(Default, Deserialize, Serialize)] + #[derive(Default, Deserialize, Serialize, utoipa::ToSchema)] pub struct Request { #[serde(skip_serializing_if = "String::is_empty")] pub username: String, @@ -26,13 +26,28 @@ pub mod request { pub mod response { use serde::{Deserialize, Serialize}; - #[derive(Deserialize, Serialize)] + #[derive(Deserialize, Serialize, utoipa::ToSchema)] pub struct Response { pub message: String, pub data: Vec, } } +/// Endpoint to register a user +#[utoipa::path( + post, + path = super::endpoints::REGISTER, + request_body( + content = request::Request, + description = "Data required to register", + content_type = "application/json" + ), + responses( + (status = 201, description = "User created", body = response::Response), + (status = 404, description = "User already exists", body = response::Response), + (status = 400, description = "Issue creating user", body = response::Response) + ) +)] pub async fn register_user( axum::Extension(pool): axum::Extension, Json(payload): Json, diff --git a/src/main.rs b/src/main.rs index e052521..c13b7f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,8 +19,31 @@ mod init { Router, routing::{get, post}, }; + use utoipa::OpenApi; use crate::callers; + use callers::common as common_callers; + use callers::login as login_caller; + use callers::register as register_caller; + use login_caller::endpoint as login_endpoints; + use login_caller::response as login_responses; + use register_caller::response as register_responses; + + #[derive(utoipa::OpenApi)] + #[openapi( + paths( + common_callers::endpoint::db_ping, common_callers::endpoint::root, + register_caller::register_user, + login_endpoints::login, login_endpoints::service_login, login_endpoints::refresh_token + ), + components(schemas(common_callers::response::TestResult, + register_responses::Response, + login_responses::Response, login_responses::service_login::Response, login_responses::refresh_token::Response)), + tags( + (name = "Icarus Auth API", description = "Auth API for Icarus API") + ) + )] + struct ApiDoc; pub async fn routes() -> Router { // build our application with a route @@ -58,7 +81,13 @@ mod init { icarus_auth::db::migrations(&pool).await; - routes().await.layer(axum::Extension(pool)) + routes() + .await + .merge( + utoipa_swagger_ui::SwaggerUi::new("/swagger-ui") + .url("/api-docs/openapi.json", ApiDoc::openapi()), + ) + .layer(axum::Extension(pool)) } }