From 53ace3bf57a92da9619f23b896fa6916ec57dc63 Mon Sep 17 00:00:00 2001 From: silverpill Date: Sat, 6 Aug 2022 16:17:38 +0000 Subject: [PATCH] Add helper function for creating responses with pagination header --- src/mastodon_api/mod.rs | 3 +- src/mastodon_api/notifications/views.rs | 58 +++++++----------------- src/mastodon_api/pagination.rs | 60 +++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 43 deletions(-) create mode 100644 src/mastodon_api/pagination.rs diff --git a/src/mastodon_api/mod.rs b/src/mastodon_api/mod.rs index 9ef8bbf..dd3a38b 100644 --- a/src/mastodon_api/mod.rs +++ b/src/mastodon_api/mod.rs @@ -5,8 +5,9 @@ pub mod markers; pub mod media; pub mod notifications; pub mod oauth; +mod pagination; pub mod search; pub mod statuses; pub mod timelines; -pub const MASTODON_API_VERSION: &str = "3.0.0"; +const MASTODON_API_VERSION: &str = "3.0.0"; diff --git a/src/mastodon_api/notifications/views.rs b/src/mastodon_api/notifications/views.rs index a9a5947..2b374a6 100644 --- a/src/mastodon_api/notifications/views.rs +++ b/src/mastodon_api/notifications/views.rs @@ -1,33 +1,26 @@ /// https://docs.joinmastodon.org/methods/notifications/ -use actix_web::{get, web, HttpResponse, Scope as ActixScope}; +use actix_web::{ + get, web, + HttpRequest, HttpResponse, + Scope as ActixScope, +}; use actix_web_httpauth::extractors::bearer::BearerAuth; use crate::config::Config; use crate::database::{Pool, get_database_client}; use crate::errors::HttpError; use crate::mastodon_api::oauth::auth::get_current_user; +use crate::mastodon_api::pagination::get_paginated_response; use crate::models::notifications::queries::get_notifications; use super::types::{ApiNotification, NotificationQueryParams}; -fn get_pagination_header( - instance_url: &str, - last_id: &str, -) -> String { - let next_page_url = format!( - "{}/api/v1/notifications?max_id={}", - instance_url, - last_id - ); - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link - format!(r#"<{}>; rel="next""#, next_page_url) -} - #[get("")] async fn get_notifications_view( auth: BearerAuth, config: web::Data, db_pool: web::Data, query_params: web::Query, + request: HttpRequest, ) -> Result { let db_client = &**get_database_client(&db_pool).await?; let current_user = get_current_user(db_client, auth.token()).await?; @@ -40,18 +33,15 @@ async fn get_notifications_view( .into_iter() .map(|item| ApiNotification::from_db(item, &config.instance_url())) .collect(); - let max_index = usize::from(query_params.limit - 1); - let response = if let Some(item) = notifications.get(max_index) { - let pagination_header = get_pagination_header(&config.instance_url(), &item.id); - HttpResponse::Ok() - .append_header(("Link", pagination_header)) - // Link header needs to be exposed - // https://github.com/actix/actix-extras/issues/192 - .append_header(("Access-Control-Expose-Headers", "Link")) - .json(notifications) - } else { - HttpResponse::Ok().json(notifications) - }; + let max_index = usize::from(query_params.limit.saturating_sub(1)); + let maybe_last_id = notifications.get(max_index) + .map(|item| item.id.clone()); + let response = get_paginated_response( + &config.instance_url(), + request.uri().path(), + notifications, + maybe_last_id, + ); Ok(response) } @@ -59,19 +49,3 @@ pub fn notification_api_scope() -> ActixScope { web::scope("/api/v1/notifications") .service(get_notifications_view) } - -#[cfg(test)] -mod tests { - use super::*; - - const INSTANCE_URL: &str = "https://example.org"; - - #[test] - fn test_get_next_page_link() { - let result = get_pagination_header(INSTANCE_URL, "123"); - assert_eq!( - result, - r#"; rel="next""#, - ); - } -} diff --git a/src/mastodon_api/pagination.rs b/src/mastodon_api/pagination.rs new file mode 100644 index 0000000..bb78c3d --- /dev/null +++ b/src/mastodon_api/pagination.rs @@ -0,0 +1,60 @@ +use actix_web::HttpResponse; +use serde::Serialize; + +fn get_pagination_header( + instance_url: &str, + path: &str, + last_id: &str, +) -> String { + let next_page_url = format!( + "{}{}?max_id={}", + instance_url, + path, + last_id + ); + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link + format!(r#"<{}>; rel="next""#, next_page_url) +} + +pub fn get_paginated_response( + instance_url: &str, + path: &str, + items: Vec, + maybe_last_item_id: Option, +) -> HttpResponse { + if let Some(last_item_id) = maybe_last_item_id { + let pagination_header = get_pagination_header( + instance_url, + path, + &last_item_id.to_string(), + ); + HttpResponse::Ok() + .append_header(("Link", pagination_header)) + // Link header needs to be exposed + // https://github.com/actix/actix-extras/issues/192 + .append_header(("Access-Control-Expose-Headers", "Link")) + .json(items) + } else { + HttpResponse::Ok().json(items) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const INSTANCE_URL: &str = "https://example.org"; + + #[test] + fn test_get_next_page_link() { + let result = get_pagination_header( + INSTANCE_URL, + "/api/v1/notifications", + "123", + ); + assert_eq!( + result, + r#"; rel="next""#, + ); + } +}