summaryrefslogtreecommitdiff
path: root/src/handlers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/handlers.rs')
-rw-r--r--src/handlers.rs92
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)
+}