diff --git a/crates/lib/mysql_queries/src/queries/beta_keys/list_beta_keys.rs b/crates/lib/mysql_queries/src/queries/beta_keys/list_beta_keys.rs index 81a7634398..7d1a8248d5 100644 --- a/crates/lib/mysql_queries/src/queries/beta_keys/list_beta_keys.rs +++ b/crates/lib/mysql_queries/src/queries/beta_keys/list_beta_keys.rs @@ -15,11 +15,8 @@ pub struct BetaKeyListPage { pub sort_ascending: bool, - /// ID of the first record in the result set. - pub first_id: Option, - - /// ID of the last record in the result set. - pub last_id: Option, + pub current_page: usize, + pub total_page_count: usize, } pub struct BetaKeyListItem { @@ -53,38 +50,53 @@ pub struct BetaKeyListItem { pub struct ListBetaKeysArgs<'a> { pub filter_to_referrer_user_token: Option<&'a UserToken>, pub filter_to_remaining_keys: bool, - pub limit: usize, - pub maybe_offset: Option, - pub cursor_is_reversed: bool, + + pub page_size: usize, + pub page_index: usize, pub sort_ascending: bool, + pub mysql_pool: &'a MySqlPool, } pub async fn list_beta_keys(args: ListBetaKeysArgs<'_>) -> AnyhowResult { + /// Let's figure out how many results we could have returned total + let count_fields = select_total_count_field(); - let mut query = query_builder( + let mut count_query_builder = query_builder( + count_fields, args.filter_to_referrer_user_token, args.filter_to_remaining_keys, - args.limit, - args.maybe_offset, - args.cursor_is_reversed, + false, + args.page_index, + args.page_size, args.sort_ascending, ); - info!("query_builder query (wip): {:?}", query.sql()); + info!("count query: {:?}", count_query_builder.sql()); - let query = query.build_query_as::(); + let row_count_query = count_query_builder.build_query_scalar::(); + let row_count_result = row_count_query.fetch_one(args.mysql_pool).await?; - info!("query dsl query (wip): {:?}", query.sql()); + let number_of_pages = (row_count_result / args.page_size as i64) as usize; + /// Now fetch the actual results with all the fields + let result_fields = select_result_fields(); + + let mut query = query_builder( + result_fields, + args.filter_to_referrer_user_token, + args.filter_to_remaining_keys, + true, + args.page_index, + args.page_size, + args.sort_ascending, + ); + + info!("actual query: {:?}", query.sql()); + + let query = query.build_query_as::(); let results = query.fetch_all(args.mysql_pool).await?; - let first_id = results.first() - .map(|raw_result| raw_result.id); - - let last_id = results.last() - .map(|raw_result| raw_result.id); - let results = results.into_iter() .map(|record| { BetaKeyListItem { @@ -113,26 +125,14 @@ pub async fn list_beta_keys(args: ListBetaKeysArgs<'_>) -> AnyhowResult( - filter_to_referrer_user_token: Option<&'a UserToken>, - filter_to_remaining_keys: bool, - limit: usize, - maybe_offset: Option, - cursor_is_reversed: bool, - sort_ascending: bool, -) -> QueryBuilder<'a, MySql> { - - let mut sort_ascending = sort_ascending; - // NB: Query cannot be statically checked by sqlx - let mut query_builder: QueryBuilder = QueryBuilder::new( - r#" -SELECT +fn select_result_fields() -> &'static str { + r#" b.id, b.token, @@ -159,6 +159,34 @@ SELECT b.created_at, b.maybe_redeemed_at + "# +} + +fn select_total_count_field() -> &'static str { + r#" + COUNT(b.id) AS total_count + "# +} + +fn query_builder<'a>( + select_fields: &'a str, + + filter_to_referrer_user_token: Option<&'a UserToken>, + filter_to_remaining_keys: bool, + + enforce_limits: bool, + page_index: usize, + page_size: usize, + sort_ascending: bool, +) -> QueryBuilder<'a, MySql> { + + let mut sort_ascending = sort_ascending; + + // NB: Query cannot be statically checked by sqlx + let mut query_builder: QueryBuilder = QueryBuilder::new( + format!(r#" +SELECT + {select_fields} FROM beta_keys AS b @@ -171,7 +199,7 @@ LEFT OUTER JOIN users AS referrer LEFT OUTER JOIN users AS redeemer ON b.maybe_redeemer_user_token = redeemer.token "# - ); + )); let mut first_predicate_added = false; @@ -198,33 +226,33 @@ LEFT OUTER JOIN users AS redeemer query_builder.push(" b.maybe_redeemed_at IS NULL "); } - if let Some(offset) = maybe_offset { - if !first_predicate_added { - query_builder.push(" WHERE "); - first_predicate_added = true; - } else { - query_builder.push(" AND "); - } - - if sort_ascending { - if cursor_is_reversed { - // NB: We're searching backwards. - query_builder.push(" b.id < "); - sort_ascending = !sort_ascending; - } else { - query_builder.push(" b.id > "); - } - } else { - if cursor_is_reversed { - // NB: We're searching backwards. - query_builder.push(" b.id > "); - sort_ascending = !sort_ascending; - } else { - query_builder.push(" b.id < "); - } - } - query_builder.push_bind(offset as i64); - } +// if let Some(offset) = maybe_offset { +// if !first_predicate_added { +// query_builder.push(" WHERE "); +// first_predicate_added = true; +// } else { +// query_builder.push(" AND "); +// } +// +// if sort_ascending { +// if cursor_is_reversed { +// // NB: We're searching backwards. +// query_builder.push(" b.id < "); +// sort_ascending = !sort_ascending; +// } else { +// query_builder.push(" b.id > "); +// } +// } else { +// if cursor_is_reversed { +// // NB: We're searching backwards. +// query_builder.push(" b.id > "); +// sort_ascending = !sort_ascending; +// } else { +// query_builder.push(" b.id < "); +// } +// } +// query_builder.push_bind(offset as i64); +// } if sort_ascending { query_builder.push(" ORDER BY b.id ASC "); @@ -232,12 +260,17 @@ LEFT OUTER JOIN users AS redeemer query_builder.push(" ORDER BY b.id DESC "); } - query_builder.push(format!(" LIMIT {limit} ")); +// query_builder.push(format!(" LIMIT {limit} ")); + + if enforce_limits { + let offset = page_index * page_size; + query_builder.push(format!(" LIMIT {page_size} OFFSET {offset} ")); + } query_builder } -struct MediaFileListItemInternal { +struct BetaKeyListItemInternal { id: i64, token: BetaKeyToken, @@ -266,7 +299,7 @@ struct MediaFileListItemInternal { maybe_redeemed_at: Option>, } -impl FromRow<'_, MySqlRow> for MediaFileListItemInternal { +impl FromRow<'_, MySqlRow> for BetaKeyListItemInternal { fn from_row(row: &MySqlRow) -> Result { let creator_user_token : String = row.try_get("creator_user_token")?; let creator_user_token = UserToken::new(creator_user_token); diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/beta_keys/list_beta_keys_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/beta_keys/list_beta_keys_handler.rs index 7f28f7b8c0..3d49538dd3 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/beta_keys/list_beta_keys_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/beta_keys/list_beta_keys_handler.rs @@ -29,6 +29,7 @@ use users_component::common_responses::user_details_lite::UserDetailsLight; use crate::http_server::common_responses::media_file_cover_image_details::{MediaFileCoverImageDetails, MediaFileDefaultCover}; use crate::http_server::common_responses::media_file_origin_details::MediaFileOriginDetails; use crate::http_server::common_responses::pagination_cursors::PaginationCursors; +use crate::http_server::common_responses::pagination_page::PaginationPage; use crate::http_server::common_responses::simple_entity_stats::SimpleEntityStats; use crate::http_server::endpoints::beta_keys::create_beta_keys_handler::CreateBetaKeysError; use crate::http_server::endpoints::events::list_events::ListEventsError; @@ -44,8 +45,7 @@ use crate::util::allowed_explore_media_access::allowed_explore_media_access; pub struct ListBetaKeysQueryParams { pub sort_ascending: Option, pub page_size: Option, - pub cursor: Option, - pub cursor_is_reversed: Option, + pub page_index: Option, /// Scope the beta keys to a referrer user. pub maybe_referrer_username: Option, @@ -58,7 +58,7 @@ pub struct ListBetaKeysQueryParams { pub struct ListBetaKeysSuccessResponse { pub success: bool, pub beta_keys: Vec, - pub pagination: PaginationCursors, + pub pagination: PaginationPage, } #[derive(Serialize, ToSchema)] @@ -150,28 +150,15 @@ pub async fn list_beta_keys_handler( } // TODO(bt,2023-12-04): Enforce real maximums and defaults - let limit = query.page_size.unwrap_or(25); - let sort_ascending = query.sort_ascending.unwrap_or(false); - let cursor_is_reversed = query.cursor_is_reversed.unwrap_or(false); - - let cursor = if let Some(cursor) = query.cursor.as_deref() { - let cursor = server_state.sort_key_crypto.decrypt_id(cursor) - .map_err(|e| { - warn!("crypto error: {:?}", e); - ListBetaKeysError::ServerError - })?; - Some(cursor as usize) - } else { - None - }; + let page_size = query.page_size.unwrap_or_else(|| 500); + let page_index = query.page_index.unwrap_or_else(|| 0); let query_results = list_beta_keys(ListBetaKeysArgs { filter_to_referrer_user_token: maybe_scope_user_token.as_ref(), filter_to_remaining_keys: query.only_list_remaining.unwrap_or(false), - limit, - maybe_offset: cursor, - cursor_is_reversed, + page_size, + page_index, sort_ascending, mysql_pool: &server_state.mysql_pool, }).await; @@ -184,28 +171,6 @@ pub async fn list_beta_keys_handler( } }; - let cursor_next = if let Some(id) = results_page.last_id { - let cursor = server_state.sort_key_crypto.encrypt_id(id as u64) - .map_err(|e| { - warn!("crypto error: {:?}", e); - ListBetaKeysError::ServerError - })?; - Some(cursor) - } else { - None - }; - - let cursor_previous = if let Some(id) = results_page.first_id { - let cursor = server_state.sort_key_crypto.encrypt_id(id as u64) - .map_err(|e| { - warn!("crypto error: {:?}", e); - ListBetaKeysError::ServerError - })?; - Some(cursor) - } else { - None - }; - let results = results_page.records.into_iter() .map(|beta_key| { BetaKeyItem { @@ -239,10 +204,9 @@ pub async fn list_beta_keys_handler( let response = ListBetaKeysSuccessResponse { success: true, beta_keys: results, - pagination: PaginationCursors { - maybe_next: cursor_next, - maybe_previous: cursor_previous, - cursor_is_reversed, + pagination: PaginationPage{ + current: results_page.current_page, + total_page_count: results_page.total_page_count, } };