From 98f37302a6c9641c04beacaa92356c320cfc0942 Mon Sep 17 00:00:00 2001 From: timokoesters Date: Thu, 30 Jul 2020 22:09:11 +0200 Subject: [PATCH] feat: handle /publicRooms pagination --- src/client_server/directory.rs | 173 +++++++++++++++++++++------------ 1 file changed, 110 insertions(+), 63 deletions(-) diff --git a/src/client_server/directory.rs b/src/client_server/directory.rs index 510511c5..9bed45b6 100644 --- a/src/client_server/directory.rs +++ b/src/client_server/directory.rs @@ -1,12 +1,15 @@ use super::State; use crate::{ConduitResult, Database, Error, Result, Ruma}; use ruma::{ - api::client::r0::{ - directory::{ - self, get_public_rooms, get_public_rooms_filtered, get_room_visibility, - set_room_visibility, + api::client::{ + error::ErrorKind, + r0::{ + directory::{ + self, get_public_rooms, get_public_rooms_filtered, get_room_visibility, + set_room_visibility, + }, + room, }, - room, }, events::{ room::{avatar, canonical_alias, guest_access, history_visibility, name, topic}, @@ -20,65 +23,39 @@ use rocket::{get, post, put}; #[cfg_attr( feature = "conduit_bin", - get("/_matrix/client/r0/publicRooms", data = "") + post("/_matrix/client/r0/publicRooms", data = "") )] -pub async fn get_public_rooms_route( +pub async fn get_public_rooms_filtered_route( db: State<'_, Database>, - body: Ruma, -) -> ConduitResult { - let Ruma { - body: - get_public_rooms::Request { - limit, - server, - since, - }, - sender_id, - device_id, - json_body, - } = body; + body: Ruma, +) -> ConduitResult { + let limit = body.limit.map_or(10, u64::from); + let mut since = 0_u64; - let get_public_rooms_filtered::Response { - chunk, - prev_batch, - next_batch, - total_room_count_estimate, - } = get_public_rooms_filtered_route( - db, - Ruma { - body: get_public_rooms_filtered::Request { - filter: None, - limit, - room_network: get_public_rooms_filtered::RoomNetwork::Matrix, - server, - since, - }, - sender_id, - device_id, - json_body, - }, - ) - .await? - .0; + if let Some(s) = &body.since { + let mut characters = s.chars(); + let backwards = match characters.next() { + Some('n') => false, + Some('p') => true, + _ => { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Invalid `since` token", + )) + } + }; - Ok(get_public_rooms::Response { - chunk, - prev_batch, - next_batch, - total_room_count_estimate, + since = characters + .collect::() + .parse() + .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `since` token."))?; + + if backwards { + since = since.saturating_sub(limit); + } } - .into()) -} -#[cfg_attr( - feature = "conduit_bin", - post("/_matrix/client/r0/publicRooms", data = "<_body>") -)] -pub async fn get_public_rooms_filtered_route( - db: State<'_, Database>, - _body: Ruma, -) -> ConduitResult { - let mut chunk = + let mut all_rooms = db.rooms .public_rooms() .map(|room_id| { @@ -190,10 +167,10 @@ pub async fn get_public_rooms_filtered_route( // We need to collect all, so we can sort by member count .collect::>(); - chunk.sort_by(|l, r| r.num_joined_members.cmp(&l.num_joined_members)); + all_rooms.sort_by(|l, r| r.num_joined_members.cmp(&l.num_joined_members)); /* - chunk.extend_from_slice( + all_rooms.extend_from_slice( &server_server::send_request( &db, "privacytools.io".to_owned(), @@ -212,17 +189,87 @@ pub async fn get_public_rooms_filtered_route( ); */ - let total_room_count_estimate = (chunk.len() as u32).into(); + let total_room_count_estimate = (all_rooms.len() as u32).into(); + + let chunk = all_rooms + .into_iter() + .skip(since as usize) + .take(limit as usize) + .collect::>(); + + let prev_batch = if since == 0 { + None + } else { + Some(format!("p{}", since)) + }; + + let next_batch = if chunk.len() < limit as usize { + None + } else { + Some(format!("n{}", since + limit)) + }; Ok(get_public_rooms_filtered::Response { chunk, - prev_batch: None, - next_batch: None, + prev_batch, + next_batch, total_room_count_estimate: Some(total_room_count_estimate), } .into()) } +#[cfg_attr( + feature = "conduit_bin", + get("/_matrix/client/r0/publicRooms", data = "") +)] +pub async fn get_public_rooms_route( + db: State<'_, Database>, + body: Ruma, +) -> ConduitResult { + let Ruma { + body: + get_public_rooms::Request { + limit, + server, + since, + }, + sender_id, + device_id, + json_body, + } = body; + + let get_public_rooms_filtered::Response { + chunk, + prev_batch, + next_batch, + total_room_count_estimate, + } = get_public_rooms_filtered_route( + db, + Ruma { + body: get_public_rooms_filtered::Request { + filter: None, + limit, + room_network: get_public_rooms_filtered::RoomNetwork::Matrix, + server, + since, + }, + sender_id, + device_id, + json_body, + }, + ) + .await? + .0; + + Ok(get_public_rooms::Response { + chunk, + prev_batch, + next_batch, + total_room_count_estimate, + } + .into()) +} + #[cfg_attr( feature = "conduit_bin", put("/_matrix/client/r0/directory/list/room/<_>", data = "")