Add helper function for creating responses with pagination header

This commit is contained in:
silverpill 2022-08-06 16:17:38 +00:00
parent acb139b0ee
commit 53ace3bf57
3 changed files with 78 additions and 43 deletions

View file

@ -5,8 +5,9 @@ pub mod markers;
pub mod media; pub mod media;
pub mod notifications; pub mod notifications;
pub mod oauth; pub mod oauth;
mod pagination;
pub mod search; pub mod search;
pub mod statuses; pub mod statuses;
pub mod timelines; pub mod timelines;
pub const MASTODON_API_VERSION: &str = "3.0.0"; const MASTODON_API_VERSION: &str = "3.0.0";

View file

@ -1,33 +1,26 @@
/// https://docs.joinmastodon.org/methods/notifications/ /// 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 actix_web_httpauth::extractors::bearer::BearerAuth;
use crate::config::Config; use crate::config::Config;
use crate::database::{Pool, get_database_client}; use crate::database::{Pool, get_database_client};
use crate::errors::HttpError; use crate::errors::HttpError;
use crate::mastodon_api::oauth::auth::get_current_user; 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 crate::models::notifications::queries::get_notifications;
use super::types::{ApiNotification, NotificationQueryParams}; 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("")] #[get("")]
async fn get_notifications_view( async fn get_notifications_view(
auth: BearerAuth, auth: BearerAuth,
config: web::Data<Config>, config: web::Data<Config>,
db_pool: web::Data<Pool>, db_pool: web::Data<Pool>,
query_params: web::Query<NotificationQueryParams>, query_params: web::Query<NotificationQueryParams>,
request: HttpRequest,
) -> Result<HttpResponse, HttpError> { ) -> Result<HttpResponse, HttpError> {
let db_client = &**get_database_client(&db_pool).await?; let db_client = &**get_database_client(&db_pool).await?;
let current_user = get_current_user(db_client, auth.token()).await?; let current_user = get_current_user(db_client, auth.token()).await?;
@ -40,18 +33,15 @@ async fn get_notifications_view(
.into_iter() .into_iter()
.map(|item| ApiNotification::from_db(item, &config.instance_url())) .map(|item| ApiNotification::from_db(item, &config.instance_url()))
.collect(); .collect();
let max_index = usize::from(query_params.limit - 1); let max_index = usize::from(query_params.limit.saturating_sub(1));
let response = if let Some(item) = notifications.get(max_index) { let maybe_last_id = notifications.get(max_index)
let pagination_header = get_pagination_header(&config.instance_url(), &item.id); .map(|item| item.id.clone());
HttpResponse::Ok() let response = get_paginated_response(
.append_header(("Link", pagination_header)) &config.instance_url(),
// Link header needs to be exposed request.uri().path(),
// https://github.com/actix/actix-extras/issues/192 notifications,
.append_header(("Access-Control-Expose-Headers", "Link")) maybe_last_id,
.json(notifications) );
} else {
HttpResponse::Ok().json(notifications)
};
Ok(response) Ok(response)
} }
@ -59,19 +49,3 @@ pub fn notification_api_scope() -> ActixScope {
web::scope("/api/v1/notifications") web::scope("/api/v1/notifications")
.service(get_notifications_view) .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#"<https://example.org/api/v1/notifications?max_id=123>; rel="next""#,
);
}
}

View file

@ -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<impl Serialize>,
maybe_last_item_id: Option<impl ToString>,
) -> 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#"<https://example.org/api/v1/notifications?max_id=123>; rel="next""#,
);
}
}