diff options
| author | batsumaru <> | 2026-10-16 14:29:08 +0900 |
|---|---|---|
| committer | batsumaru <> | 2026-10-16 14:29:08 +0900 |
| commit | 784e52da5f3bef26655c77b210b3b105847e3d9a (patch) | |
| tree | db49dd742a0f4e6ff72e33e8255b16b487da851a /src/handlers.rs | |
| parent | a69e78b7482227f22e7ddbfaa7dc7ee31f29a99b (diff) | |
Add delete and update
Diffstat (limited to 'src/handlers.rs')
| -rw-r--r-- | src/handlers.rs | 92 |
1 files changed, 87 insertions, 5 deletions
diff --git a/src/handlers.rs b/src/handlers.rs index 3f815a5..a49d29a 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -1,5 +1,5 @@ use axum::{ - extract::{Query, State}, + extract::{Path, Query, State}, http::StatusCode, response::Json, }; @@ -7,17 +7,36 @@ use chrono::NaiveTime; use sqlx::PgPool; use uuid::Uuid; -use crate::error::AppError; -use crate::models::{DateQuery, MeasurementData, MeasurementView, Unit}; +use crate::models::{AllQuery, DateQuery, MeasurementData, MeasurementView, Unit}; +use crate::{error::AppError, models::MetricType}; pub async fn index() -> &'static str { "Hello World!" } -pub async fn all(State(pool): State<PgPool>) -> Result<Json<Vec<MeasurementView>>, AppError> { +pub async fn all( + State(pool): State<PgPool>, + Query(options): Query<AllQuery>, +) -> Result<Json<Vec<MeasurementView>>, AppError> { + let limit = match options.limit { + Some(x) if (x > 0) => Some(x), + None => None, + _ => return Err(AppError::BadRequest), + }; + // SQL can't bind ASC/DESC as a parameter. The CASE yields measured_at only when + // `reverse` is true (sorted DESC) and NULL otherwise, so that key ties for every + // row and the ASC key decides the order. If this gets slow, use an if/else with a + // separate ASC and DESC query and add an index on measured_at. let rows = sqlx::query_as!( MeasurementView, - "SELECT id, measured_at, metric_type, value, unit FROM measurement" + " + SELECT id, measured_at, metric_type, value, unit FROM measurement + ORDER BY CASE WHEN $2 THEN measured_at END DESC, + measured_at ASC + LIMIT $1 + ", + limit, + options.reverse.unwrap_or(false) ) .fetch_all(&pool) .await?; @@ -92,3 +111,66 @@ pub async fn units(State(pool): State<PgPool>) -> Result<Json<Vec<Unit>>, AppErr .await?, )) } + +pub async fn metric_types(State(pool): State<PgPool>) -> Result<Json<Vec<MetricType>>, AppError> { + Ok(Json( + sqlx::query_scalar("SELECT enum_range(NULL::metric_type)") + .fetch_one(&pool) + .await?, + )) +} + +pub async fn measurement_by_id( + State(pool): State<PgPool>, + Path(id): Path<Uuid>, +) -> Result<Json<MeasurementView>, AppError> { + let measurement = sqlx::query_as!( + MeasurementView, + " + SELECT id, measured_at, metric_type, value, unit FROM measurement + WHERE id = $1 + ", + id + ) + .fetch_one(&pool) + .await?; + Ok(Json(measurement)) +} + +pub async fn delete( + State(pool): State<PgPool>, + Path(id): Path<Uuid>, +) -> Result<StatusCode, AppError> { + sqlx::query!("DELETE FROM measurement WHERE id = $1", id) + .execute(&pool) + .await?; + Ok(StatusCode::NO_CONTENT) +} + +pub async fn update( + State(pool): State<PgPool>, + Path(id): Path<Uuid>, + Json(data): Json<MeasurementData>, +) -> Result<StatusCode, AppError> { + let result = sqlx::query!( + " + UPDATE measurement + SET measured_at = $1, + metric_type = $2, + value = $3, + unit = $4 + WHERE id = $5 + ", + data.measured_at, + data.metric_type, + data.value, + data.unit, + id + ) + .execute(&pool) + .await?; + if result.rows_affected() == 0 { + return Err(AppError::NotFound); + } + Ok(StatusCode::NO_CONTENT) +} |
