1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-29 22:01:14 +03:00

storage: simplify pagination

This commit is contained in:
Quentin Gliech
2023-01-13 18:25:25 +01:00
parent 195203823a
commit 1344527934
7 changed files with 90 additions and 99 deletions

View File

@ -22,7 +22,7 @@ use url::Url;
use uuid::Uuid;
use crate::{
pagination::{process_page, Page, QueryBuilderExt},
pagination::{Page, QueryBuilderExt},
tracing::ExecuteExt,
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
};
@ -379,19 +379,13 @@ impl<'c> CompatSsoLoginRepository for PgCompatSsoLoginRepository<'c> {
.push_bind(Uuid::from(user.id))
.generate_pagination("cl.compat_sso_login_id", before, after, first, last)?;
let page: Vec<CompatSsoLoginLookup> = query
let edges: Vec<CompatSsoLoginLookup> = query
.build_query_as()
.traced()
.fetch_all(&mut *self.conn)
.await?;
let (has_previous_page, has_next_page, edges) = process_page(page, first, last)?;
let edges: Result<Vec<_>, _> = edges.into_iter().map(TryInto::try_into).collect();
Ok(Page {
has_next_page,
has_previous_page,
edges: edges?,
})
let page = Page::process(edges, first, last)?.try_map(CompatSsoLogin::try_from)?;
Ok(page)
}
}

View File

@ -21,7 +21,7 @@ use ulid::Ulid;
use uuid::Uuid;
use crate::{
pagination::{process_page, Page, QueryBuilderExt},
pagination::{Page, QueryBuilderExt},
tracing::ExecuteExt,
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
};
@ -271,15 +271,7 @@ impl<'c> OAuth2SessionRepository for PgOAuth2SessionRepository<'c> {
.fetch_all(&mut *self.conn)
.await?;
let (has_previous_page, has_next_page, edges) = process_page(edges, first, last)?;
let edges: Result<Vec<_>, DatabaseInconsistencyError> =
edges.into_iter().map(Session::try_from).collect();
Ok(Page {
has_next_page,
has_previous_page,
edges: edges?,
})
let page = Page::process(edges, first, last)?.try_map(Session::try_from)?;
Ok(page)
}
}

View File

@ -82,39 +82,69 @@ where
Ok(())
}
pub struct Page<T> {
pub has_next_page: bool,
pub has_previous_page: bool,
pub edges: Vec<T>,
}
impl<T> Page<T> {
/// Process a page returned by a paginated query
pub fn process_page<T>(
mut page: Vec<T>,
pub fn process(
mut edges: Vec<T>,
first: Option<usize>,
last: Option<usize>,
) -> Result<(bool, bool, Vec<T>), InvalidPagination> {
) -> Result<Self, InvalidPagination> {
let limit = match (first, last) {
(Some(count), _) | (_, Some(count)) => count,
_ => return Err(InvalidPagination),
};
let is_full = page.len() == (limit + 1);
let is_full = edges.len() == (limit + 1);
if is_full {
page.pop();
edges.pop();
}
let (has_previous_page, has_next_page) = if first.is_some() {
(false, is_full)
} else if last.is_some() {
// 6. If the last argument is provided, I reverse the order of the results
page.reverse();
edges.reverse();
(is_full, false)
} else {
unreachable!()
};
Ok((has_previous_page, has_next_page, page))
Ok(Page {
has_next_page,
has_previous_page,
edges,
})
}
pub struct Page<T> {
pub has_next_page: bool,
pub has_previous_page: bool,
pub edges: Vec<T>,
pub fn map<F, T2>(self, f: F) -> Page<T2>
where
F: FnMut(T) -> T2,
{
let edges = self.edges.into_iter().map(f).collect();
Page {
has_next_page: self.has_next_page,
has_previous_page: self.has_previous_page,
edges,
}
}
pub fn try_map<F, E, T2>(self, f: F) -> Result<Page<T2>, E>
where
F: FnMut(T) -> Result<T2, E>,
{
let edges: Result<Vec<T2>, E> = self.edges.into_iter().map(f).collect();
Ok(Page {
has_next_page: self.has_next_page,
has_previous_page: self.has_previous_page,
edges: edges?,
})
}
}
impl<T> Page<T> {}

View File

@ -21,7 +21,7 @@ use ulid::Ulid;
use uuid::Uuid;
use crate::{
pagination::{process_page, Page, QueryBuilderExt},
pagination::{Page, QueryBuilderExt},
tracing::ExecuteExt,
Clock, DatabaseError, LookupResultExt,
};
@ -297,19 +297,13 @@ impl<'c> UpstreamOAuthLinkRepository for PgUpstreamOAuthLinkRepository<'c> {
.push_bind(Uuid::from(user.id))
.generate_pagination("upstream_oauth_link_id", before, after, first, last)?;
let page: Vec<LinkLookup> = query
let edges: Vec<LinkLookup> = query
.build_query_as()
.traced()
.fetch_all(&mut *self.conn)
.await?;
let (has_previous_page, has_next_page, edges) = process_page(page, first, last)?;
let edges: Vec<_> = edges.into_iter().map(Into::into).collect();
Ok(Page {
has_next_page,
has_previous_page,
edges,
})
let page = Page::process(edges, first, last)?.map(UpstreamOAuthLink::from);
Ok(page)
}
}

View File

@ -23,7 +23,7 @@ use ulid::Ulid;
use uuid::Uuid;
use crate::{
pagination::{process_page, Page, QueryBuilderExt},
pagination::{Page, QueryBuilderExt},
tracing::ExecuteExt,
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
};
@ -266,20 +266,14 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'
query.generate_pagination("upstream_oauth_provider_id", before, after, first, last)?;
let page: Vec<ProviderLookup> = query
let edges: Vec<ProviderLookup> = query
.build_query_as()
.traced()
.fetch_all(&mut *self.conn)
.await?;
let (has_previous_page, has_next_page, edges) = process_page(page, first, last)?;
let edges: Result<Vec<_>, _> = edges.into_iter().map(TryInto::try_into).collect();
Ok(Page {
has_next_page,
has_previous_page,
edges: edges?,
})
let page = Page::process(edges, first, last)?.try_map(TryInto::try_into)?;
Ok(page)
}
#[tracing::instrument(

