Add helper function for creating responses with pagination header
This commit is contained in:
parent
acb139b0ee
commit
53ace3bf57
3 changed files with 78 additions and 43 deletions
|
@ -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";
|
||||||
|
|
|
@ -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""#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
60
src/mastodon_api/pagination.rs
Normal file
60
src/mastodon_api/pagination.rs
Normal 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""#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue