From da445c54449ca5cb0d511af44ba109553fd5f8ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arifin=20Yunianta=20=F0=9F=A7=8A?= <72777947+Yunnie-pin@users.noreply.github.com> Date: Thu, 21 Nov 2024 05:31:29 +0700 Subject: [PATCH] feat(pagination): add paginate function to calculate offset, limit, and total pages in `get_table` handler (#26) * feat(pagination): add paginate function to calculate offset, limit, and total pages in `get_table` handler * move more work into the `Pagination` struct --------- Co-authored-by: Jacob Heider --- api/src/handlers.rs | 28 +++++++++++++--------------- api/src/utils.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/api/src/handlers.rs b/api/src/handlers.rs index fad1928..cf64f3a 100644 --- a/api/src/handlers.rs +++ b/api/src/handlers.rs @@ -5,12 +5,12 @@ use tokio_postgres::error::SqlState; use uuid::Uuid; use crate::app_state::AppState; -use crate::utils::{get_column_names, rows_to_json}; +use crate::utils::{get_column_names, rows_to_json, Pagination}; #[derive(Deserialize)] -struct PaginationParams { - page: Option, - limit: Option, +pub struct PaginationParams { + pub page: Option, + pub limit: Option, } #[derive(Serialize)] @@ -59,29 +59,27 @@ pub async fn get_table( })); } - let page = query.page.unwrap_or(1).max(1); - let limit = query.limit.unwrap_or(200).clamp(1, 1000); - let offset = (page - 1) * limit; - let count_query = format!("SELECT COUNT(*) FROM {}", table); - let data_query = format!("SELECT * FROM {} LIMIT $1 OFFSET $2", table); - match data.pool.get().await { Ok(client) => match client.query_one(&count_query, &[]).await { Ok(count_row) => { let total_count: i64 = count_row.get(0); - let total_pages = (total_count as f64 / limit as f64).ceil() as i64; + let pagination = Pagination::new(query, total_count); - match client.query(&data_query, &[&limit, &offset]).await { + let data_query = format!("SELECT * FROM {} LIMIT $1 OFFSET $2", table); + match client + .query(&data_query, &[&pagination.limit, &pagination.offset]) + .await + { Ok(rows) => { let columns = get_column_names(&rows); let data = rows_to_json(&rows); let response = PaginatedResponse { table, total_count, - page, - limit, - total_pages, + page: pagination.page, + limit: pagination.limit, + total_pages: pagination.total_pages, columns, data, }; diff --git a/api/src/utils.rs b/api/src/utils.rs index d2a5cf2..2c48ac1 100644 --- a/api/src/utils.rs +++ b/api/src/utils.rs @@ -1,8 +1,11 @@ +use actix_web::web::Query; use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc}; use serde_json::{json, Value}; use tokio_postgres::{types::Type, Row}; use uuid::Uuid; +use crate::handlers::PaginationParams; + pub fn get_column_names(rows: &[Row]) -> Vec { if let Some(row) = rows.first() { row.columns() @@ -55,3 +58,27 @@ pub fn rows_to_json(rows: &[Row]) -> Vec { }) .collect() } + +pub struct Pagination { + pub page: i64, + pub limit: i64, + pub offset: i64, + pub total_pages: i64, +} + +impl Pagination { + pub fn new(query: Query, total_count: i64) -> Self { + let limit = query.limit.unwrap_or(200).clamp(1, 1000); + let total_pages = (total_count as f64 / limit as f64).ceil() as i64; + + let page = query.page.unwrap_or(1).clamp(1, total_pages); + + let offset = (page - 1) * limit; + Self { + page, + limit, + offset, + total_pages, + } + } +}