You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
storage: simplify pagination
This commit is contained in:
@ -22,7 +22,7 @@ use url::Url;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pagination::{process_page, Page, QueryBuilderExt},
|
pagination::{Page, QueryBuilderExt},
|
||||||
tracing::ExecuteExt,
|
tracing::ExecuteExt,
|
||||||
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
|
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
|
||||||
};
|
};
|
||||||
@ -379,19 +379,13 @@ impl<'c> CompatSsoLoginRepository for PgCompatSsoLoginRepository<'c> {
|
|||||||
.push_bind(Uuid::from(user.id))
|
.push_bind(Uuid::from(user.id))
|
||||||
.generate_pagination("cl.compat_sso_login_id", before, after, first, last)?;
|
.generate_pagination("cl.compat_sso_login_id", before, after, first, last)?;
|
||||||
|
|
||||||
let page: Vec<CompatSsoLoginLookup> = query
|
let edges: Vec<CompatSsoLoginLookup> = query
|
||||||
.build_query_as()
|
.build_query_as()
|
||||||
.traced()
|
.traced()
|
||||||
.fetch_all(&mut *self.conn)
|
.fetch_all(&mut *self.conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (has_previous_page, has_next_page, edges) = process_page(page, first, last)?;
|
let page = Page::process(edges, first, last)?.try_map(CompatSsoLogin::try_from)?;
|
||||||
|
Ok(page)
|
||||||
let edges: Result<Vec<_>, _> = edges.into_iter().map(TryInto::try_into).collect();
|
|
||||||
Ok(Page {
|
|
||||||
has_next_page,
|
|
||||||
has_previous_page,
|
|
||||||
edges: edges?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ use ulid::Ulid;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pagination::{process_page, Page, QueryBuilderExt},
|
pagination::{Page, QueryBuilderExt},
|
||||||
tracing::ExecuteExt,
|
tracing::ExecuteExt,
|
||||||
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
|
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
|
||||||
};
|
};
|
||||||
@ -271,15 +271,7 @@ impl<'c> OAuth2SessionRepository for PgOAuth2SessionRepository<'c> {
|
|||||||
.fetch_all(&mut *self.conn)
|
.fetch_all(&mut *self.conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (has_previous_page, has_next_page, edges) = process_page(edges, first, last)?;
|
let page = Page::process(edges, first, last)?.try_map(Session::try_from)?;
|
||||||
|
Ok(page)
|
||||||
let edges: Result<Vec<_>, DatabaseInconsistencyError> =
|
|
||||||
edges.into_iter().map(Session::try_from).collect();
|
|
||||||
|
|
||||||
Ok(Page {
|
|
||||||
has_next_page,
|
|
||||||
has_previous_page,
|
|
||||||
edges: edges?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,41 +82,71 @@ where
|
|||||||
Ok(())
|
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 struct Page<T> {
|
||||||
pub has_next_page: bool,
|
pub has_next_page: bool,
|
||||||
pub has_previous_page: bool,
|
pub has_previous_page: bool,
|
||||||
pub edges: Vec<T>,
|
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> {}
|
impl<T> Page<T> {}
|
||||||
|
|
||||||
pub trait QueryBuilderExt {
|
pub trait QueryBuilderExt {
|
||||||
|
@ -21,7 +21,7 @@ use ulid::Ulid;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pagination::{process_page, Page, QueryBuilderExt},
|
pagination::{Page, QueryBuilderExt},
|
||||||
tracing::ExecuteExt,
|
tracing::ExecuteExt,
|
||||||
Clock, DatabaseError, LookupResultExt,
|
Clock, DatabaseError, LookupResultExt,
|
||||||
};
|
};
|
||||||
@ -297,19 +297,13 @@ impl<'c> UpstreamOAuthLinkRepository for PgUpstreamOAuthLinkRepository<'c> {
|
|||||||
.push_bind(Uuid::from(user.id))
|
.push_bind(Uuid::from(user.id))
|
||||||
.generate_pagination("upstream_oauth_link_id", before, after, first, last)?;
|
.generate_pagination("upstream_oauth_link_id", before, after, first, last)?;
|
||||||
|
|
||||||
let page: Vec<LinkLookup> = query
|
let edges: Vec<LinkLookup> = query
|
||||||
.build_query_as()
|
.build_query_as()
|
||||||
.traced()
|
.traced()
|
||||||
.fetch_all(&mut *self.conn)
|
.fetch_all(&mut *self.conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (has_previous_page, has_next_page, edges) = process_page(page, first, last)?;
|
let page = Page::process(edges, first, last)?.map(UpstreamOAuthLink::from);
|
||||||
|
Ok(page)
|
||||||
let edges: Vec<_> = edges.into_iter().map(Into::into).collect();
|
|
||||||
Ok(Page {
|
|
||||||
has_next_page,
|
|
||||||
has_previous_page,
|
|
||||||
edges,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ use ulid::Ulid;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pagination::{process_page, Page, QueryBuilderExt},
|
pagination::{Page, QueryBuilderExt},
|
||||||
tracing::ExecuteExt,
|
tracing::ExecuteExt,
|
||||||
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
|
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
|
||||||
};
|
};
|
||||||
@ -266,20 +266,14 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'
|
|||||||
|
|
||||||
query.generate_pagination("upstream_oauth_provider_id", before, after, first, last)?;
|
query.generate_pagination("upstream_oauth_provider_id", before, after, first, last)?;
|
||||||
|
|
||||||
let page: Vec<ProviderLookup> = query
|
let edges: Vec<ProviderLookup> = query
|
||||||
.build_query_as()
|
.build_query_as()
|
||||||
.traced()
|
.traced()
|
||||||
.fetch_all(&mut *self.conn)
|
.fetch_all(&mut *self.conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (has_previous_page, has_next_page, edges) = process_page(page, first, last)?;
|
let page = Page::process(edges, first, last)?.try_map(TryInto::try_into)?;
|
||||||
|
Ok(page)
|
||||||
let edges: Result<Vec<_>, _> = edges.into_iter().map(TryInto::try_into).collect();
|
|
||||||
Ok(Page {
|
|
||||||
has_next_page,
|
|
||||||
has_previous_page,
|
|
||||||
edges: edges?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(
|
#[tracing::instrument(
|
||||||
|
@ -21,7 +21,7 @@ use ulid::Ulid;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pagination::{process_page, Page, QueryBuilderExt},
|
pagination::{Page, QueryBuilderExt},
|
||||||
tracing::ExecuteExt,
|
tracing::ExecuteExt,
|
||||||
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
|
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
|
||||||
};
|
};
|
||||||
@ -315,15 +315,8 @@ impl<'c> UserEmailRepository for PgUserEmailRepository<'c> {
|
|||||||
.fetch_all(&mut *self.conn)
|
.fetch_all(&mut *self.conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (has_previous_page, has_next_page, edges) = process_page(edges, first, last)?;
|
let page = Page::process(edges, first, last)?.map(UserEmail::from);
|
||||||
|
Ok(page)
|
||||||
let edges = edges.into_iter().map(Into::into).collect();
|
|
||||||
|
|
||||||
Ok(Page {
|
|
||||||
has_next_page,
|
|
||||||
has_previous_page,
|
|
||||||
edges,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(
|
#[tracing::instrument(
|
||||||
|
@ -21,7 +21,7 @@ use ulid::Ulid;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pagination::{process_page, Page, QueryBuilderExt},
|
pagination::{Page, QueryBuilderExt},
|
||||||
tracing::ExecuteExt,
|
tracing::ExecuteExt,
|
||||||
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
|
Clock, DatabaseError, DatabaseInconsistencyError, LookupResultExt,
|
||||||
};
|
};
|
||||||
@ -91,19 +91,19 @@ struct SessionLookup {
|
|||||||
last_authd_at: Option<DateTime<Utc>>,
|
last_authd_at: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryInto<BrowserSession> for SessionLookup {
|
impl TryFrom<SessionLookup> for BrowserSession {
|
||||||
type Error = DatabaseInconsistencyError;
|
type Error = DatabaseInconsistencyError;
|
||||||
|
|
||||||
fn try_into(self) -> Result<BrowserSession, Self::Error> {
|
fn try_from(value: SessionLookup) -> Result<Self, Self::Error> {
|
||||||
let id = Ulid::from(self.user_id);
|
let id = Ulid::from(value.user_id);
|
||||||
let user = User {
|
let user = User {
|
||||||
id,
|
id,
|
||||||
username: self.user_username,
|
username: value.user_username,
|
||||||
sub: id.to_string(),
|
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 {
|
(Some(id), Some(created_at)) => Some(Authentication {
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
created_at,
|
created_at,
|
||||||
@ -117,10 +117,10 @@ impl TryInto<BrowserSession> for SessionLookup {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(BrowserSession {
|
Ok(BrowserSession {
|
||||||
id: self.user_session_id.into(),
|
id: value.user_session_id.into(),
|
||||||
user,
|
user,
|
||||||
created_at: self.user_session_created_at,
|
created_at: value.user_session_created_at,
|
||||||
finished_at: self.user_session_finished_at,
|
finished_at: value.user_session_finished_at,
|
||||||
last_authentication,
|
last_authentication,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -292,20 +292,14 @@ impl<'c> BrowserSessionRepository for PgBrowserSessionRepository<'c> {
|
|||||||
.push_bind(Uuid::from(user.id))
|
.push_bind(Uuid::from(user.id))
|
||||||
.generate_pagination("s.user_session_id", before, after, first, last)?;
|
.generate_pagination("s.user_session_id", before, after, first, last)?;
|
||||||
|
|
||||||
let page: Vec<SessionLookup> = query
|
let edges: Vec<SessionLookup> = query
|
||||||
.build_query_as()
|
.build_query_as()
|
||||||
.traced()
|
.traced()
|
||||||
.fetch_all(&mut *self.conn)
|
.fetch_all(&mut *self.conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (has_previous_page, has_next_page, edges) = process_page(page, first, last)?;
|
let page = Page::process(edges, first, last)?.try_map(BrowserSession::try_from)?;
|
||||||
|
Ok(page)
|
||||||
let edges: Result<Vec<_>, _> = edges.into_iter().map(TryInto::try_into).collect();
|
|
||||||
Ok(Page {
|
|
||||||
has_previous_page,
|
|
||||||
has_next_page,
|
|
||||||
edges: edges?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(
|
#[tracing::instrument(
|
||||||
|
Reference in New Issue
Block a user