From 784e52da5f3bef26655c77b210b3b105847e3d9a Mon Sep 17 00:00:00 2001 From: batsumaru <> Date: Fri, 16 Oct 2026 14:29:08 +0900 Subject: Add delete and update --- src/handlers.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 87 insertions(+), 5 deletions(-) (limited to 'src/handlers.rs') 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) -> Result>, AppError> { +pub async fn all( + State(pool): State, + Query(options): Query, +) -> Result>, 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) -> Result>, AppErr .await?, )) } + +pub async fn metric_types(State(pool): State) -> Result>, 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, + Path(id): Path, +) -> Result, 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, + Path(id): Path, +) -> Result { + sqlx::query!("DELETE FROM measurement WHERE id = $1", id) + .execute(&pool) + .await?; + Ok(StatusCode::NO_CONTENT) +} + +pub async fn update( + State(pool): State, + Path(id): Path, + Json(data): Json, +) -> Result { + 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) +} -- cgit v1.3