diff --git a/.gitignore b/.gitignore index ea8c4bf..2c4918c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +*.db diff --git a/migrations/20251114144629_db.down.sql b/migrations/20251114144629_db.down.sql index a5ec4a2..4c79bf2 100644 --- a/migrations/20251114144629_db.down.sql +++ b/migrations/20251114144629_db.down.sql @@ -1,3 +1 @@ --- Add migration script here - drop table "patients"; diff --git a/migrations/20251114144629_db.up.sql b/migrations/20251114144629_db.up.sql index dbd71c9..225cc32 100644 --- a/migrations/20251114144629_db.up.sql +++ b/migrations/20251114144629_db.up.sql @@ -1,5 +1,3 @@ --- Add migration script here - create table "patients" ( "id" INTEGER PRIMARY KEY, "full_name" TEXT NOT NULL, diff --git a/mydb.db b/mydb.db deleted file mode 100644 index 9374e0b..0000000 Binary files a/mydb.db and /dev/null differ diff --git a/queries/create_patient.sql b/queries/create_patient.sql index f4fd8e0..5a19220 100644 --- a/queries/create_patient.sql +++ b/queries/create_patient.sql @@ -1,3 +1,9 @@ -insert into patients ("full_name", "phone") -values($1,$2) -returning "id"; +insert into patients( + "full_name", + "phone" +) +values($1, $2) +returning + "id", + "full_name" as "full_name!", + "phone" as "phone!" diff --git a/src/lib.rs b/src/lib.rs index 5947e5b..3717039 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,11 @@ use axum::{ - debug_handler, + Router, debug_handler, extract::{Json, Path, State}, http::StatusCode, response::IntoResponse, }; use models::Patient; -use sqlx::{query_file, query_file_as, query_file_scalar}; -use tracing::info; +use sqlx::{query_file, query_file_as}; use utoipa::OpenApi; use utoipa_axum::{router::OpenApiRouter, routes}; @@ -24,26 +23,25 @@ pub mod schemas; #[derive(OpenApi)] struct Api; -pub async fn router(state: AppState) { - let addr = "0.0.0.0:3000"; - tracing_subscriber::fmt::init(); +pub fn router(state: AppState) -> Router { let (router, docs) = OpenApiRouter::with_openapi(Api::openapi()) - .routes(routes!(get_openapi)) - .routes(routes!(create_patient)) - .routes(routes!(get_all)) - .routes(routes!(update_patient_by_id)) - .routes(routes!(delete_patient_by_id)) - .routes(routes!(get_patient_by_id)) + .nest( + "/api/patients/", + OpenApiRouter::new() + .routes(routes!(create_patient, get_all)) + .routes(routes!( + update_patient_by_id, + delete_patient_by_id, + get_patient_by_id + )), + ) .with_state(state) .split_for_parts(); - let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); - info!("Listening in {:?}", listener.local_addr()); - let router = route_docs(router, docs); - axum::serve(listener, router).await.unwrap(); + route_docs(router, docs) } #[debug_handler] -#[utoipa::path(get, path = "/api/patients", description="Get all patients", responses((status = OK, body = Patient)))] +#[utoipa::path(get, path = "/", description="Get all patients", responses((status = OK, body = Patient)))] async fn get_all(State(state): State) -> Json> { let patients = query_file_as!(Patient, "queries/get_all.sql") .fetch_all(&state.pool) @@ -53,7 +51,7 @@ async fn get_all(State(state): State) -> Json> { } #[debug_handler] -#[utoipa::path(get, path = "/api/patients/{id}", description="Get all patients", responses((status = OK, body = Patient)))] +#[utoipa::path(get, path = "/{id}", description="Get all patients", responses((status = OK, body = Patient)))] async fn get_patient_by_id( State(state): State, Path(path): Path, @@ -69,18 +67,13 @@ async fn get_patient_by_id( } #[debug_handler] -#[utoipa::path(get, path = "/api/openapi.json", description = "Get openapi.json file", responses((status = OK)))] -async fn get_openapi() -> impl IntoResponse { - axum::http::StatusCode::NOT_IMPLEMENTED -} - -#[debug_handler] -#[utoipa::path(post, path = "/api/patients", description = "Create a new patient", responses((status = OK)))] +#[utoipa::path(post, path = "/", description = "Create a new patient", responses((status = OK)))] async fn create_patient( State(state): State, Json(patient): Json, -) -> Json { - let new_patient_id = query_file_scalar!( +) -> Json { + let patient = query_file_as!( + Patient, "queries/create_patient.sql", patient.full_name, patient.phone @@ -88,11 +81,12 @@ async fn create_patient( .fetch_one(&state.pool) .await .expect("Could not create new patient"); - Json(new_patient_id) + + Json(patient) } #[debug_handler] -#[utoipa::path(post, path = "/api/patients/{id}", description = "Update patient by ID", responses((status = OK)))] +#[utoipa::path(post, path = "/{id}", description = "Update patient by ID", responses((status = OK)))] async fn update_patient_by_id( State(state): State, Path(path): Path, @@ -111,7 +105,7 @@ async fn update_patient_by_id( } #[debug_handler] -#[utoipa::path(delete, path = "/api/patients/{id}", description = "Delete patient by ID", responses((status = OK)))] +#[utoipa::path(delete, path = "/{id}", description = "Delete patient by ID", responses((status = OK)))] async fn delete_patient_by_id( State(state): State, Path(path): Path, diff --git a/src/main.rs b/src/main.rs index f2c1e86..b3f9bc0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,25 @@ use hospital_server::app_state::AppState; use hospital_server::router; use sqlx::SqlitePool; +use tracing::info; #[tokio::main] async fn main() { let _ = dotenvy::dotenv(); + tracing_subscriber::fmt::init(); let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL is not set"); let pool = SqlitePool::connect(&db_url) .await .expect("Cannot connect to db"); - let state = AppState { pool: pool }; - sqlx::migrate!().run(&state.pool).await.unwrap(); - router(state).await; + sqlx::migrate!().run(&pool).await.unwrap(); + + let state = AppState { pool }; + let router = router(state); + + let addr = "0.0.0.0:3000"; + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + info!("Listening in {:?}", listener.local_addr()); + + axum::serve(listener, router).await.unwrap(); } diff --git a/src/openapi.rs b/src/openapi.rs index f064e9a..a918dd5 100644 --- a/src/openapi.rs +++ b/src/openapi.rs @@ -1,9 +1,9 @@ -use axum::Router; +use axum::{Router, routing::get}; use utoipa::openapi::OpenApi; use utoipa_scalar::{Scalar, Servable}; -struct ApiDocs; - pub fn route_docs(router: Router, docs: OpenApi) -> Router { - router.merge(Scalar::with_url("/", docs)) + router + .merge(Scalar::with_url("/", docs.clone())) + .route("/docs/openapi.json", get(docs.to_json().expect("serializing OpenAPI"))) }