diff --git a/crates/storage/src/compat/sso_login.rs b/crates/storage/src/compat/sso_login.rs index cba777d3..8cb84dc1 100644 --- a/crates/storage/src/compat/sso_login.rs +++ b/crates/storage/src/compat/sso_login.rs @@ -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 = query + let edges: Vec = 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, _> = 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) } } diff --git a/crates/storage/src/oauth2/session.rs b/crates/storage/src/oauth2/session.rs index c28bc4ef..0a6b5c99 100644 --- a/crates/storage/src/oauth2/session.rs +++ b/crates/storage/src/oauth2/session.rs @@ -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, 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) } } diff --git a/crates/storage/src/pagination.rs b/crates/storage/src/pagination.rs index a240c554..5887117c 100644 --- a/crates/storage/src/pagination.rs +++ b/crates/storage/src/pagination.rs @@ -82,41 +82,71 @@ where Ok(()) } -/// Process a page returned by a paginated query -pub fn process_page( - mut page: Vec, - first: Option, - last: Option, -) -> Result<(bool, bool, Vec), 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 { pub has_next_page: bool, pub has_previous_page: bool, pub edges: Vec, } +impl Page { + /// Process a page returned by a paginated query + pub fn process( + mut edges: Vec, + first: Option, + last: Option, + ) -> Result { + 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(self, f: F) -> Page + 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(self, f: F) -> Result, E> + where + F: FnMut(T) -> Result, + { + let edges: Result, 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 Page {} pub trait QueryBuilderExt { diff --git a/crates/storage/src/upstream_oauth2/link.rs b/crates/storage/src/upstream_oauth2/link.rs index 0d443671..f72a8504 100644 --- a/crates/storage/src/upstream_oauth2/link.rs +++ b/crates/storage/src/upstream_oauth2/link.rs @@ -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 = query + let edges: Vec = 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) } } diff --git a/crates/storage/src/upstream_oauth2/provider.rs b/crates/storage/src/upstream_oauth2/provider.rs index a7efb6c8..088e9e93 100644 --- a/crates/storage/src/upstream_oauth2/provider.rs +++ b/crates/storage/src/upstream_oauth2/provider.rs @@ -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 = query + let edges: Vec = 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, _> = 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( diff --git a/crates/storage/src/user/email.rs b/crates/storage/src/user/email.rs index 2f748611..d725dea5 100644 --- a/crates/storage/src/user/email.rs +++ b/crates/storage/src/user/email.rs @@ -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( diff --git a/crates/storage/src/user/session.rs b/crates/storage/src/user/session.rs index 01102ca9..f2ceed2a 100644 --- a/crates/storage/src/user/session.rs +++ b/crates/storage/src/user/session.rs @@ -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>, } -impl TryInto for SessionLookup { +impl TryFrom for BrowserSession { type Error = DatabaseInconsistencyError; - fn try_into(self) -> Result { - let id = Ulid::from(self.user_id); + fn try_from(value: SessionLookup) -> Result { + 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 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 = query + let edges: Vec = 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, _> = 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(