View File

@ -21,7 +21,7 @@ use ulid::Ulid;
use uuid::Uuid;
use crate::{
pagination::{process_page, Page, QueryBuilderExt},
pagination::{Page, QueryBuilderExt},
tracing::ExecuteExt,
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
};
@ -315,15 +315,8 @@ impl<'c> UserEmailRepository for PgUserEmailRepository<'c> {
.fetch_all(&mut *self.conn)
.await?;
let (has_previous_page, has_next_page, edges) = process_page(edges, first, last)?;
let edges = edges.into_iter().map(Into::into).collect();
Ok(Page {
has_next_page,
has_previous_page,
edges,
})
let page = Page::process(edges, first, last)?.map(UserEmail::from);
Ok(page)
}
#[tracing::instrument(

View File

@ -21,7 +21,7 @@ use ulid::Ulid;
use uuid::Uuid;
use crate::{
pagination::{process_page, Page, QueryBuilderExt},
pagination::{Page, QueryBuilderExt},
tracing::ExecuteExt,
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
};
@ -91,19 +91,19 @@ struct SessionLookup {
last_authd_at: Option<DateTime<Utc>>,
}
impl TryInto<BrowserSession> for SessionLookup {
impl TryFrom<SessionLookup> for BrowserSession {
type Error = DatabaseInconsistencyError;
fn try_into(self) -> Result<BrowserSession, Self::Error> {
let id = Ulid::from(self.user_id);
fn try_from(value: SessionLookup) -> Result<Self, Self::Error> {
let id = Ulid::from(value.user_id);
let user = User {
id,
username: self.user_username,
username: value.user_username,
sub: id.to_string(),
primary_user_email_id: self.user_primary_user_email_id.map(Into::into),
primary_user_email_id: value.user_primary_user_email_id.map(Into::into),
};
let last_authentication = match (self.last_authentication_id, self.last_authd_at) {
let last_authentication = match (value.last_authentication_id, value.last_authd_at) {
(Some(id), Some(created_at)) => Some(Authentication {
id: id.into(),
created_at,
@ -117,10 +117,10 @@ impl TryInto<BrowserSession> for SessionLookup {
};
Ok(BrowserSession {
id: self.user_session_id.into(),
id: value.user_session_id.into(),
user,
created_at: self.user_session_created_at,
finished_at: self.user_session_finished_at,
created_at: value.user_session_created_at,
finished_at: value.user_session_finished_at,
last_authentication,
})
}
@ -292,20 +292,14 @@ impl<'c> BrowserSessionRepository for PgBrowserSessionRepository<'c> {
.push_bind(Uuid::from(user.id))
.generate_pagination("s.user_session_id", before, after, first, last)?;
let page: Vec<SessionLookup> = query
let edges: Vec<SessionLookup> = query
.build_query_as()
.traced()
.fetch_all(&mut *self.conn)
.await?;
let (has_previous_page, has_next_page, edges) = process_page(page, first, last)?;
let edges: Result<Vec<_>, _> = edges.into_iter().map(TryInto::try_into).collect();
Ok(Page {
has_previous_page,
has_next_page,
edges: edges?,
})
let page = Page::process(edges, first, last)?.try_map(BrowserSession::try_from)?;
Ok(page)
}
#[tracing::instrument(