You've already forked authentication-service
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:
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -82,41 +82,71 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Process a page returned by a paginated query
|
||||
pub fn process_page<T>(
|
||||
mut page: Vec<T>,
|
||||
first: Option<usize>,
|
||||
last: Option<usize>,
|
||||
) -> Result<(bool, bool, Vec<T>), InvalidPagination> {
|
||||
let limit = match (first, last) {
|
||||
(Some(count), _) | (_, Some(count)) => count,
|
||||
_ => return Err(InvalidPagination),
|
||||
};
|
||||
|
||||
let is_full = page.len() == (limit + 1);
|
||||
if is_full {
|
||||
page.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();
|
||||
(is_full, false)
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
Ok((has_previous_page, has_next_page, page))
|
||||
}
|
||||
|
||||
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(
|
||||
mut edges: Vec<T>,
|
||||
first: Option<usize>,
|
||||
last: Option<usize>,
|
||||
) -> Result<Self, InvalidPagination> {
|
||||
let limit = match (first, last) {
|
||||
(Some(count), _) | (_, Some(count)) => count,
|
||||
_ => return Err(InvalidPagination),
|
||||
};
|
||||
|
||||
let is_full = edges.len() == (limit + 1);
|
||||
if is_full {
|
||||
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
|
||||
edges.reverse();
|
||||
(is_full, false)
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
Ok(Page {
|
||||
has_next_page,
|
||||
has_previous_page,
|
||||
edges,
|
||||
})
|
||||
}
|
||||
|
||||
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> {}
|
||||
|
||||
pub trait QueryBuilderExt {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
Reference in New Issue
Block a user