Skip to content

Commit

Permalink
feat: add swagger ui for both axum and actix (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xBoji committed Oct 7, 2024
1 parent 2643468 commit cb5726e
Show file tree
Hide file tree
Showing 8 changed files with 373 additions and 4 deletions.
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,10 @@ edition = "2021"
[dependencies]
rayon = "1.5"
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
actix-web = "4"
serde = { version = "1.0", features = ["derive"] }
axum = "0.7.7"
# # utoipa = { version = "4.2.3", features = ["actix_extras"] }
# # utoipa-swagger-ui = { version = "7.1.0", features = ["actix-web"] }
utoipa = { version = "4.2.3", features = ["axum_extras"] }
utoipa-swagger-ui = { version = "7.1.0", features = ["axum"] }
6 changes: 4 additions & 2 deletions src/day16_to_day18/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mod web_development;
mod web_development_actix;
mod web_development_axum;

pub fn run() {
println!("Days 16-18: Web Development");
let _ = web_development::main();
// let _ = web_development_actix::main();
let _ = web_development_axum::main();
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct UserFilter {
/// * `query` - Query parameters for filtering users
///
/// Returns a JSON array of filtered users in the system
#[allow(dead_code)]
async fn get_users(query: web::Query<UserFilter>) -> impl Responder {
let users = vec![
User { id: 1, name: "Alice".to_string() },
Expand All @@ -45,6 +46,7 @@ async fn get_users(query: web::Query<UserFilter>) -> impl Responder {
/// * `path` - A Path extractor containing the user ID
///
/// Returns a JSON object of the requested user or a 404 if not found
#[allow(dead_code)]
async fn get_user(path: web::Path<u32>) -> impl Responder {
let user_id = path.into_inner();
// In a real application, we would fetch the user from a database
Expand All @@ -59,6 +61,7 @@ async fn get_user(path: web::Path<u32>) -> impl Responder {
/// * `user` - JSON payload representing the new user
///
/// Returns the created user with a 201 status code
#[allow(dead_code)]
async fn create_user(user: web::Json<User>) -> impl Responder {
// In a real application, we would save the user to a database
HttpResponse::Created().json(user.into_inner())
Expand Down
88 changes: 88 additions & 0 deletions src/day16_to_day18/web_development_axum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use axum::{
routing::get,
Router, Json, extract::{Query, Path},
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use tokio::net::TcpListener;

/// Represents a user in the system
#[derive(Serialize, Deserialize)]
struct User {
id: u32,
name: String,
}

/// Represents query parameters for filtering users
#[derive(Deserialize)]
struct UserFilter {
name: Option<String>,
}

/// Handles GET request to retrieve all users
///
/// # Arguments
///
/// * `query` - Query parameters for filtering users
///
/// Returns a JSON array of filtered users in the system
async fn get_users(Query(query): Query<UserFilter>) -> Json<Vec<User>> {
let users = vec![
User { id: 1, name: "Alice".to_string() },
User { id: 2, name: "Bob".to_string() },
User { id: 3, name: "Charlie".to_string() },
User { id: 4, name: "Bobby Pickle".to_string() },
];

let filtered_users: Vec<User> = users.into_iter()
.filter(|user| {
query.name.as_ref().map_or(true, |name| user.name.to_lowercase().contains(&name.to_lowercase()))
})
.collect();

Json(filtered_users)
}

/// Handles GET request to retrieve a specific user by ID
///
/// # Arguments
///
/// * `path` - A Path extractor containing the user ID
///
/// Returns a JSON object of the requested user
async fn get_user(Path(user_id): Path<u32>) -> Json<User> {
// In a real application, we would fetch the user from a database
let user = User { id: user_id, name: format!("User {}", user_id) };
Json(user)
}

/// Handles POST request to create a new user
///
/// # Arguments
///
/// * `user` - JSON payload representing the new user
///
/// Returns the created user
async fn create_user(Json(user): Json<User>) -> Json<User> {
// In a real application, we would save the user to a database
Json(user)
}

/// Starts the web server and configures the routes
#[tokio::main]
pub async fn main() {
const HOST: &str = "127.0.0.1";
const PORT: u16 = 8080;
// Run to get all users http://127.0.0.1:8080/api/v1/users
// Run to get user with id 1 http://127.0.0.1:8080/api/v1/users/1
// Run to get filter user http://127.0.0.1:8080/api/v1/users?name=Alice
println!("Starting web server at http://{}:{}", HOST, PORT);

let app = Router::new()
.route("/api/v1/users", get(get_users).post(create_user))
.route("/api/v1/users/:id", get(get_user));

let addr = SocketAddr::from(([127, 0, 0, 1], PORT));
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
5 changes: 5 additions & 0 deletions src/day19_to_day21/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
mod web_development_actix_swaggerui;
mod web_development_axum_swaggerui;

pub fn run() {
println!("Days 19-21: Web Development with Swagger UI");
// let _ = web_development_actix_swaggerui::main();
let _ = web_development_axum_swaggerui::main();
}
134 changes: 134 additions & 0 deletions src/day19_to_day21/web_development_actix_swaggerui.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// use actix_web::{web, App, HttpResponse, HttpServer, Responder};
// use serde::{Deserialize, Serialize};
// use utoipa::OpenApi;
// use utoipa_swagger_ui::SwaggerUi;

// /// Represents a user in the system
// #[derive(Serialize, Deserialize, utoipa::ToSchema)]
// struct User {
// id: u32,
// name: String,
// }

// /// Represents query parameters for filtering users
// #[derive(Deserialize, utoipa::IntoParams, utoipa::ToSchema)]
// struct UserFilter {
// name: Option<String>,
// }

// /// Handles GET request to retrieve all users
// ///
// /// # Arguments
// ///
// /// * `query` - Query parameters for filtering users
// ///
// /// Returns a JSON array of filtered users in the system
// #[utoipa::path(
// get,
// path = "/api/v1/users",
// params(UserFilter),
// responses(
// (status = 200, description = "List of users", body = Vec<User>)
// )
// )]
// #[allow(dead_code)]
// async fn get_users(query: web::Query<UserFilter>) -> impl Responder {
// let users = vec![
// User { id: 1, name: "Alice".to_string() },
// User { id: 2, name: "Bob".to_string() },
// User { id: 3, name: "Charlie".to_string() },
// User { id: 4, name: "Bobby Pickle".to_string() },
// ];

// let filtered_users: Vec<User> = users.into_iter()
// .filter(|user| {
// query.name.as_ref().map_or(true, |name| user.name.to_lowercase().contains(&name.to_lowercase()))
// })
// .collect();

// HttpResponse::Ok().json(filtered_users)
// }

// /// Handles GET request to retrieve a specific user by ID
// ///
// /// # Arguments
// ///
// /// * `path` - A Path extractor containing the user ID
// ///
// /// Returns a JSON object of the requested user or a 404 if not found
// #[utoipa::path(
// get,
// path = "/api/v1/users/{id}",
// params(
// ("id" = u32, Path, description = "User ID")
// ),
// responses(
// (status = 200, description = "User found", body = User),
// (status = 404, description = "User not found")
// )
// )]
// #[allow(dead_code)]
// async fn get_user(path: web::Path<u32>) -> impl Responder {
// let user_id = path.into_inner();
// // In a real application, we would fetch the user from a database
// let user = User { id: user_id, name: format!("User {}", user_id) };
// HttpResponse::Ok().json(user)
// }

// /// Handles POST request to create a new user
// ///
// /// # Arguments
// ///
// /// * `user` - JSON payload representing the new user
// ///
// /// Returns the created user with a 201 status code
// #[utoipa::path(
// post,
// path = "/api/v1/users",
// request_body = User,
// responses(
// (status = 201, description = "User created", body = User)
// )
// )]
// #[allow(dead_code)]
// async fn create_user(user: web::Json<User>) -> impl Responder {
// // In a real application, we would save the user to a database
// HttpResponse::Created().json(user.into_inner())
// }

// #[derive(OpenApi)]
// #[openapi(
// paths(get_users, get_user, create_user),
// components(schemas(User, UserFilter))
// )]
// struct ApiDoc;

// /// Starts the web server and configures the routes
// #[actix_web::main]
// pub async fn main() -> std::io::Result<()> {
// const HOST: &str = "127.0.0.1";
// const PORT: u16 = 8080;
// // Run to get all users http://127.0.0.1:8080/api/v1/users
// // Run to get user with id 1 http://127.0.0.1:8080/api/v1/users/1
// // Run to get filter user http://127.0.0.1:8080/api/v1/users
// // Access Swagger UI at http://127.0.0.1:8080/swagger-ui/
// println!("Starting web server at http://{}:{}", HOST, PORT);
// println!("Access Swagger UI at http://{}:{}/swagger-ui/", HOST, PORT);

// HttpServer::new(|| {
// App::new()
// .service(
// web::scope("/api/v1")
// .route("/users", web::get().to(get_users))
// .route("/users/{id}", web::get().to(get_user))
// .route("/users", web::post().to(create_user))
// )
// .service(
// SwaggerUi::new("/swagger-ui/{_:.*}")
// .url("/api-docs/openapi.json", ApiDoc::openapi())
// )
// })
// .bind((HOST, PORT))?
// .run()
// .await
// }
Loading

0 comments on commit cb5726e

Please sign in to comment.