diff --git a/Cargo.lock b/Cargo.lock index 3413b2a..b0182a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -669,9 +669,9 @@ dependencies = [ [[package]] name = "momento-protos" -version = "0.120.0" +version = "0.122.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4213df712c988787f748b35e40f40a7e3cba7a7b327c48f7d8da42354aebb9fa" +checksum = "f4fcecfc2e01f759d1ac0d2f80969f8b6ccdfafe2bc874a968fb587d70627b4f" dependencies = [ "prost", "tonic", diff --git a/Cargo.toml b/Cargo.toml index f22b974..684c51d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ doc = false [dependencies] -momento-protos = { version = "0.120.0" } +momento-protos = { version = "0.122.1" } log = "0.4" hyper = { version = "0.14" } h2 = { version = "0.3" } diff --git a/src/leaderboard/leaderboard_resource.rs b/src/leaderboard/leaderboard_resource.rs index 5dcbc5c..a7cb0be 100644 --- a/src/leaderboard/leaderboard_resource.rs +++ b/src/leaderboard/leaderboard_resource.rs @@ -4,7 +4,8 @@ use crate::leaderboard::messages::data::delete::{DeleteRequest, DeleteResponse}; use crate::leaderboard::messages::data::fetch::FetchResponse; use crate::leaderboard::messages::data::fetch_by_rank::{FetchByRankRequest, RankRange}; use crate::leaderboard::messages::data::fetch_by_score::{FetchByScoreRequest, ScoreRange}; -use crate::leaderboard::messages::data::get_rank::{GetRankRequest, GetRankResponse}; +use crate::leaderboard::messages::data::get_competition_rank::GetCompetitionRankRequest; +use crate::leaderboard::messages::data::get_rank::GetRankRequest; use crate::leaderboard::messages::data::length::{LengthRequest, LengthResponse}; use crate::leaderboard::messages::data::remove_elements::{ RemoveElementsRequest, RemoveElementsResponse, @@ -80,7 +81,7 @@ impl Leaderboard { pub async fn get_rank( &self, ids: impl IntoIterator, - ) -> MomentoResult { + ) -> MomentoResult { let request = GetRankRequest::new(ids); request.send(self).await } @@ -103,6 +104,18 @@ impl Leaderboard { request.send(self).await } + /// Get rank of elements, using competition ranking, from a leaderboard using their element ids. + /// + /// Defaults to DESCENDING order rank, meaning rank 0 is the element with + /// the highest score. + pub async fn get_competition_rank( + &self, + ids: impl IntoIterator, + ) -> MomentoResult { + let request = GetCompetitionRankRequest::new(ids); + request.send(self).await + } + /* helper fns */ pub(crate) fn new( data_clients: Vec< diff --git a/src/leaderboard/messages/data/delete.rs b/src/leaderboard/messages/data/delete.rs index 87698d1..366685c 100644 --- a/src/leaderboard/messages/data/delete.rs +++ b/src/leaderboard/messages/data/delete.rs @@ -27,7 +27,6 @@ impl LeaderboardRequest for DeleteRequest { cache_name, leaderboard.client_timeout(), momento_protos::leaderboard::DeleteLeaderboardRequest { - cache_name: cache_name.to_string(), leaderboard: leaderboard.leaderboard_name().to_string(), }, )?; diff --git a/src/leaderboard/messages/data/fetch_by_rank.rs b/src/leaderboard/messages/data/fetch_by_rank.rs index 6a3b4a8..5e1d882 100644 --- a/src/leaderboard/messages/data/fetch_by_rank.rs +++ b/src/leaderboard/messages/data/fetch_by_rank.rs @@ -101,7 +101,6 @@ impl LeaderboardRequest for FetchByRankRequest { cache_name, leaderboard.client_timeout(), momento_protos::leaderboard::GetByRankRequest { - cache_name: cache_name.to_string(), leaderboard: leaderboard.leaderboard_name().to_string(), rank_range: Some(self.rank_range.into()), order: self.order.into_proto() as i32, diff --git a/src/leaderboard/messages/data/fetch_by_score.rs b/src/leaderboard/messages/data/fetch_by_score.rs index 4064249..6cb846e 100644 --- a/src/leaderboard/messages/data/fetch_by_score.rs +++ b/src/leaderboard/messages/data/fetch_by_score.rs @@ -145,7 +145,6 @@ impl LeaderboardRequest for FetchByScoreRequest { cache_name, leaderboard.client_timeout(), momento_protos::leaderboard::GetByScoreRequest { - cache_name: cache_name.to_string(), leaderboard: leaderboard.leaderboard_name().to_string(), score_range: Some(self.score_range.into()), offset: self.offset.unwrap_or(0), diff --git a/src/leaderboard/messages/data/get_competition_rank.rs b/src/leaderboard/messages/data/get_competition_rank.rs new file mode 100644 index 0000000..d833077 --- /dev/null +++ b/src/leaderboard/messages/data/get_competition_rank.rs @@ -0,0 +1,59 @@ +use super::fetch::FetchResponse; +use super::Order; +use crate::leaderboard::LeaderboardRequest; +use crate::utils::prep_leaderboard_request_with_timeout; +use crate::{Leaderboard, MomentoResult}; + +/// A request to get ranked elements, sorted by competition ranking, by providing a list of element IDs. +pub struct GetCompetitionRankRequest { + ids: Vec, + order: Order, +} + +impl GetCompetitionRankRequest { + /// Constructs a new `GetCompetitionRankRequest`. + /// + /// Defaults to DESCENDING order, meaning that rank 0 + /// is the element with the highest score. + pub fn new(ids: impl IntoIterator) -> Self { + Self { + ids: ids.into_iter().collect(), + order: Order::Descending, + } + } + + /// Sets the order ranking. + /// + /// Defaults to DESCENDING order. + pub fn order(mut self, order: Order) -> Self { + self.order = order; + self + } +} + +impl LeaderboardRequest for GetCompetitionRankRequest { + type Response = FetchResponse; + + async fn send(self, leaderboard: &Leaderboard) -> MomentoResult { + let cache_name = leaderboard.cache_name(); + let request = prep_leaderboard_request_with_timeout( + cache_name, + leaderboard.client_timeout(), + momento_protos::leaderboard::GetCompetitionRankRequest { + leaderboard: leaderboard.leaderboard_name().to_string(), + ids: self.ids, + order: Some(self.order.into_proto() as i32), + }, + )?; + + let response = leaderboard + .next_data_client() + .get_competition_rank(request) + .await? + .into_inner(); + + Ok(FetchResponse::new( + response.elements.iter().map(|v| v.into()).collect(), + )) + } +} diff --git a/src/leaderboard/messages/data/get_rank.rs b/src/leaderboard/messages/data/get_rank.rs index c93a8bb..c3bbd3d 100644 --- a/src/leaderboard/messages/data/get_rank.rs +++ b/src/leaderboard/messages/data/get_rank.rs @@ -1,4 +1,5 @@ -use super::{Order, RankedElement}; +use super::fetch::FetchResponse; +use super::Order; use crate::leaderboard::LeaderboardRequest; use crate::utils::prep_leaderboard_request_with_timeout; use crate::{Leaderboard, MomentoResult}; @@ -31,7 +32,7 @@ impl GetRankRequest { } impl LeaderboardRequest for GetRankRequest { - type Response = GetRankResponse; + type Response = FetchResponse; async fn send(self, leaderboard: &Leaderboard) -> MomentoResult { let cache_name = leaderboard.cache_name(); @@ -39,7 +40,6 @@ impl LeaderboardRequest for GetRankRequest { cache_name, leaderboard.client_timeout(), momento_protos::leaderboard::GetRankRequest { - cache_name: cache_name.to_string(), leaderboard: leaderboard.leaderboard_name().to_string(), ids: self.ids, order: self.order.into_proto() as i32, @@ -52,25 +52,8 @@ impl LeaderboardRequest for GetRankRequest { .await? .into_inner(); - Ok(Self::Response { - elements: response.elements.iter().map(|v| v.into()).collect(), - }) - } -} - -/// The response type for a successful `GetRankRequest` -pub struct GetRankResponse { - elements: Vec, -} - -impl GetRankResponse { - /// Returns the ranked elements in the response. - pub fn elements(&self) -> &[RankedElement] { - &self.elements - } - - /// Consumes the response and returns the ranked elements. - pub fn into_elements(self) -> Vec { - self.elements + Ok(FetchResponse::new( + response.elements.iter().map(|v| v.into()).collect(), + )) } } diff --git a/src/leaderboard/messages/data/length.rs b/src/leaderboard/messages/data/length.rs index 805cd8c..153ca8b 100644 --- a/src/leaderboard/messages/data/length.rs +++ b/src/leaderboard/messages/data/length.rs @@ -27,7 +27,6 @@ impl LeaderboardRequest for LengthRequest { cache_name, leaderboard.client_timeout(), momento_protos::leaderboard::GetLeaderboardLengthRequest { - cache_name: cache_name.to_string(), leaderboard: leaderboard.leaderboard_name().to_string(), }, )?; diff --git a/src/leaderboard/messages/data/mod.rs b/src/leaderboard/messages/data/mod.rs index f9474cf..3ee40c3 100644 --- a/src/leaderboard/messages/data/mod.rs +++ b/src/leaderboard/messages/data/mod.rs @@ -9,6 +9,9 @@ pub mod fetch_by_rank; /// Contains the request and response types for requesting elements from a /// leaderboard by score. pub mod fetch_by_score; +/// Contains the request and response types for requesting elements, sorted +/// by competition ranking, from a leaderboard using their element ids. +pub mod get_competition_rank; /// Contains the request and response types for requesting elements from a /// leaderboard using their element ids. pub mod get_rank; diff --git a/src/leaderboard/messages/data/remove_elements.rs b/src/leaderboard/messages/data/remove_elements.rs index b25416f..4e35c40 100644 --- a/src/leaderboard/messages/data/remove_elements.rs +++ b/src/leaderboard/messages/data/remove_elements.rs @@ -26,7 +26,6 @@ impl LeaderboardRequest for RemoveElementsRequest { cache_name, leaderboard.client_timeout(), momento_protos::leaderboard::RemoveElementsRequest { - cache_name: cache_name.to_string(), leaderboard: leaderboard.leaderboard_name().to_string(), ids: self.ids, }, diff --git a/src/leaderboard/messages/data/upsert.rs b/src/leaderboard/messages/data/upsert.rs index 76152e6..93d186e 100644 --- a/src/leaderboard/messages/data/upsert.rs +++ b/src/leaderboard/messages/data/upsert.rs @@ -61,7 +61,6 @@ where cache_name, leaderboard.client_timeout(), momento_protos::leaderboard::UpsertElementsRequest { - cache_name: cache_name.to_string(), leaderboard: leaderboard.leaderboard_name().to_string(), elements: self .elements diff --git a/src/leaderboard/mod.rs b/src/leaderboard/mod.rs index ecdd56b..3fc4c94 100644 --- a/src/leaderboard/mod.rs +++ b/src/leaderboard/mod.rs @@ -6,7 +6,7 @@ pub use messages::LeaderboardRequest; pub use messages::data::delete::{DeleteRequest, DeleteResponse}; pub use messages::data::fetch_by_rank::{FetchByRankRequest, RankRange}; pub use messages::data::fetch_by_score::{FetchByScoreRequest, ScoreRange}; -pub use messages::data::get_rank::{GetRankRequest, GetRankResponse}; +pub use messages::data::get_rank::GetRankRequest; pub use messages::data::length::{LengthRequest, LengthResponse}; pub use messages::data::remove_elements::{RemoveElementsRequest, RemoveElementsResponse}; pub use messages::data::upsert::{Element, UpsertRequest, UpsertResponse}; diff --git a/tests/leaderboard/mod.rs b/tests/leaderboard/mod.rs index de99e05..21cc6b1 100644 --- a/tests/leaderboard/mod.rs +++ b/tests/leaderboard/mod.rs @@ -350,3 +350,94 @@ mod fetch_by_score { Ok(()) } } + +mod get_competition_rank { + use momento::leaderboard::{ + messages::data::get_competition_rank::GetCompetitionRankRequest, Element, RankedElement, + }; + + use super::*; + + fn test_competition_leaderboard() -> Vec { + vec![ + Element { id: 0, score: 20.0 }, + Element { id: 1, score: 10.0 }, + Element { id: 2, score: 10.0 }, + Element { id: 3, score: 5.0 }, + ] + } + + #[tokio::test] + async fn get_competition_rank_of_elements() -> MomentoResult<()> { + let leaderboard = unique_leaderboard(); + leaderboard.upsert(test_competition_leaderboard()).await?; + + let response = leaderboard.get_competition_rank([0, 1, 2, 3, 4]).await?; + + assert_eq!( + vec![ + RankedElement { + id: 0, + score: 20.0, + rank: 0 + }, + RankedElement { + id: 1, + score: 10.0, + rank: 1 + }, + RankedElement { + id: 2, + score: 10.0, + rank: 1 + }, + RankedElement { + id: 3, + score: 5.0, + rank: 3 + }, + ], + response.elements(), + "Expected the leaderboard to be sorted in 0113 order" + ); + Ok(()) + } + + #[tokio::test] + async fn get_rank_of_elements_asc() -> MomentoResult<()> { + let leaderboard = unique_leaderboard(); + leaderboard.upsert(test_competition_leaderboard()).await?; + + let response = leaderboard + .send_request(GetCompetitionRankRequest::new([0, 1, 2, 3, 4]).order(Order::Ascending)) + .await?; + + assert_eq!( + vec![ + RankedElement { + id: 0, + score: 20.0, + rank: 3 + }, + RankedElement { + id: 1, + score: 10.0, + rank: 1 + }, + RankedElement { + id: 2, + score: 10.0, + rank: 1 + }, + RankedElement { + id: 3, + score: 5.0, + rank: 0 + }, + ], + response.elements(), + "Expected the leaderboard to be sorted in 3110 order" + ); + Ok(()) + } +}