diff --git a/Cargo.lock b/Cargo.lock index a5355d6..8b6ca2a 100644 Binary files a/Cargo.lock and b/Cargo.lock differ diff --git a/emgauwa-core/Cargo.toml b/emgauwa-core/Cargo.toml index 6400d42..1bb4ef4 100644 --- a/emgauwa-core/Cargo.toml +++ b/emgauwa-core/Cargo.toml @@ -12,6 +12,9 @@ actix-web = "4.4" actix-web-actors = "4.2" actix-cors = "0.7" +utoipa = "4.2" +utoipa-swagger-ui = { version = "6.0", features = ["actix-web", "debug-embed"] } + log = "0.4" chrono = { version = "0.4", features = ["serde"] } diff --git a/emgauwa-core/build.rs b/emgauwa-core/build.rs index 41015be..443df4c 100644 --- a/emgauwa-core/build.rs +++ b/emgauwa-core/build.rs @@ -1,3 +1,17 @@ +use std::process::{exit, Command}; + fn main() { - println!("cargo:rustc-env=DATABASE_URL=sqlite://emgauwa-core.sqlite") + println!("cargo:rustc-env=DATABASE_URL=sqlite://emgauwa-core.sqlite"); + + println!("cargo:rerun-if-changed=../api.v1.yaml"); + let output = Command::new("sh") + .arg("-c") + .arg("yq . < ../api.v1.yaml > $OUT_DIR/api.v1.json") + .output() + .expect("Failed to convert api documentation to json"); + + if !output.status.success() { + eprintln!("Error: {}", String::from_utf8_lossy(&output.stderr)); + exit(1); + } } diff --git a/emgauwa-core/src/handlers/v1/controllers.rs b/emgauwa-core/src/handlers/v1/controllers.rs index 1fdd6cd..eb1b870 100644 --- a/emgauwa-core/src/handlers/v1/controllers.rs +++ b/emgauwa-core/src/handlers/v1/controllers.rs @@ -9,7 +9,7 @@ use sqlx::{Pool, Sqlite}; use crate::app_state; use crate::app_state::AppState; -#[get("/api/v1/controllers")] +#[get("/controllers")] pub async fn index(pool: web::Data>) -> Result { let mut pool_conn = pool.acquire().await?; @@ -20,7 +20,7 @@ pub async fn index(pool: web::Data>) -> Result>, path: web::Path<(String,)>, @@ -38,7 +38,7 @@ pub async fn show( Ok(HttpResponse::Ok().json(return_controller)) } -#[put("/api/v1/controllers/{controller_id}")] +#[put("/controllers/{controller_id}")] pub async fn update( pool: web::Data>, app_state: web::Data>, @@ -70,7 +70,7 @@ pub async fn update( Ok(HttpResponse::Ok().json(return_controller)) } -#[delete("/api/v1/controllers/{controller_id}")] +#[delete("/controllers/{controller_id}")] pub async fn delete( pool: web::Data>, app_state: web::Data>, diff --git a/emgauwa-core/src/handlers/v1/relays.rs b/emgauwa-core/src/handlers/v1/relays.rs index 6f2ddfa..2d519d4 100644 --- a/emgauwa-core/src/handlers/v1/relays.rs +++ b/emgauwa-core/src/handlers/v1/relays.rs @@ -10,7 +10,7 @@ use sqlx::{Pool, Sqlite}; use crate::app_state; use crate::app_state::AppState; -#[get("/api/v1/relays")] +#[get("/relays")] pub async fn index(pool: web::Data>) -> Result { let mut pool_conn = pool.acquire().await?; @@ -21,7 +21,7 @@ pub async fn index(pool: web::Data>) -> Result>, path: web::Path<(String,)>, @@ -39,7 +39,7 @@ pub async fn tagged( Ok(HttpResponse::Ok().json(relays)) } -#[get("/api/v1/controllers/{controller_id}/relays")] +#[get("/controllers/{controller_id}/relays")] pub async fn index_for_controller( pool: web::Data>, path: web::Path<(String,)>, @@ -59,7 +59,7 @@ pub async fn index_for_controller( Ok(HttpResponse::Ok().json(relays)) } -#[get("/api/v1/controllers/{controller_id}/relays/{relay_num}")] +#[get("/controllers/{controller_id}/relays/{relay_num}")] pub async fn show_for_controller( pool: web::Data>, path: web::Path<(String, i64)>, @@ -81,7 +81,7 @@ pub async fn show_for_controller( Ok(HttpResponse::Ok().json(return_relay)) } -#[put("/api/v1/controllers/{controller_id}/relays/{relay_num}")] +#[put("/controllers/{controller_id}/relays/{relay_num}")] pub async fn update_for_controller( pool: web::Data>, app_state: web::Data>, diff --git a/emgauwa-core/src/handlers/v1/schedules.rs b/emgauwa-core/src/handlers/v1/schedules.rs index a1d1583..1130e6b 100644 --- a/emgauwa-core/src/handlers/v1/schedules.rs +++ b/emgauwa-core/src/handlers/v1/schedules.rs @@ -6,7 +6,7 @@ use emgauwa_lib::types::{RequestCreateSchedule, RequestUpdateSchedule, ScheduleU use sqlx::pool::PoolConnection; use sqlx::{Pool, Sqlite}; -#[get("/api/v1/schedules")] +#[get("/schedules")] pub async fn index(pool: web::Data>) -> Result { let mut pool_conn = pool.acquire().await?; @@ -16,7 +16,7 @@ pub async fn index(pool: web::Data>) -> Result>, path: web::Path<(String,)>, @@ -34,7 +34,7 @@ pub async fn tagged( Ok(HttpResponse::Ok().json(schedules)) } -#[get("/api/v1/schedules/{schedule_id}")] +#[get("/schedules/{schedule_id}")] pub async fn show( pool: web::Data>, path: web::Path<(String,)>, @@ -52,7 +52,7 @@ pub async fn show( Ok(HttpResponse::Ok().json(return_schedule)) } -#[post("/api/v1/schedules")] +#[post("/schedules")] pub async fn add( pool: web::Data>, data: web::Json, @@ -96,7 +96,7 @@ async fn add_list_single( Ok(new_schedule) } -#[post("/api/v1/schedules/list")] +#[post("/schedules/list")] pub async fn add_list( pool: web::Data>, data: web::Json>, @@ -113,7 +113,7 @@ pub async fn add_list( Ok(HttpResponse::Created().json(schedules)) } -#[put("/api/v1/schedules/{schedule_id}")] +#[put("/schedules/{schedule_id}")] pub async fn update( pool: web::Data>, path: web::Path<(String,)>, @@ -148,7 +148,7 @@ pub async fn update( Ok(HttpResponse::Ok().json(return_schedule)) } -#[delete("/api/v1/schedules/{schedule_id}")] +#[delete("/schedules/{schedule_id}")] pub async fn delete( pool: web::Data>, path: web::Path<(String,)>, diff --git a/emgauwa-core/src/handlers/v1/tags.rs b/emgauwa-core/src/handlers/v1/tags.rs index 851bf98..5a42715 100644 --- a/emgauwa-core/src/handlers/v1/tags.rs +++ b/emgauwa-core/src/handlers/v1/tags.rs @@ -5,7 +5,7 @@ use emgauwa_lib::models::{FromDbModel, Tag}; use emgauwa_lib::types::RequestCreateTag; use sqlx::{Pool, Sqlite}; -#[get("/api/v1/tags")] +#[get("/tags")] pub async fn index(pool: web::Data>) -> Result { let mut pool_conn = pool.acquire().await?; @@ -16,7 +16,7 @@ pub async fn index(pool: web::Data>) -> Result>, path: web::Path<(String,)>, @@ -33,7 +33,7 @@ pub async fn show( Ok(HttpResponse::Ok().json(return_tag)) } -#[delete("/api/v1/tags/{tag_name}")] +#[delete("/tags/{tag_name}")] pub async fn delete( pool: web::Data>, path: web::Path<(String,)>, @@ -46,7 +46,7 @@ pub async fn delete( Ok(HttpResponse::Ok().json("tag got deleted")) } -#[post("/api/v1/tags")] +#[post("/tags")] pub async fn add( pool: web::Data>, data: web::Json, diff --git a/emgauwa-core/src/handlers/v1/ws/mod.rs b/emgauwa-core/src/handlers/v1/ws/mod.rs index 23a545a..cda5a47 100644 --- a/emgauwa-core/src/handlers/v1/ws/mod.rs +++ b/emgauwa-core/src/handlers/v1/ws/mod.rs @@ -11,7 +11,7 @@ use crate::handlers::v1::ws::controllers::ControllerWs; pub mod controllers; -#[get("/api/v1/ws/controllers")] +#[get("/ws/controllers")] pub async fn ws_controllers( pool: web::Data>, app_state: web::Data>, diff --git a/emgauwa-core/src/main.rs b/emgauwa-core/src/main.rs index 1191da6..6bd47a3 100644 --- a/emgauwa-core/src/main.rs +++ b/emgauwa-core/src/main.rs @@ -7,6 +7,8 @@ use actix_web::{middleware, web, App, HttpServer}; use emgauwa_lib::db::DbController; use emgauwa_lib::errors::EmgauwaError; use emgauwa_lib::utils::{drop_privileges, init_logging}; +use serde_json::json; +use utoipa_swagger_ui::SwaggerUi; use crate::app_state::AppState; @@ -61,34 +63,52 @@ async fn main() -> Result<(), std::io::Error> { }), }; + let api_default = json!({ + "openapi": "3.0.0", + "info": { + "version": "0.0.0", + "title": "Failed to load API documentation", + } + }); + let api_v1_json = + serde_json::from_str(include_str!(concat!(env!("OUT_DIR"), "/api.v1.json"))) + .unwrap_or(api_default.clone()); + App::new() .wrap(cors) .wrap(middleware::Logger::default()) - .wrap(middleware::NormalizePath::new(TrailingSlash::Trim)) .app_data(web::JsonConfig::default().error_handler(handlers::json_error_handler)) .app_data(web::Data::new(pool.clone())) .app_data(web::Data::new(app_state.clone())) - .service(handlers::v1::controllers::index) - .service(handlers::v1::controllers::show) - .service(handlers::v1::controllers::update) - .service(handlers::v1::controllers::delete) - .service(handlers::v1::relays::index) - .service(handlers::v1::relays::tagged) - .service(handlers::v1::relays::index_for_controller) - .service(handlers::v1::relays::show_for_controller) - .service(handlers::v1::relays::update_for_controller) - .service(handlers::v1::schedules::index) - .service(handlers::v1::schedules::tagged) - .service(handlers::v1::schedules::show) - .service(handlers::v1::schedules::add) - .service(handlers::v1::schedules::add_list) - .service(handlers::v1::schedules::update) - .service(handlers::v1::schedules::delete) - .service(handlers::v1::tags::index) - .service(handlers::v1::tags::show) - .service(handlers::v1::tags::delete) - .service(handlers::v1::tags::add) - .service(handlers::v1::ws::ws_controllers) + .service( + SwaggerUi::new("/api/docs/{_:.*}") + .external_urls_from_iter_unchecked([("/api/v1.json", api_v1_json)]), + ) + .service( + web::scope("/api/v1") + .wrap(middleware::NormalizePath::new(TrailingSlash::Trim)) + .service(handlers::v1::controllers::index) + .service(handlers::v1::controllers::show) + .service(handlers::v1::controllers::update) + .service(handlers::v1::controllers::delete) + .service(handlers::v1::relays::index) + .service(handlers::v1::relays::tagged) + .service(handlers::v1::relays::index_for_controller) + .service(handlers::v1::relays::show_for_controller) + .service(handlers::v1::relays::update_for_controller) + .service(handlers::v1::schedules::index) + .service(handlers::v1::schedules::tagged) + .service(handlers::v1::schedules::show) + .service(handlers::v1::schedules::add) + .service(handlers::v1::schedules::add_list) + .service(handlers::v1::schedules::update) + .service(handlers::v1::schedules::delete) + .service(handlers::v1::tags::index) + .service(handlers::v1::tags::show) + .service(handlers::v1::tags::delete) + .service(handlers::v1::tags::add) + .service(handlers::v1::ws::ws_controllers), + ) }) .listen(listener)? .run()